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

lafore_robert_objectoriented_programming_in_c

.pdf
Скачиваний:
51
Добавлен:
27.03.2023
Размер:
8.94 Mб
Скачать

‘l’ to add a laborer Type selection: s

Enter last name: Faraday Enter number: 2222

Enter number of pubs: 99

‘a’ -- add data for an employee

‘d’ -- display data for all employees ‘w’ -- write all employee data to file ‘r’ -- read all employee data from file ‘x’ -- exit

Type selection: a ‘m’ to add a manager

‘s’ to add a scientist ‘l’ to add a laborer Type selection: l

Enter last name: Smith Enter number: 3333

‘a’ -- add data for an employee

‘d’ -- display data for all employees ‘w’ -- write all employee data to file ‘r’ -- read all employee data from file ‘x’ -- exit

Type selection: w Writing 3 employees

‘a’ -- add data for an employee

‘d’ -- display data for all employees ‘w’ -- write all employee data to file ‘r’ -- read all employee data from file ‘x’ -- exit

Type selection: r Reading 3 employees

‘a’ -- add data for an employee

‘d’ -- display data for all employees ‘w’ -- write all employee data to file ‘r’ -- read all employee data from file ‘x’ -- exit

Type selection: d

1.Type: Manager Name: Johnson Title: President

Golf club dues: 20000

Streams and Files

615

12

AND S

F TREAMS

ILES

Chapter 12

616

2.Type: Scientist Name: Faraday Number: 2222

Number of publications: 99

3.Type: Laborer Name: Smith Number: 3333

Of course you can also exit the program after writing the data to disk. When you start it up again, you can read the file back in and all the data will reappear.

It would be easy to add functions to this program to delete an employee, retrieve data for a single employee from the file, search the file for employees with particular characteristics, and so forth.

Overloading the Extraction and Insertion

Operators

Let’s move on to another stream-related topic: overloading the extraction and insertion operators. This is a powerful feature of C++. It lets you treat I/O for user-defined data types in the same way as basic types like int and double. For example, if you have an object of class crawdad called cd1, you can display it with the statement

cout << “\ncd1=” << cd1;

just as if it were a basic data type.

We can overload the extraction and insertion operators so they work with the display and keyboard (cout and cin) alone. With a little more care, we can also overload them so they work with disk files. We’ll look at examples of both of these situations.

Overloading for cout and cin

Here’s an example, ENGLIO, that overloads the insertion and extraction operators for the Distance class so they work with cout and cin.

//englio.cpp

//overloaded << and >> operators #include <iostream>

using namespace std;

////////////////////////////////////////////////////////////////

class Distance

//English Distance class

{

 

private:

 

int feet;

 

float inches;

 

public:

 

Streams and Files

617

Distance() : feet(0), inches(0.0) //constructor (no args)

{}

//constructor (two args) Distance(int ft, float in) : feet(ft), inches(in)

{}

friend istream& operator >> (istream& s, Distance& d); friend ostream& operator << (ostream& s, Distance& d);

};

 

 

//--------------------------------------------------------------

 

 

istream& operator >> (istream& s, Distance& d)

//get Distance

{

 

//from user

cout << “\nEnter feet: “;

s >> d.feet;

//using

cout << “Enter inches: “;

s >> d.inches;

//overloaded

return s;

 

//>> operator

}

 

 

//--------------------------------------------------------------

 

 

ostream& operator << (ostream& s, Distance& d)

//display

{

 

//Distance

s << d.feet << “\’-” << d.inches << ‘\”’;

//using

return s;

 

//overloaded

}

 

//<< operator

////////////////////////////////////////////////////////////////

int main()

 

{

 

Distance dist1, dist2;

//define Distances

Distance dist3(11, 6.25);

//define, initialize dist3

cout << “\nEnter two Distance values:”;

cin >> dist1 >> dist2;

//get values from user

//display distances cout << “\ndist1 = “ << dist1 << “\ndist2 = “ << dist2; cout << “\ndist3 = “ << dist3 << endl;

return 0;

}

