|
应用中,经常会用到 << 插入符将内容送往标准输出,而用 >> 提取符将从标准输入中获取内容。
在讨论重载 << 和 >> 之前,先简要介绍一下 C++ 的 I/O 系统的一些概念。
需要使用 C++ 中的 I/O 系统就需要包含头文件 <iostream>。在这个头文件中定义了一组非常复杂的类层次结构来支持 I/O 操作。I/O 类以一组模板类开始 --- 模板类 定义了一个类的结构但没有指定类使用的数据类型。在创建了一个模板类对象后,模板类才被实例化。
对于 I/O 库而言,标准 C++ 创建了 I/O 模板类的两种实例:一种用于 8 位字符,另一种用于宽字符。一般而言,8 位字符的 I/O 类最为常用。
C++ 的 I/O 系统建立再两种相关但又不相同的模板类层次结构上。第一种类层次结构是从低级的 I/O 类 basic_streambuf 中派生出来。这个类提供了底层且基本的输入输出操作,同时也为整个 C++ I/O 系统提供了根本的支持。除非在程序中需要使用高级的 I/O 技术,否则不需要直接和 basic_streambuf 打交道。
第二种层次结构,也是经常使用到的,是从类 basic_ios 中派生出来的。这是个高级的 I/O 类,它提供了格式化,错误检测以及 I/O 留的状态信息。basic_ios 被用作其它几个派生类的基类,包括 basic_istream,basic_ostream 以及 basic_iostream 。这些类分别用来创建能够输入、输出以及 输入/输出的流。
下面的表中给出了模板类的名字以及对应的基于 8 位字符的实例类:
模板类 | 基于8位字符的类 | basic_streambuf | streambuuf | basic_ios | ios | basic_istream | istream | basic_ostream | ostream | basic_iostream | iostream | basic_fstream | fstream | basic_ifstream | ifstream | basic_ofstream | ofstream |
下面是一个简单的示例,演示了如何创建一个插入符。首先定义一个名为 three_d 的类:
[C++] 纯文本查看 复制代码
class three_d {
public:
int x, y, z;
three_d(int a, int b, int c) { x = a; y = b; z = c; }
};
要创建类 three_d 的插入符,我们就必须重载运算符 << ,如下所示:
[C++] 纯文本查看 复制代码
ostream &operator << (ostream &stream, three_d obj)
{
stream << obj.x << ", ";
stream << obj.y << ", ";
stream << obj.z << "\n";
return stream; //返回流对象
}
在上面的重载函数中,函数被声明为返回一个 ostream 类型对象的引用。只有这样声明,重载的 << 运算符才能在复合的 I/O 表达式中。
函数有两个参数,第 1 个参数是对 << 运算符左边的流对象的引用;第 2 个是运算符右边的对象,如果你愿意的话,这个参数也可以是该对象的引用。
在上面的函数中,three_d 类型对象中的 3 个坐标值(x, y, z)被插入到流中,并且函数将返回这个流。
下面程序演示了插入流的用法:
[C++] 纯文本查看 复制代码 #include <iostream>
#include <fstream>
using namespace std;
class three_d {
public:
int x, y, z;
three_d(int a, int b, int c)
{
x = a;
y = b;
z = c;
}
};
//输出X,Y,Z的坐标值 -- 通过类three_d 的插入符
ostream &operator << (ostream &stream, three_d obj)
{
stream << obj.x << ", ";
stream << obj.y << ", ";
stream << obj.z << "\n";
return stream; //返回流对象
}
int main()
{
three_d a(1, 2, 3), b(3, 4, 5), c(5, 6, 7);
cout << a << b << c;
return 0;
}
运行输出:./reload_ios
1, 2, 3
3, 4, 5
5, 6, 7 如果去掉插入符函数中合 three_d 相关的代码,那么抽象起来就得到了插入符函数的框架,如下所示:
[Plain Text] 纯文本查看 复制代码
ostream &operator<<(ostream &stream, class_type obj)
{
//与具体的类相关的代码
return stream; //返回流对象
}
当然,也可以将 obj 的引用作为参数传递到函数中。
也许可能会有疑问:为什么类 three_d 的插入符函数没有以下面的形式实现:
[C++] 纯文本查看 复制代码
ostream &operator << (ostream &stream, three_d obj)
{
cout << obj.x << ", ";
cout << obj.x << ", ";
cout << obj.x << "\n";
return stream; //返回流对象
}
注意上面使用了 cout 代替了先前的 stream 。
在上面的实现形式中,流 cout 是被硬编码到函数中的,这就限制了函数可被使用的环境。
记住:运算符 << 可以应用到任何一种流,并且 << 表达式中的流对象将被作为参数传递给函数。
我们可以改造一下上面的程序以验证上面的说法:
[C++] 纯文本查看 复制代码 #include <iostream>
#include <fstream>
using namespace std;
class three_d {
public:
int x, y, z;
three_d(int a, int b, int c)
{
x = a;
y = b;
z = c;
}
};
//输出X,Y,Z的坐标值 -- 通过类three_d 的插入符
ostream &operator << (ostream &stream, three_d obj)
{
cout << obj.x << ", ";
cout << obj.y << ", ";
cout << obj.z << "\n";
return stream; //返回流对象
}
int main()
{
ofstream out("test.txt");
if (!out) {
cout << "Cannot open file.\n";
return 1;
}
three_d a(1, 2, 3), b(3, 4, 5), c(5, 6, 7);
out << a << b << c;
return 0;
}
在上面程序中,我们将 cout 硬编码到插入符函数中。另外,在 main 中为打开并写入当前目录下的 test.txt 文件而建立了一个输出流 out (希望写入文件) 。当我们运行程序时,可以看到标准输出中可以输出:
./reload_ios
1, 2, 3
3, 4, 5
5, 6, 7 但是打开文件 test.txt 查看时是没有内容的。这是因为在插入符函数中已经将输出流写死为 cout (标准输出),实际上在函数内部根本没有对 out 这个流有任何操作,它只是被简单的返回。
然而,如果我们将上面的 cout 改成 stream 的话那就可以正常的写入文件了,这是因为我们通过参数中队 out 的引用,将数据输送到其中,然后再返回这个引用。 |
|