为什么std :: ostream的构造函数受到保护?

时间:2013-08-03 09:28:25

标签: c++ iostream ostream

为什么我不能像我这样为我的输出创建一个“空”流

std::ostream out;

这行显然是非法的clang 3.4gcc 4.8.1在linux下libstdc++,我真的不明白为什么,我的意思是为什么我不能只创建一个流无处可去,像我想的那样使用它?注意std::ofstream out;是100%确定的。我只是没有得到这背后的逻辑。 如果您考虑在创建之后我可以使用此缓冲区并与copyfmt的其他缓冲区共享公共缓冲区,那么这甚至更奇怪,因此我不需要将std::ostream初始化为正确的从对象的创建到有用。

请不要偏离这一点,我不需要stringstreams,因为我必须做的事情以及他们提供的方法和属性,我需要ios个流。

5 个答案:

答案 0 :(得分:12)

我承认我也不理解。我找不到任何东西 std::istream的默认构造函数,我想 如果你想创建一个双向流,你会想要一个, 因为std::ios_base工作的奇怪方式: 构造函数初始化任何东西,但派生的 class必须在其中明确调用std::ios_base::init 构造函数。当涉及多重继承时(即 双向IO,类派生自两者 std::istreamstd::ostream),我只期望最多 派生类来调用std::ios_base::init。 (在 std::iostreamstd::ios_base::init将被调用两次。) 事实上,在查看标准之前,我就是这样做的 回答默认构造函数是受保护的,因为它 没有打电话给std::ios_base::init,而是直接使用它 而不是派生类,会导致未初始化 流。

无论如何,你的直接问题有一个简单的解决方案:

std::ostream out( NULL );

另外:稍后设置其接收器所需的功能是 {const rdbuf()的非常量版本,而不是copyfmt()rdbuf()是 用于读取和设置指向streambuf的指针, copyfmt()复制格式化标记,但触摸 指向streambuf的指针。

所以你可以这样做:

std::ostream out( NULL );
//  ...
std::filebuf fileBuffer;
if ( filenameGiven ) {
    fileBuffer.open( filename.c_str(), std::ios_base::out );
}
if ( fileIsOpen() ) {
    out.rdbuf( &fileBuffer );
} else {
    out.rdbuf( std::cout.rdbuf() );
}

(我做了很多。事实上,我认为这是通常的 当你不知道是否输出到文件时,成语 或std::cout。)

编辑:

又一次更正:rdbuf的非常量版本调用clear(), 所以你不必。 (我知道我没有打电话给clear()就这样做了,但是 当我看到init设置badbit ...)

无论如何:摘要是:通常更喜欢将指针传递给有效的 streambuf到std::ostream的构造函数,但如果你不能,那就是。{ 完全有效传递空指针,稍后使用设置有效指针 rdbuf()。而另外说的答案是完全错误的。

答案 1 :(得分:9)

std::ostream在概念上是抽象的 - 你不应该创建它们(但有关详细信息,请参阅其他答案)。

std::stringstream为您提供std::ostream所做的一切,因为它源自它。这也意味着您想要std::ostream&的任何地方,例如在函数参数中,您可以传递std::stringstream对象。

你说“没有必要初始化[...]有用”。这毫无意义。您不能使用尚未初始化的任何:偶数int s。

答案 2 :(得分:6)

std::basic_ostream默认构造函数为protected,因为创建std::basic_ostream而不设置其std::basic_streambuf通常没有任何意义并且默认构造函数实际上不进行任何初始化(见下文)。但是,有另一个构造函数将流缓冲区作为参数。如果你真的需要一个std::ostream但没有设置任何东西,你可以使用那个构造函数:

std::ostream out(0);

std::ostream将设置std::ios_base::badbit,直到使用std::ios::rdbuf()设置流缓冲区。

std::ostream默认构造函数为protected的根本原因是它实际上故意不做任何有用的事情!特别是,调用此构造函数[来自另一个派生类]将初始化流缓冲区:标准不明确强制任何行为,即默认构造函数隐式地具有行为生成默认构造函数(或者,在C ++ 2011中,就好像它是使用= default定义的那样)。要初始化流缓冲区,需要调用std::ios::init()。这种奇怪行为的原因是构造进一步派生类std::iostream的对象会将std::ios对象初始化两次,一次通过std::ostream,一次通过std::istream

与其他一些答案不同,std::ostream根本不是抽象的。事实上,它只有一个virtual函数,它是析构函数(析构函数virtual恰好是我的错;我并不完全相信强迫它是一个好主意这一点)。

答案 3 :(得分:2)

stringstream是一个ios信息流。

但关于你的问题:

ostream在某处写。如果你宣布

std::ostream out;
out << "Hello, world!"<<std::endl

"Hello, world!"应写在某处。但是哪里?这取决于每个特定ostream的实施情况。是的,有一个缓冲区,但该缓冲区还取决于具体的实现。

所以,当你说“我想要一个ostream”时,我不得不问 - 一个写东西......到哪里?

根据您的回答,这将告诉我您需要使用ostream的具体实例/实现。

答案 4 :(得分:2)

std :: ostream是一个通用的流类。因为它无法知道流向什么,这就是拥有广义类的重点。

当您将数据发送到流时,它实际上并不存储数据本身。它只是将其传递给相关的缓冲区。考虑到这一点,创建一个通用流而不将此缓冲区分配给它是没有意义的,因为通用类不能创建一个空的通用缓冲区。 (缓冲区本身需要是具体类型的缓冲区,通过查看根本没有公共consstructor的通用缓冲区std :: treambuf也可以理解)

将此与std :: ofstream(具有特定类型缓冲区的流)进行比较,现在可以让ofstream知道它使用的缓冲区类型,从而能够实例化默认的std :: filebuf

解决您的具体问题。

首先创建所需类型的缓冲区,然后使用缓冲区作为参数创建一个通用的std :: ostream。然后,您可以稍后使用std :: filebuf :: open()连接到文件。

示例:

std::filebuf fileBuffer;
std::ostream myOstream(&fileBuffer); // Hand over the address of the fileBuffer

fileBuffer.open("filename.txt", std::ios::out);
myOstream << "Text to file";