This program asks for two Distance values from the user, and then prints out these values and another value that was initialized in the program. Here’s a sample interaction:

Enter feet: 10

Enter inches: 3.5

Enter feet: 12

Enter inches: 6

dist1 = 10’-3.5” dist2 = 12’-6” dist3 = 11’-6.25”

12

AND S

F TREAMS

ILES

Chapter 12

618

Notice how convenient and natural it is to treat Distance objects like any other data type, using statements like

cin >> dist1 >> dist2;

and

cout << “\ndist1=” << dist1 << “\ndist2=” << dist2;

The << and >> operators are overloaded in similar ways. They return, by reference, an object of istream (for >>) or ostream (for <<). These return values permit chaining. The operators take two arguments, both passed by reference. The first argument for >> is an object of istream (such as cin). For << it’s an object of ostream (such as cout). The second argument is an object of the class to be displayed, Distance in this example. The >> operator takes input from the stream specified in the first argument and puts it in the member data of the object specified by the second argument. The << operator removes the data from the object specified by the second argument and sends it into the stream specified by the first argument.

The operator<<() and operator>>() functions must be friends of the Distance class, since the istream and ostream objects appear on the left side of the operator. (See the discussion of friend functions in Chapter 11.)

You can overload the insertion and extraction operators for other classes by following these same steps.

Overloading for Files

Our next example shows how we might overload the << and >> operators in the Distance class so they work with file I/O as well as with cout and cin.

//englio2.cpp

//overloaded << and >> operators can work with files #include <fstream>

#include <iostream> using namespace std;

////////////////////////////////////////////////////////////////

class Distance

//English Distance class

{

 

private:

 

int feet;

 

float inches;

 

public:

 

Distance() : feet(0), inches(0.0) //constructor (no args)

{ }

//constructor (two args)

Distance(int ft, float in) : feet(ft), inches(in)

{}

Streams and Files

619

friend istream& operator >> (istream& s, Distance& d); friend ostream& operator << (ostream& s, Distance& d);

};

 

//--------------------------------------------------------------

 

istream& operator >> (istream& s, Distance& d)

//get Distance

{

//from file or

char dummy; //for (‘), (-), and (“)

//keyboard

 

//with

s >> d.feet >> dummy >> dummy >> d.inches >>

dummy;

return s;

//overloaded

}

//>> operator

//--------------------------------------------------------------

 

ostream& operator << (ostream& s, Distance& d)

//send Distance

{

//to file or

s << d.feet << “\’-” << d.inches << ‘\”’;

//screen with

return s;

//overloaded

}

//<< operator

////////////////////////////////////////////////////////////////

int main()

 

 

{

 

 

char ch;

 

 

Distance dist1;

 

 

ofstream ofile;

//create and open

ofile.open(“DIST.DAT”);

//output stream

do {

 

 

cout << “\nEnter Distance: “;

 

 

cin >> dist1;

//get distance from user

ofile << dist1;

//write it to output str

cout << “Do another (y/n)? “;

 

 

cin >> ch;

 

 

} while(ch != ‘n’);

 

 

ofile.close();

//close output stream

ifstream ifile;

//create and open

ifile.open(“DIST.DAT”);

//input stream

cout << “\nContents of disk file is:\n”;

 

while(true)

 

 

{

 

 

ifile >> dist1;

//read dist from stream

if( ifile.eof() )

//quit

on EOF

break;

 

 

cout << “Distance = “ << dist1 <<endl;

//display distance

}

 

 

return 0;

 

 

}

 

 

12

AND S

F TREAMS

ILES

Chapter 12

620

