优秀的编程知识分享平台

网站首页 > 技术文章 正文

std::cin、std::cout、std::cerr和std::endl在C++里的真实面目

nanyue 2024-07-18 22:18:37 技术文章 13 ℃

C++输入和输出

在C++里std::cin、std::cout、std::cerr和std::endl分别是标准输入、标准输出、标准错误输出和刷新缓冲区并换行,它们都在命名空间std中,那么它们真实面目是什么?我们先来看一段代码:

#include <iostream>

int main()
{
std::cout << "Hello World!" << std::endl;
std::cerr << "error" << std::endl;
return 0;
}

这段代码很简单,就是输出"Hello world!"和"error",那么这段代码的底层原理是?我们先来看一下std::cout在标准库中的定义:

#ifndef _LIBCPP_HAS_NO_STDOUT
extern _LIBCPP_FUNC_VIS ostream cout;
extern _LIBCPP_FUNC_VIS wostream wcout;
#endif

......

typedef basic_streambuf<char> streambuf;
typedef basic_istream<char> istream;
typedef basic_ostream<char> ostream;
typedef basic_iostream<char> iostream;

......

template <class _CharT, class _Traits>
class _LIBCPP_TEMPLATE_VIS basic_ostream
: virtual public basic_ios<_CharT, _Traits>
{ ...... };

从以上代码我们可以看出std::cout是一个类basic_stream<char>的一个实例,那么很容易我们就能想到<<很有可能是类basic_stream<char>的一个成员函数,继续追踪下去,看看<<到底是啥。在类模板basic_stream中我们找到成员函数声明如下:

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);

充分证实了我们猜想,<<其实是成员函数operator<<并且返回值是basic_ostream&,到这里我们就可以看出std::cout << "Hello World!"其实是basic_ostream实例变量cout调用成员函数operator<<输出字符串"Hello World!"并返回basic_ostream&。

那么std::endl是不是某个类的实例呢?我们看看std::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;
}

从代码里可以看出,std::endl其实是一个函数模板,调用该函数会将一个换行符"\n"放入缓冲区,并刷新缓冲区,最后返回basic_ostream&。到这里我们终于明白std::cout << "Hello World!" << std::endl;的含义了,basic_ostream实例变量cout调用成员函数operator<<输出字符串"Hello World!",返回basic_ostream&并继续调用成员函数operator<<输出换行符并刷新输出缓冲区。

现在我们很容易想到std::cerr和std::cout应该差不多,区别则是std::cerr是标准错误输出,将信息输出到标准错误流。std::cerr定义如下:

extern _LIBCPP_FUNC_VIS ostream cerr;
extern _LIBCPP_FUNC_VIS wostream wcerr;
extern _LIBCPP_FUNC_VIS ostream clog;
extern _LIBCPP_FUNC_VIS wostream wclog;

最后我们看看std::cin到底是什么玩意,先来看下下面这段代码:

#include <iostream>

int main()
{
std::string name;
std::cin >> name;

return 0;
}

代码很简单,就是想通过标准输入输入名字,并保存在变量name中。有了上面的经验,我们很容易想到std::cin应该是某个类的实例变量,而>>则是类的成员函数。std::cin的定义如下:

#ifndef _LIBCPP_HAS_NO_STDIN
extern _LIBCPP_FUNC_VIS istream cin;
extern _LIBCPP_FUNC_VIS wistream wcin;
#endif

......

typedef basic_streambuf<char> streambuf;
typedef basic_istream<char> istream;
typedef basic_ostream<char> ostream;
typedef basic_iostream<char> iostream;

......

template <class _CharT, class _Traits>
class _LIBCPP_TEMPLATE_VIS basic_istream
: virtual public basic_ios<_CharT, _Traits>
{ ...... };

从代码中可以看出std::cin是类basic_istream<char>的实例变量,且basic_istream是类模板。下面我们看看>>在basic_istream中声明:

basic_istream& operator>>(basic_streambuf<char_type, traits_type>* __sb);
basic_istream& operator>>(bool& __n);
basic_istream& operator>>(short& __n);
basic_istream& operator>>(unsigned short& __n);
basic_istream& operator>>(int& __n);
basic_istream& operator>>(unsigned int& __n);
basic_istream& operator>>(long& __n);
basic_istream& operator>>(unsigned long& __n);
basic_istream& operator>>(long long& __n);
basic_istream& operator>>(unsigned long long& __n);
basic_istream& operator>>(float& __f);
basic_istream& operator>>(double& __f);
basic_istream& operator>>(long double& __f);
basic_istream& operator>>(void*& __p);

不出我们所料>>确实是成员函数operator>>并返回basic_istream&,那么这段代码std::cin>>name就很容易理解了,basic_istream<char>类实例变量cin调用成员函数operator>>从标准输入输入数据,并保存在变量name中。

到这里std::cout、std::cin、std::cerr和std::endl的含义终于真相大白了!

往期推荐

C++11很吊的新特性!std::function

C++里std::enable_shared_from_this是干什么用的?

C++ mutable关键字如何使用?

最近发表
标签列表