输出流作为类成员

Posted

技术标签:

【中文标题】输出流作为类成员【英文标题】:Output stream as class member 【发布时间】:2013-01-10 16:32:39 【问题描述】:

我有一个 c++ 库,它提供了一个具有复杂逻辑的对象。在数据处理期间,这个对象向 std::cout 输出很多东西(现在是硬编码的)。我希望处理输出不要转到标准输出,而是转到 custm 小部件(一些文本显示)。我试图创建一个std::ostream 类成员,并使用参数设置它(std::cout 用于控制台应用程序和其他一些在 GUI 应用程序中处理的 ostream)。但是编译器会抛出以下错误:

[ 14%] 构建 CXX 对象 src/core/CMakeFiles/PietCore.dir/pvirtualmachine.cpp.o /usr/include/c++/4.6/ostream:在构造函数“PVirtualMachine::PVirtualMachine(QString)”中: /usr/include/c++/4.6/ostream:363:7: 错误:'std::basic_ostream::basic_ostream() [with _CharT = char, _Traits = std::char_traits]' 受保护 /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:33:50:错误:在此上下文中 在 /usr/include/c++/4.6/ios:45:0 包含的文件中, 来自 /usr/include/c++/4.6/ostream:40, 来自 /usr/include/c++/4.6/iterator:64, 来自/usr/include/qt4/QtCore/qlist.h:50, 来自/usr/include/qt4/QtCore/qvector.h:48, 来自/usr/include/qt4/QtGui/qpolygon.h:45, 来自 /usr/include/qt4/QtGui/qmatrix.h:45, 来自/usr/include/qt4/QtGui/qtransform.h:44, 来自/usr/include/qt4/QtGui/qimage.h:45, 来自/usr/include/qt4/QtGui/QImage:1, 来自/home/tomasz/Development/C++/piet/src/core/pcodepointer.h:17, 来自/home/tomasz/Development/C++/piet/src/core/pblockmanager.h:9, 来自/home/tomasz/Development/C++/piet/src/core/pvirtualmachine.h:10, 来自/home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:4: /usr/include/c++/4.6/bits/ios_base.h:在成员函数‘std::basic_ios& std::basic_ios::operator=(const std::basic_ios&)’中: /usr/include/c++/4.6/bits/ios_base.h:791:5: 错误:'std::ios_base& std::ios_base::operator=(const std::ios_base&)' 是私有的 /usr/include/c++/4.6/bits/basic_ios.h:64:11:错误:在此上下文中 在 /usr/include/c++/4.6/iterator:64:0 包含的文件中, 来自/usr/include/qt4/QtCore/qlist.h:50, 来自/usr/include/qt4/QtCore/qvector.h:48, 来自/usr/include/qt4/QtGui/qpolygon.h:45, 来自 /usr/include/qt4/QtGui/qmatrix.h:45, 来自/usr/include/qt4/QtGui/qtransform.h:44, 来自/usr/include/qt4/QtGui/qimage.h:45, 来自/usr/include/qt4/QtGui/QImage:1, 来自/home/tomasz/Development/C++/piet/src/core/pcodepointer.h:17, 来自/home/tomasz/Development/C++/piet/src/core/pblockmanager.h:9, 来自/home/tomasz/Development/C++/piet/src/core/pvirtualmachine.h:10, 来自/home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:4: /usr/include/c++/4.6/ostream:在成员函数‘std::basic_ostream& std::basic_ostream::operator=(const std::basic_ostream&)’中: /usr/include/c++/4.6/ostream:57:11: 注意:这里首先需要综合方法‘std::basic_ios& std::basic_ios::operator=(const std::basic_ios&)’ /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:在成员函数‘void PVirtualMachine::setOutput(std::ostream)’中: /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:216:11:注意:合成方法'std::basic_ostream& std::basic_ostream::operator=(const std::basic_ostream&)'首先需要这里

如果有人指出哪里出了问题,我会很高兴,因为我不知道......

我的代码如下所示:

.h 文件 类 PVirtualMachine 私人的: std::ostream 输出; [...] 民众: 无效 setOutput(std::ostream); [...] ; .cpp 文件 无效 PVirtualMachine::setOutput(std::ostream os) 输出=操作系统;