We’ve made minimal changes to the overloaded operators themselves. The >> operator no longer prompts for input, since it doesn’t make sense to prompt a file. We assume that the user knows exactly how to enter a feet-and-inches value, including the various punctuation marks. The << operator is unchanged. The program asks for input from the user, writing each Distance value to the file as it’s obtained. When the user is finished with input, the program then reads and displays all the values from the file. Here’s some sample interaction:

Enter Distance: 3’-4.5”

Do another (y/n)? yes

Enter Distance: 7’-11.25”

Do another (y/n)? yes

Enter Distance: 11’-6”

Do another (y/n)? no

Contents of disk file is:

Distance = 3’-4.5”

Distance = 7’-11.25”

Distance = 11’-6”

The distances are stored character by character to the file. In this example the contents of the file would be as follows:

3’-4.5”7’-11.25”11’-6

If the user fails to enter the distances with the correct punctuation, they won’t be written to the file correctly and the file won’t be readable for the << operator. In a real program it’s essential to check the input for errors.

Memory as a Stream Object

You can treat a section of memory as a stream object, inserting data into it just as you would a file. This is useful when you need to format your output in a particular way (such as displaying exactly two digits to the right of the decimal point), but you also need to use a text-output function that requires a string as input. This is common when calling output functions in a GUI environment such as Windows, since these functions often require a string as an argument. (C programmers will remember using the sprintf() function for this purpose.)

A family of stream classes implements such in-memory formatting. For output to memory there is ostrstream, which is derived from (among other classes) ostream. For input from memory there is istrstream, derived from istream; and for memory objects that do both input and output there is strstream, derived from iostream.

Streams and Files

621

Most commonly you will want to use ostrstream. Our next example shows how this works. You start with a data buffer in memory. Then you create an ostrstream object, using the memory buffer and its size as arguments to the stream’s constructor. Now you can output formatted text to the memory buffer as if it were a stream object. Here’s the listing for OSTRSTR:

//ostrstr.cpp

//writes formatted data into memory #include <strstream>

#include <iostream>

#include <iomanip>

//for setiosflags()

using namespace std;

 

const int SIZE = 80;

//size of memory buffer

int main()

 

 

{

 

 

char ch = ‘x’;

//test data

int j = 77;

 

double d = 67890.12345;

 

char str1[] = “Kafka”;

 

char str2[] = “Freud”;

 

char membuff[SIZE];

//buffer in memory

ostrstream omem(membuff, SIZE);

//create stream object

omem << “ch=” << ch << endl

//insert formatted data

<<

“j=” << j << endl

//into object

<<

setiosflags(ios::fixed)

//format with decimal point

<<

setprecision(2)

//two digits to right of dec

<<

“d=” << d << endl

 

<<

“str1=” << str1 << endl

 

<<

“str2=” << str2 << endl

 

<<

ends;

//end the buffer with ‘\0’

cout <<

membuff;

//display the memory buffer

return 0;

}

When you run the program, membuff will be filled with the formatted text:

ch=x\nj=77\nd=67890.12\nstr1=Kafka\nstr2=Freud\n\0

We can format floating-point numbers using the usual methods. Here we specify a fixed decimal format (rather than exponential) with ios::fixed, and two digits to the right of the decimal point. The manipulator ends inserts a ‘\0’ character at the end of the string to provide an EOF. Displaying this buffer in the usual way with cout produces the program’s output:

ch=x

j=77

12

AND S

F TREAMS

ILES

Chapter 12

622

d=67890.12

str1=Kafka

str2=Freud

In this example the program displays the contents of the buffer only to show what it looks like. Ordinarily you would have a more sophisticated use for this formatted data.

Command-Line Arguments

If you’ve ever used MS-DOS, you are probably familiar with command-line arguments, used when invoking a program. They are typically used to pass the name of a data file to an application. For example, you can invoke a word processor application and the document it will work on at the same time:

C>wordproc afile.doc

