18

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

std::ostream out;

?

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

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

4

5 回答 5

16

我承认我也看不懂。我根本找不到任何默认构造函数,std::istream如果你想创建一个双向流,我认为你会想要一个,因为奇怪的工作方式std::ios_base:构造函数初始化任何东西,但派生类必须std::ios_base::init显式调用在它的构造函数中。当涉及多重继承时(即双向 IO,其中类派生自 std::istreamstd::ostream),我希望只有派生最多的类调用std::ios_base::init. (在 std::iostream, 中std::ios_base::init会被调用两次。)其实在标准中查之前,我正准备回答默认构造函数是受保护的,因为它没有调用std::ios_base::init,并且直接使用它而不是在派生类中使用它会导致未初始化的流。

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

std::ostream out( NULL );

另外:您稍后需要设置其接收器的功能是 的非常量版本rdbuf(),而不是copyfmt(). rdbuf()用于读取和设置指向 的指针streambufcopyfmt()复制格式化标志,但不触及指向 的指针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...)

无论如何:总结是:通常最好将指向有效流缓冲区的指针传递给 的构造函数std::ostream,但如果不能,则传递空指针是完全有效的,然后使用 rdbuf(). 否则的答案是完全错误的。

于 2013-08-03T10:12:30.473 回答
9

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

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

您说“没有必要将 [it] 初始化 [...] 才有用”。这根本没有意义。您不能使用任何尚未初始化的东西:甚至ints。

于 2013-08-03T09:35:28.190 回答
6

默认构造函数std::basic_ostream是因为在不设置它的情况下protected创建一个通常没有任何意义,并且默认构造函数实际上不进行任何初始化(见下文)。但是,还有另一个构造函数将流缓冲区作为参数。如果你真的需要一个没有设置做任何事情的,你可以使用该构造函数:std::basic_ostreamstd::basic_streambufstd::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对象两次,一次 throughstd::ostream和一次 through std::istream

与其他一些答案所暗示的不同,std::ostream它根本不是抽象的。事实上,无论如何它只有一个virtual功能,那就是它的析构函数(析构函数virtual恰好是我的错;我并不完全相信强制这样做真的是个好主意)。

于 2013-08-03T10:15:43.573 回答
2

stringstream是一个ios流。

但是关于你的问题:

某处ostream写着。如果你声明

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

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

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

ostream鉴于您的回答,这将告诉我您需要使用哪个特定实例/实现。

于 2013-08-03T09:34:26.520 回答
2

std::ostream 是一个通用的流类。正因为如此,它无法知道它正在流向什么,这就是拥有一个通用类的全部意义所在。

当您将数据发送到流时,它实际上并不存储数据本身。它只是将其转发给关联的缓冲区。考虑到这一点,创建一个通用流而不分配这个缓冲区是没有意义的,因为通用类不能创建一个空的通用缓冲区。(缓冲区本身需要是具体类型的缓冲区,通过查看根本没有公共构造函数的通用缓冲区 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";
于 2013-08-03T09:45:56.750 回答