cout << "Hello World" << endl; 这几乎要成为所有刚学C++同学的第一行代码;可是回头来看,这一句里的问题可真不少,我先列一下我想到的:
- cout是啥,关键字么?不是。那是普通变量?那它在哪定义的?是什么类型?在哪赋值的?
- '<<'是啥?哦,可能是运算符重载
- endl是啥?是个特定字符串么?
这些问题咱们来一个一个解决。
cout是啥
首先跳到cout的定义,在iostream头文件中:略去不重要的,加上点注释,代码如下(不同环境代码也可能不一样)
_LIBCPP_BEGIN_NAMESPACE_STD // 在 __config 头文件中可以看到它的定义:#define _LIBCPP_BEGIN_NAMESPACE_STD namespace std {inline namespace _LIBCPP_NAMESPACE {
#ifndef _LIBCPP_HAS_NO_STDIN
extern _LIBCPP_FUNC_VIS istream cin; // cin 在这里 类型为 istream
extern _LIBCPP_FUNC_VIS wistream wcin;
#endif
#ifndef _LIBCPP_HAS_NO_STDOUT
extern _LIBCPP_FUNC_VIS ostream cout; // cout 在这里 类型为 ostream
extern _LIBCPP_FUNC_VIS wostream wcout;
#endif
extern _LIBCPP_FUNC_VIS ostream cerr;
extern _LIBCPP_FUNC_VIS wostream wcerr;
extern _LIBCPP_FUNC_VIS ostream clog;
extern _LIBCPP_FUNC_VIS wostream wclog;
_LIBCPP_END_NAMESPACE_STD // 在 __config 头文件中可以看到它的定义:#define _LIBCPP_END_NAMESPACE_STD } }
那大概cout应该就是在栈里(具体在内存中的位置还有待考究,目前通过gdb发现cout的地址和栈中普通int变量的地址相差挺远的,不过和一个新new出来的char[]地址很接近,难道是在堆中?它又不是指针,为什么呢?请高人指教!研究变量在内存中位置时发现了这篇文档和这个回答,感觉很赞。)的一个ostream类型的对象。(关于extern关键字,可以看这个回答,解释挺清晰的。extern的常用方法,参考这个问答和这个问答)关于cout这个对象在哪里被初始化的,据本人无根据的猜测应该是在glibc里边的某处,不过我还没找到对应的源码,有高人了解这里细节的话,请多多指教~(可以顺便去这个问题赚20积分)
'<<'是啥,确实是运算符重载
不过对'<<'的重载方法,有的是作为成员方法,有的并没有放在成员方法中。
作为成员方法的有:
basic_ostream& operator<<(basic_ostream& (*__pf)(basic_ostream&)); // 这个方法注意一下,它是在 cout << endl; 时使用的
basic_ostream& operator<<(basic_ios<char_type, traits_type>&
(*__pf)(basic_ios<char_type,traits_type>&));
basic_ostream& operator<<(ios_base& (*__pf)(ios_base&));
basic_ostream& operator<<(bool __n);
basic_ostream& operator<<(short __n);
basic_ostream& operator<<(unsigned short __n);
basic_ostream& operator<<(int __n);
basic_ostream& operator<<(unsigned int __n);
basic_ostream& operator<<(long __n);
basic_ostream& operator<<(unsigned long __n);
basic_ostream& operator<<(long long __n);
basic_ostream& operator<<(unsigned long long __n);
basic_ostream& operator<<(float __f);
basic_ostream& operator<<(double __f);
basic_ostream& operator<<(long double __f);
basic_ostream& operator<<(const void* __p);
basic_ostream& operator<<(basic_streambuf<char_type, traits_type>* __sb);
非成员方法的典型(Hello World里最常用的输出字符串那个'<<'的重载就是非成员方法):
template<class _Traits>
basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __os, const char* __str)
{
return _VSTD::__put_character_sequence(__os, __str, _Traits::length(__str));
}
关于为什么不把它全部作为成员方法,这个问答和这篇文章给了一些解释,《Effective C++》中第23条也有相应的说明(我自己还要再消化一下)。
endl是啥?是个特定字符串么?不,是个函数!
来看一下endl的定义:
template <class _CharT, class _Traits>
inline _LIBCPP_INLINE_VISIBILITY
basic_ostream<_CharT, _Traits>&
endl(basic_ostream<_CharT, _Traits>& __os)
{
__os.put(__os.widen('\n'));
__os.flush();
return __os;
}
是不是和想象中相去甚远?第一次看到这里估计都会和我一样一头雾水,一个函数怎么能交给'<<'来输出呢?
这就要看一下'<<'的这一个重载:
template <class _CharT, class _Traits>
inline _LIBCPP_INLINE_VISIBILITY
basic_ostream<_CharT, _Traits>&
basic_ostream<_CharT, _Traits>::operator<<(basic_ostream& (*__pf)(basic_ostream&))
{
return __pf(*this);
}
这个重载方法接受一个函数指针,然后去执行这个函数(一个看似很简单的cout里居然还有这么骚的操作)。因为有了这个方法,所以像endl这样的方法又有了一个新的名字:manipulators(本人随便翻译下,就叫它操作符吧)。这里列出了所有的操作符。
----------低调的分割线----------
到这里大概就把之前我想到的几个问题回答得差不多了。有问题或者有指教欢迎在评论区讨论~