Here afile.doc is a command-line argument. How can we get a C++ program to read the command-line arguments? Here’s an example, COMLINE, that reads and displays as many command-line arguments as you care to type (they’re separated by spaces):

//comline.cpp

//demonstrates command-line arguments #include <iostream>

using namespace std;

int main(int argc, char* argv[] )

 

 

{

 

 

 

 

cout <<

“\nargc = “ << argc <<

endl;

//number of arguments

for(int

j=0; j<argc;

j++)

 

//display arguments

cout

<< “Argument

“ << j <<

“ = “

<< argv[j] << endl;

return 0;

}

And here’s a sample interaction with the program:

C:\C++BOOK\Chap12>comline uno dos tres

argc = 4

Argument 0 = C:\CPP\CHAP12\COMLINE.EXE Argument 1 = uno

Argument 2 = dos Argument 3 = tres

To read command-line arguments, the main() function (don’t forget it’s a function!) must itself be given two arguments. The first, argc (for argument count), represents the total number of

Streams and Files

623

command-line arguments. The first command-line argument is always the pathname of the current program. The remaining command-line arguments are those typed by the user; they are delimited by the space character. In the preceding example they are uno, dos, and tres.

The system stores the command-line arguments as strings in memory, and creates an array of pointers to these strings. In the example, the array is called argv (for argument values). Individual strings are accessed through the appropriate pointer, so the first string (the pathname) is argv[0], the second (uno in this example) is argv[1], and so on. COMLINE accesses the arguments in turn and prints them out in a for loop that uses argc, the number of command-line arguments, as its upper limit.

You don’t need to use the particular names argc and argv as arguments to main(), but they are so common that any other names would cause consternation to everyone but the compiler.

Here’s a program that uses a command-line argument for something useful. It displays the contents of a text file whose name is supplied by the user on the command line. Thus it imitates the DOS command TYPE. Here’s the listing for OTYPE:

//otype.cpp

//imitates TYPE command

#include <fstream>

//for

file functions

#include

<iostream>

 

 

using namespace std;

 

 

#include

<process.h>

//for

exit()

int main(int argc, char* argv[] )

{

if( argc != 2 )

{

cerr << “\nFormat: otype filename”;

exit(-1);

 

}

 

char ch;

//character to read

ifstream infile;

//create file for input

infile.open( argv[1] );

//open file

if( !infile )

//check for errors

{

 

cerr << “\nCan’t open “ << argv[1];

exit(-1);

 

}

 

while( infile.get(ch) != 0 )

//read a character

cout << ch;

//display the character

return 0;

 

}

 

12

AND S

F TREAMS

ILES

Chapter 12

624

This program first checks to see whether the user has entered the correct number of commandline arguments. Remember that the pathname of OTYPE.EXE itself is always the first commandline argument. The second argument is the name of the file to be displayed, which the user should have entered when invoking the program:

C>otype ichar.cpp

Thus the total number of command-line arguments should equal 2. If it doesn’t, the user probably doesn’t understand how to use the program, and the program sends an error message via cerr to clarify matters.

If the number of arguments is correct, the program tries to open the file whose name is the second command-line argument (argv[1]). Again, if the file can’t be opened, the program signals an error. Finally, in a while loop, the program reads the file character by character and writes it to the screen.

A value of 0 for the character signals an EOF. This is another way to check for EOF. You can also use the value of the file object itself, as we’ve done before:

while( infile )

{

infile.get(ch); cout << ch;

}

You could also replace this entire while loop with the statement

cout << infile.rdbuf();

as we saw earlier in the ICHAR2 program.

Printer Output

It’s fairly easy to use console-mode programs to send data to the printer. A number of special filenames for hardware devices are defined by the operating system. These make it possible to treat the devices as if they were files. Table 12.11 shows these predefined names.

TABLE 12.11 Hardware Device Names

Name

Device

con

Console (keyboard and screen)

aux or com1

First serial port

com2

Second serial port

prn or lpt1

First parallel printer