Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

AhmadLang / Object Oriented Programing using C++

.pdf
Скачиваний:
92
Добавлен:
31.05.2015
Размер:
799.32 Кб
Скачать

Object Oriented Programming using C++

#defines MAX_WIDTH 100 char str1[MAX_WIDTH]; char str2[MAX_WIDTH];

It defines two strings to store up to 100 characters.

#define can also be used to generate macro functions:

#define getmax(a,b) a>b?a:b int x=5, y;

y = getmax(x,2);

after the execution of this code y would contain 5.

#undef

#undef fulfills the inverse functionality than #define. What it does is to eliminate from the list of defined constants the one that has the name passed as parameter to #undef:

#define MAX_WIDTH 100 char str1[MAX_WIDTH]; #undef MAX_WIDTH #define MAX_WIDTH 200 char str2[MAX_WIDTH];

#ifdef, #ifndef, #if, #endif, #else and #elif

These directives allow to discard part of the code of a program if a certain condition is not fulfilled.

#ifdef allows that a section of a program is compiled only if the defined constant that is specified as parameter has been defined, independently of its value. Its operation is:

#ifdef name

// code here

#endif

For example:

#ifdef MAX_WIDTH char str[MAX_WIDTH]; #endif

In this case, the line char str[MAX_WIDTH]; is only considered by the compiler if the defined constant MAX_WIDTH has been previously defined, independently of its value. If it has not been defined, that line will not be included in the program.

#ifndef serves for the opposite: the code between the #ifndef directive and the #endif directive is only compiled if the constant name that is specified has not been defined previously. For example:

#ifndef MAX_WIDTH #define MAX_WIDTH 100 #endif

char str[MAX_WIDTH];

131

Object Oriented Programming using C++

