首先,
不要在不熟悉操作并且愿意承担所有风险的情况下这样做。这仅仅是一个思想实验,演示如何将另一个流绑定到
stdout
,从而创建第二个
cout
。言归正传,下面开始。
如果您想为
stdout
创建另一个流,您需要深入了解编译器的内部结构,并找出它如何定义
cout
、
cerr
和/或
clog
。这将位于与编译器相关的位置,很可能不在您预期的位置;例如,在较旧版本的 Visual Studio 上,您需要查看
crt\src
文件夹中的一些文件:
__PURE_APPDOMAIN_GLOBAL static filebuf fout(_cpp_stdout);
#if defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern ostream cout(&fout);
#else
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cout(&fout);
#endif
struct _Init_cout
{
__CLR_OR_THIS_CALL _Init_cout()
{
_Ptr_cout = &cout;
if (_Ptr_cin != 0)
_Ptr_cin->tie(_Ptr_cout);
if (_Ptr_cerr != 0)
_Ptr_cerr->tie(_Ptr_cout);
if (_Ptr_clog != 0)
_Ptr_clog->tie(_Ptr_cout);
}
};
__PURE_APPDOMAIN_GLOBAL static _Init_cout init_cout;
#define _INTERNAL_BUFSIZ 4096
#define _IOB_ENTRIES 20
#ifndef _STDSTREAM_DEFINED
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
#define _STDSTREAM_DEFINED
#endif
char _bufin[_INTERNAL_BUFSIZ];
FILE _iob[_IOB_ENTRIES] = {
{ _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ },
{ NULL, 0, NULL, _IOWRT, 1, 0, 0 },
{ NULL, 0, NULL, _IOWRT, 2, 0, 0 },
};
_CRTIMP FILE * __cdecl __iob_func(void)
{
return _iob;
}
通过这个方法,我们可以派生出自己的stdout
流,但这会与编译器有关。
#include <iostream>
#include <fstream>
#include <string>
#include <codecvt>
#define _cpp_stdout (&(__iob_func())[1])
typedef std::basic_filebuf<char16_t, std::char_traits<char16_t>> filebuf_c16;
typedef std::basic_ostream<char16_t, std::char_traits<char16_t>> ostream_c16;
int main() {
filebuf_c16 f16out(_cpp_stdout);
ostream_c16 c16out(&f16out);
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
std::string u8tmp = "Hello from char16_t!";
std::u16string u16str = converter.from_bytes(u8tmp);
c16out << u16str << std::endl;
}
结果如下...
Hello from char16_t!
如果您想将第二个标准
ostream
(也称为
basic_ostream<char,char_traits<char>>
)绑定到
stdout
,可以使用类似的方法。请注意,由于
fout
是
static
,因此您需要制作自己的
filebuf
。还要注意的是,这只会引起麻烦,但这并不重要;只需小心数据竞争或任何类似问题即可。
请注意,虽然您可以这样做,但除非您非常了解自己在做什么,并愿意对任何出现问题负责,并愿意花足够的时间深入研究编译器的库和/或代码,以找出它如何实现
stdout
和默认字符串,否则最好不要这样做。
另请注意,您的代码将与编译器紧密耦合,并且同一编译器的未来版本很有可能会破坏它。例如,据我所知,由于CRT的更改,特别是
FILE
的更改,此代码将无法在Visual Studio 2015中编译,但我没有深入研究。
std::cin
和std::cout
的实现开始。 - πάντα ῥεῖstd::cout
。 - Lightness Races in Orbitstd::cout
和std::cerr
是std::ostream
的实例化;它们不是类。它们是实例,预定义为使用_stdout_和_stderr_作为它们的数据接收器。你可以轻松地创建自己的实例,做同样的事情,但它们将是相同的对象,只是名称不同。完全没有意义。如果你真的想给自己更多的工作,你也可以重新发明std::ostream
,但那么你又回到了“我发现大部分都是重载<<运算符”。 - Lightness Races in Orbit