8.5 重载流插入与流读取运算符
C++ 的流读取运算>>和流插入运算符<<可用来输入输出标准类型的数据。这两个运算符是 C++ 编译器在类库中提供的,可以处理包括类C语言中的char*字符串和指针在内的每一种内部数据类型。也可以重载运两个运算符以输入输出用户自定义类型的数据。图 8.3 中的程序演示了重载的流读取运算符和流插入运算符,它们用来处理用户自定义的电话号码类 PhoneNumber 的数据。程序假定输入的电话号码是正确的,错误检测留给读者在练习中完成。
1 // Fig. 8.3: fig0S03.cpp
2 // Overloading the stream-insertion and
3 // stream-extraction operators.
4 #include <iostream.h>
5 #include <iomanip.h>
6
7 class PhoneNumber {
8 friend ostream &operator<<( ostream&, const PhoneNumber & );
9 friend istream &operator>>( istream&, PhoneNumber & );
10
11 private:
12 char areaCode[ 4 ]; // 3-digit area code and null
13 char exchang[ 4 ]; // 3-digit exchange and null
14 char line[ 5 ]; // 4-digit line and null
15 };
16
17 // Overloaded stream-insertion operator (cannot be
18 // a member function if we would like to invoke-ti with
19 // cout << somePhoneNumber;).
20 ostream &operator<<( ostream &output, const PhoneNumber &num)
21 {
22 output << "(" << num.areaCode << ")"
23 << num.exchange << "-" << num.line;
24 return output; // enables cout << a << b << c;
25 }
26
27 istream &operator>>( istream &input, PhoneNumber &num )
28 {
29 input.ignore(); // skip (
30 input >> setw( 4 ) >> num,areaCode; // input area code
31 // skip ) and space
32 input >> setw( 4 ) >> num.exchange; // input exchange
33 input.ignore();
34 input >> setw( 5 ) >> num.line; // input line
35 return input; // enables cin >> a >> b >> c;
36 }
37
38 int main()
39 {
40 PhoneNumber phone; // create object phone
41
42 cout << "Enter phone number in the form (123) 456-7890:\n";
43
44 // cin >> phone invokes operator>> function by
45 // issuing the call operator>>( ein, phone ).
46 cin >> phone;
47
48 // cout << phone invokes operator<< function by
49 // issuing the call operator<<( eout, phone ).
50 cout << "The phone number entered was: "<< phone << endl;
51 return 0;
52 }
Enter phone number in the form (123) 456-7890:
(800) 555-1212
The phone number entered was: (800) 555-1212
图 8.3 用户自定义的流插入和流读取运算符
流读取运算符函数 operator>>(第27行)含有两个参数,一个是对 istream 的引用(即程序中的 input),另一个则是对用户自定义类型PhoneNumer的引用(即程序中的num)。函数返回一个对 istream 的引用。在图8. 3的程序中,运算符函数 operator>>用来把下述格式的电话号码输入到类PhoneNumber的对象中:
(800) 555 = 1212
当编译器遇到main()函数中的表达式:
cin >> phone
编译器将生成函数调用:
operator >> (cin,phone);
当执行该调用时,引用参数input成为cin的一个别名,Num成为Phone的一个别名。运算符函数使用istream成员函数getline,将电话号码的三部分作为字符串分别读到被引用的PhoneNumber对象(运算符函数中的num和main函数中的phone)的areaCode、exchange和line成员中。流操纵算子sesetw保证将正确的字符数读入到字符数组中。回忆一下,我们曾经使用cin和setw限制读入的字符数比参数少1(例如setw(4)只允许读入3个字符,留出一个位置保存null终止符)。
通过调用istream的成员函数ignore跳过括号、空格、破折号等等(ignore函数删除输入流中指定数目的字符,默认个数为1)。函数operator>>返回对isream对象的引用input(即cin),因而能够在PhoneNumber对象的输入操作完成后,继续执行对PhoneNumber的其他对象或者其他数据类型对象的输入操作。例如,可以像下面那样输入两个 PhoneNumber 对象:
cin >> phone1 >> phone2;
首先是表达式cin >> phone1产生如下调用:
operator >> (cin,phone1);
该调用返回cin并把它作为cin >> phone1的值,因此表达式的其余部分将被简单地解释为cin >> phone2,这将通过下列调用执行:
operator >> (cin,phone2);
流插入运算符有两个参数,一个是对ostream的引用(即output),另一个是对用户自定义类型PhoneNumber的引用(即 num),函数返回一个对ostream的引用。函数operator<<显示了PhoneNumber的对象。当编译器遇到main函数中的表达式:
cout << phone
编译器生成非成员函数调用:
operator << (cout,phone);
因为电话号码的各个部分是以字符串的格式存储的,所以函数 operator<< 以字符串形式显示它们。
注意,函数 operator<< 和 operator>> 在类 PhoneNumber 中被声明为友元函数而不是成员函数。因为要把类 PhoneNumber 的对象作为运算符的右操作数,所以这些运算符函数必须是非成员函数。要把运算符重载为成员函数,类的操作数(类的对象)必须出现在运算符的左边,如果重载的输入和输出运算符必须直接访问类的非public成员,则必须把它们声明为友元。另外,还要注意 operator<< 参数表中引用的PhoneNumber是const类型(因为只输出 PhoneNumber),而 operator>> 参数表中引用的 PhoneNumber 是非 const 类型(由于 PhoneNumber 对象要修改成在该对象中存放输入的电话号码)。
软件工程视点 8.3
无需修改类 ostream 和 istream 的声明和 private 数据成员就可以给用户自定义类型添加新的输入/输出能力。这种方式提高了 C++ 语言的可扩展性,可扩展性是 C++ 的最具吸引力的特点。