In this case, if when arriving to this piece of code the defined constant MAX_WIDTH has not yet been defined it would be defined with a value of 100. If it already existed it would maintain the value that it had (because the #define statement won't be executed).

The #if, #else and #elif (elif = else if) directives serve for that the portion of code that follows is compiled only if the specified condition is met. The condition can only serve to evaluate constant expressions. For example:

#if MAX_WIDTH>200 #undef MAX_WIDTH #define MAX_WIDTH 200

#elsif MAX_WIDTH<50 #undef MAX_WIDTH #defines MAX_WIDTH 50

#else

#undef MAX_WIDTH #defines MAX_WIDTH 100 #endif

char str[MAX_WIDTH];

Notice how the structure of chained directives #if, #elsif and #else finishes with #endif.

#line

When we compile a program and there happens any errors during the compiling process, the compiler shows the error that have happened preceded by the name of the file and the line within the file where it has taken place.

The #line directive allows us to control both things, the line numbers within the code files as well as the file name that we want that it appears when an error takes place. Its form is the following one:

#line number "filename"

Where number is the new line number that will be assigned to the next code line. The line number of successive lines will be increased one by one from this.

filename is an optional parameter that serves to replace the file name that will be shown in case of error from this directive until other one changes it again or the end of the file is reached. For example:

#line 1 "assigning variable" int a?;

This code will generate an error that will be shown as error in file "assigning variable", line 1.

#error

This directive aborts the compilation process when it is found returning the error that is specified as parameter:

132

Object Oriented Programming using C++

#ifndef __cplusplus

#error A C++ compiler is required #endif

This example aborts the compilation process if the defined constant __cplusplus is not defined.

#include

This directive has also been used assiduously in other sections of this tutorial. When the preprocessor finds an #include directive it replaces it by the whole content of the specified file. There are two ways to specify a file to be included:

#include "file" #include <file>

The only difference between both expressions is in the directories in which the compiler is going to look for the file. In the first case in that the file is specified between quotes the file is looked for from the same directory in which the file that includes the directive is, and only in case that it is not there the compiler looks for in the default directories where it is configured to look for the standard header files.

In case that the file name is included enclosed between angle-brackets <> the file is directly looked for in the default directories where the compiler is configured to look for the standard header files.

#pragma

This directive is used to specify diverse options to the compiler. These options are specific for the platform and the compiler you use. Consult the manual or the reference of your compiler for more information on the possible parameters that you can define with #pragma.

Input/Output with files

As an extension to the standard stream ( FILE) approach well known from the C programming language, C++ offers an I/O library based on class concepts.

Earlier we've already seen examples of the use of the C++ I/O library, especially the use of the insertion operator (<<) and the extraction operator (>>). In this chapter we'll cover the library in more detail.

The discussion of input and output facilities provided by the C++ programming language heavily uses the class concept, and the notion of member functions. we think it is well possible to introduce input and output (I/O) facilities well before discussing the technical background of these topics.

The use of the C++ I/O library offers the additional advantage of type safety in all kinds of standard situations. Objects (or plain values) are inserted into streams. Compare this to the situation commonly encountered in C where the fprintf() function is used to indicate by a format string what kind of value to expect where. Compared to this latter situation C++'s iostream approach immediately uses the objects where their values should appear, as in

133

Object Oriented Programming using C++

cout << "There were " << nMaidens << " virgins present\n";

The compiler notices the type of the nMaidens variable, inserting its proper value at the appropriate place in the sentence inserted into the cout iostream.

Compare this to the situation encountered in C. Although C compilers are getting smarter and smarter over the years, and although a well-designed C compiler may warn you for a mismatch between a format specifier and the type of a variable encountered in the corresponding position of the argument list of a printf() statement, it can't do much more than warn you. The type safety seen in C++ prevents you from making type mismatches, as there are no types to match.

Apart from this, the iostreams offer more or less the same set of possibilities as the standard streams of C: files can be opened, closed, positioned, read, written, etc..

figure 3: Central I/O Classes

This chapter is organized as follows see also figure figure 3:

First, the ios class is introduced. The ios class forms the foundation of all I/O operations, and defines, among other things, the facilities for inspecting the state of I/O streams and output formatting. Every class of the I/O library doing input or output is derived from this ios class, and inherits its capabilities. This means that a member function like good(), which is defined in the ios

134

ios::ios(streambuf *)

Object Oriented Programming using C++

class, is also available in classes that were derived from ios, like istream and ofstream. The reader is urged to keep this feature in mind while reading this chapter. The concept of inheritance is not discussed further here, but rather in chapter 13.

Next, basic C++ output facilities are discussed. The basic class used for output is ostream, defining the insertion operator as well as other facilities for writing information to streams. Apart from inserting information in files it is possible to insert information in memory buffers, for which the ostringstream class is available. Formatting of the output is to a great extent possible using the facilities defined in the ios class, but it is also possible to insert formatting commands directly in streams, using manipulators. This aspect of C++ output is discussed as well.

Basic C++ input facilities are available in the istream class. This class defines the insertion

operator and related facilities for input. Analogous to the ostringstream a class istringstream class is available for extracting information from memory buffers.

Finally, several advanced I/O-related topics are discussed: Among other topics, combined reading and writing using streams; C and C++ I/O> mixing C and C++ I/O using stdiobuf ojects; communicating with external processes using procbuf objects and marking positions in streams using streammarker objects are discussed. The basic I/O facilities are declared in the iostream header file: the preprocessor directive #include <iostream> is required to use the ostream and istream classes, e.g., cout and cin.

The foundation: ios

The ios class forms the foundation of all I/O operations, and defines, among other things, the facilities for inspecting the state of I/O streams and output formatting. Every class of the I/O library doing input or output is derived from this ios class, and inherits its capabilities.

The discussion of the class ios precedes the introduction of members that can be used for actual reading from and writing to streams. But as the ios class is the basis on which all I/O in C++ was built, we start with discussing this part of the C++ I/O library.

Note, however, that as in C, I/O in C++ is not part of the language (although it is part of the ANSI/ISO standard on C++): although it is technically possible to ignore all predefined I/O facilities, nobody actually does so, and the I/O library represents therefore a de facto I/O standard in C++.

For the sake of completeness and not so much because it is necessary to understand the ongoing discussion, it is noted here that it is not possible to construct an ios object directly. classes that are derived from ios (like ostream) may construct ios objects indirectly using the

constructor.

The ios object internally uses a streambuf in which the state of the stream that is managed by the ios object is maintained. In several situations it is desirable to access the streambuf layer of the ios object. For this, the ios class defines the member streambuf *ios::rdbuf()

Condition states

Operations on streams may succeed and they may fail for several reasons. Whenever an operation fails, further read and write operations on the stream are suspended. It is possible to inspect (and possibly: clear) the condition state of streams, so that a program can repair the problem, instead of having to abort.

Conditions are represented by the following condition flags:

ios::badbit:

135

Object Oriented Programming using C++

if this flag has been raised an illegal operation has been performed, like an attempt to read beyond end-of-file.

ios::eofbit:

if this flag has been raised, the ios object has sensed end of file.

ios::failbit:

if this flag has been raised, an operation has failed, like an attempt to extract an int when no numeric characters are available on input.

ios::goodbit:

this flag indicates that no failure has been sensed for the ios object. The ios::failbit and ios::goodbit are each other's complements.

Several condition member functions are available to manipulate or determine the states of ios objects:

ios::bad():

this member function returns a non-zero value when an invalid operation has been requested (i.e., when ios::goodbit has been set).

ios::eof():

this member function returns a non-zero value when end of file (EOF) has been sensed (i.e., ios::eofbit has been set).

ios::fail():

this member function returns a non-zero value when ios::eof() or ios::bad() returns a non-zero value.

ios::good():

this member function returns a non-zero value when ios::fail() returns a zero value and vice versa.

Ios objects can also be interrogated using boolean operators: true is returned in boolean expressions for ios objects if ios::good() would have returned a non-zero value. So, a construction like

cin >> x; // cin is also an ios object

if (cin)

cout << "Good extraction of `x'\n";

is possible. Also, the complementary test is possible:

cin >> x; // cin is also an ios object

136

Object Oriented Programming using C++

if (!cin)

cout << "Extraction of `x' failed\n";

Once an error condition has been raised (ios::goodbit has not been set), further processing of the ios object is suspended.

The following members are available for the mangement of error states:

ios::clear():

When an error condition has occurred, and the condition can be repaired, then clear() can be called to clear the error status of the file.

ios::rdstate():

This member function returns the current set of flags that are set for an ios object. To test for a particular flag, use the bitwise and operator:

if (iosObject.rdstate() & ios::good)

{

// state is good

}

ios::setState(int flags):

This member is used to set a particular set of flags. The member ios::clear() is a shortcut to clear all error flags. Of course, clearing the flags doesn't automatically mean the errorcondition has been cleared too. The strategy should be:

An error condition is detected,

The error is repaired

The member ios::clear() is called.

Formatting output and input

The way information is written to ios objects (or, occasionally, read from ios objects) may be controlled by formatting flags.

Formatting may involve the control of the width of an output field or an input buffer or the form (e.g., the radix) in which a value is displayed. Most of the format control is realized in the context of the ios class, although most formatting is actually found with output streams, like the upcoming ostream class. Since the formatting is controlled by flags, which are defined in the ios class, it was considered best to discuss formatting with the ios class itself, rather than with a selected derived class, where the choice of the derived class would always be somewhat arbitrarily.

The flags control the formatting, but the flags can be altered basically in two ways: using specialized member functions or using manipulators, which are directly inserted in streams. Manipulators are not applied directly to the ios class, as they require the use of the insertion operator.

Formatting flags

137

Object Oriented Programming using C++

Most formatting flags are related to outputting information. Information can be written to output streams in basically two ways: binary output will write information directly to the output stream, without conversion to some human-readable format. E.g., an int value is written as a set of four bytes. Alternatively, formatted output will convert the values that are stored in bytes in the computer's memory to ASCII-characters, in order to create a human-readable form.

Formatting flags can be used to define the way this conversion takes place, to control, e.g., the number of characters that are written to the output stream.

The following formatting flags are available

ios::basefield:

used in combination with a flag setting the radix of integral values to output (ios::dec, ios::hex or ios::oct, see below).

ios::dec:

to display integral values as decimal (i.e., radix 10) values. This is the default.

ios::fixed:

to display real values in a fixed notation (e.g., 12.25), as opposed to displaying values in a scientific notation (see below).

ios::floatfield:

used in combination with a flag setting the way real numbers are displayed ios::fixed or ios::scientific,

ios::hex:

to display integral values as hexadecimal values (i.e., radix 16) values.

ios::internal:

to add fill characters (blanks by default) between the minus sign of negative numbers and the value itself.

ios::left:

to left-adjust (integral) values in fields that are wider than needed to display the values. By default values are right-adjusted (see below).

ios::oct:

to display integral values as octal values (i.e., radix 8) values.

ios::right:

to right-adjust (integral) values in fields that are wider than needed to display the values. This is the default adjustment.

138

Object Oriented Programming using C++

ios::scientific:

to display real values in scientific notation (e.g., 1.24e+03).

ios::showbase:

to display the numeric base of integral values. With hexadecimal values the 0x prefix is used, with octal values the prefix 0. For the (default) decimal value no particular prefix is used.

ios::showpoint:

display a trailing decimal point and trailing decimal zeros when real numbers are displayed. When this flag is set, an insertion like:

cout << 16.0 << ", " << 16.1 << ", " << 16 << endl;

could result in:

16.0000, 16.1000, 16

Note that the last 16 is an integral rather than a real number, and is not given a decimal point: ios::showpoint has no effect here. If ios::showpoint is not used, then trailing zeros are discarded. If the decimal part is zero, then the decimal point is discarded as well.

ios::showpos:

display a + character with positive values.

ios::skipws:

used for extracting information from streams. When this flag is is (which is the default) leading white space characters (blanks, tabs, newlines, etc.) are skipped when a value is extracted from a stream. If the flag is not set, leading white space characters are not skipped.

ios::stdio:

flush the standard C streams stdout and stderr after each output operation.

ios::unitbuf:

flush the stream after each output operation.

ios::uppercase:

use capital letters in the representation of (hexadecimal or scientifically formatted) values.

Format modifying member functions

139

Object Oriented Programming using C++

Several member functions are available for I/O formatting. They are:

ios::fill() const:

returns (as char) the current padding character. By default, this is the blank space.

ios::fill(char padding):

redefines the padding character. Returns (as char) the previous padding character.

ios::flags() const:

returns the current collection of flags controlling the format state of the stream for which the member function is called. To inspect a particular flag, use the binairy and operator, e.g.,

if (cout.flags() & ios::hex)

{

// hexadecimal output of integral values

}

ios::flags(fmtflags flagset):

returns the previous set of flags, and defines the current set of flags as flagset, defined by a combination of formatting flags, combined by the binary or operator.

ios::precision() const:

returns (as int) the number of significant digits used for outputting real values (default: 6).

ios::precision(int signif):

redefines the number of significant digits used for outputting real values, returns (as int) the previously used number of significant digits.

ios::setf(fmtflags flag):

returns the previous set of all flags, and sets one formatting flag (leaving the remaining flags unaltered).

ios::setf(fmtflags flag, fmtflags fieldtype):

returns the previous set of all flags, and sets the way values are represented:

setf(ios::hex, ios::basefield) is used to activate the hexadecimal representation of integral values

(alternatively, ios::dec and ios::oct can be used).

setf(ios::fixed, ios::floatfield) is used to activate the fixed value representation of real values

(alternatively, ios::scientific can be used).

ios::unsetf(fmtflags flag):

140