【问题讨论】:

ostream 不可复制。 那么如何将通用引用传递给输出流? @tkoomzaaskz:通过使用std::ostream&,特别是在类型后使用&(称为通过引用传递)。 【参考方案1】:

您可以使用对std::ostream 的引用,这将支持任何类型的输出流,例如标准输出、文件等。只要您只想使用一个流并且该流不会被破坏,就可以了:

class PVirtualMachine 
  private:
    std::ostream & output;
    [...]
  public:
    PVirtualMachine(std::ostream & os = std::cout): output(os)  
    // void setOutput(std::ostream & os)  output = os;  // can't change the reference
    [...]
;

如果您希望此类共享流(因此在此类的生命周期内使其保持活动状态),请使用 std::shared_ptr<std::ostream> 而不是引用。

【讨论】:

/home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:33:1: error: uninitialized reference member 'PVirtualMachine::output' [-fpermissive] - 所以我有在构造函数的初始化列表中传递它? 当然可以。我可能会使用std::reference_wrapper<> 并将其绑定到给定的流引用,如果它是绝对强制性的,我们需要一个引用并且已知对象实例之外的引用的生命周期超过对象本身(例如 cout 可能会,等等)。但无论如何,概念都是一样的。【参考方案2】:

这里有两个选择:

使用参考,或 使用指针

您不能使用普通实例,因为ostream 是不可复制的。

使用引用(直接引用已实例化的ostream

class PVirtualMachine 
  private:
    std::ostream & output;
    [...]
  public:
    PVirtualMachine(std::ostream &);  // Reference must be initialized on construction.
    [...]
;

优点:

没有指针语法。 应该始终引用std::ostream 的有效实例,只要不删除原始变量。

缺点:

PVirtualMachine类必须用初始化列表中的输出引用构造,否则无法编译。 引用一旦初始化就无法更改。 不能使用移动赋值运算符(即operator=(PVirtualMachine &&)

使用指针(对象的可选引用)

class PVirtualMachine 
  private:
    std::ostream * output;
    [...]
  public:
    void setOutput(std::ostream *);
    [...]
;

优点:

可以实例化为空指针。 可以轻松传递。 可以更新为指向新的std::ostream 实例。 可以在 PVirtualMachine 实例内部或外部创建。 与移动赋值运算符一起使用。

缺点:

指针语法。 在访问 ostream 和/或在构造函数中必须检查空引用。

【讨论】:

引用的第二个缺点应该反映在它前面的代码中。 IE。应该没有 setOutput() 成员,而是选择构造初始化列表。 好吧,你仍然可以稍后设置输出,但是是的,它确实需要最初构建。 一旦它被构造初始化(它必须是),你不能改变它。引用是仅初始化的。因此,在基本情况下,对setOutput() 的需求就消失了。 @WhozCraig 真的吗?有趣的。我一直认为这是可能的。我想我从来没有尝试过!我会更新我的答案。 是的,没有骰子。如果您绝对不得不参考,您可以使用std::reference_wrapper<> 之类的东西玩游戏,但对于您介绍的情况,它是construct-init-only .【参考方案3】:

我实际上会在我可能想要调试的模块中使用一个 ostream 实例。请注意,此类型没有默认构造函数,您必须将指针传递给流缓冲区,但该指针可以为空。现在,当/如果您想捕获模块的输出时,您只需使用rdbuf() 将流缓冲区附加到它。这个流缓冲区可以是 std::cout 的流缓冲区,但它也可以是 std::stringbufstd::filebuf 或一些自动将输出重定向到某个窗口的自写缓冲区。一个警告:没有流缓冲区的输出将设置故障位(甚至是坏位?),因此您必须在更改流缓冲区后在输出流上调用clear()。另请注意,您必须手动管理此流缓冲区和引用它的流的生命周期,不涉及所有权转移和自动清理。

【讨论】:

以上是关于输出流作为类成员的主要内容,如果未能解决你的问题,请参考以下文章

9流类库与输入/输出2.3输出文件流成员函数

FileWriter 字符文件输出流

File类--输入流和输出流

字节流

流概述与字节流输入输出

字节流 字符流