接触这么久 C++ 了,我想尝试写一个自己的 cout 了。在实现之前,先扯扯几个概念:
一、cout 是谁?
不就是输出语句嘛?!这个答案太浅显。
正确答案: cout 是类 ostream 的一个对象,而这个对象有一个成员重载运算符函数:operator <<
。顺便一说,类 ostream 又属于 iostream 库中,iostream 是标准的 C++ 头文件。既然如此,那么这个头文件里应该是这样写的:
这个 ostream::operator << 有多个重载版本,所以形参什么类型都有,包括 int,double,char 等等。当我们写 cout<<a 时,其实也可以写成 cout.operator<<(a) 。
cout 的语句格式是:cout<<表达式1<<表达式2<<……<<表达式n; ,那么如果按照以上的写法,我们只能写诸如 cout<<a; 的语句,而不能写成 cout<<a<<b<<c; 这样连起来的形式,可是 C++ 里面却能实现这样的写法,这是为什么呢?
原来在 ostream::operator << 里还有一句 return *this 。当执行语句 cout<<a<<b<<c; 时,先执行 cout.operator<<(a) ,然后这个函数会返回 cout,其实就是返回自己本身,然后再去执行 cout.operator<<(b) ,然后又会去执行下一个函数,以此类推。
因此可以这样实现:
二、什么是namespace?
我们经常在程序开头写 using namespace std; ,但有没有想过是干什么用的?
顾名思义,namespace 的意思是 命名空间,它是用来组织和重用代码的。那这个到底有什么用呢?
好,我们在脑中想这么一种情况:你写了一个库文件,里面有个名字叫 fun 的函数,很不幸你另外一个人写的库文件也有个名字叫 fun 的函数,这样就冲突了。
这时,我们就想了一个办法:
把你写的 fun 函数放到一个你命名的空间里,把另外一个人写的 fun 函数放到另一个命名空间里。
只要有人说要用你的空间,那么他用的 fun 函数肯定是你写的 fun 函数,不会是另外一个人的。
如果那个人说要用另一个人的空间,那么他用的 fun 函数肯定是另一个人写的 fun 函数,不会是你的。
如果,你直接使用 cout 而不使用 namespace ,那系统不知道你在用的是谁的 cout (没准有多个 cout 呢?)
扯了这么多,其实这里面表达的意思是:为了解决名字冲突问题,引入了名字空间这个概念,通过使用 namespace xxx; 你所使用的库函数或变量就是在该名字空间中定义的,这样一来就不会引起不必要的冲突了。
那我们为什么要写 using namespace std; ?早期的 C 将标准库功能定义在全局空间里,声明在带 .h 后缀的头文件里。
C++ 标准为了和 C 区别开,也为了正确使用命名空间,规定头文件不使用后缀 .h 。因此,当使用 <iostream.h> 时,相当于在 C 中调用库函数,使用的是全局命名空间,这也是早期的 C++ 实现;
当使用 <iostream> 的时候,该头文件没有定义全局命名空间,必须使用 namespace std; 这样才能正确使用 cout。
假如不写 using namespace std;
,那就要写成 std::cout<<
了。每一句都这么写很烦,于是干脆在程序开头直接来一句 using namespace std;
。
好了,其实这个 iostream 库大概是这么写的:
三、自己实现 cout
以上两个已经讲得很清楚了,下面上实现 cout 的代码:
本代码已经通过编译,并且能正确输出。对于C++的这些特性,当我们学得深入了以后,就很有必要弄懂了。