【问题标题】:Writing to not opened ofstream写入未打开的流
【发布时间】:2015-02-04 21:56:46
【问题描述】:

我的应用程序需要非常简单的记录器。但有时我不想使用这个记录器来加速我的应用程序。

我的记录器看起来像:

class Logger
{
    public:
        Logger(bool isActive = true)
        {
            mIsActive = isActive;
            if (isActive)
            {
                out.open(pathToLogFile);
            }
        }

        static std::ofstream& log()
        {
            return out;
        }
    private:
        static bool mIsActive;
        static std::ofstream out;
};

在我的应用程序中,我将其用作:

Logger(true);  // in one place
Logger::log() << "Log" << std::endl;

如果我不想使用这个记录器怎么办?

Logger(false);  // in one place. it doesn't open any file.
Logger::log() << "Log" << std::endl; // it shouldn't write anywhere

如果我没有打开文件,

【问题讨论】:

  • 最困扰我的是你如何滥用构造函数来操纵static数据成员。
  • @5gon12eder 用没有免费功能的语言来做会很有趣。 class imAFunction {...} ?
  • 好吧,您总是可以在布尔上下文中评估流对象以查看它是否处于良好状态,如果是则仅写入。就像这样if ( Logger::log() ) { Logger::log() &lt;&lt; "blah blah"; }。但无论如何,在没有检查参考的情况下,是的,我认为写入未打开/处于良好状态的流应该是安全的。编辑:再一次,也许不是:stackoverflow.com/questions/20693758/…
  • @Quentin:我承认曾多次这样做以滥用模板专业化:template&lt;&gt; class imAFunction&lt;int&gt; {int v; imAFunction(int e):v(e+2){} operator int() const {return v;} };
  • 我猜 @MooingDuck 可专用包装器中的静态函数被高估了。

标签: c++ io ofstream


【解决方案1】:

您的记录器存在几个问题。

如果您在已停用的记录器上使用operator&lt;&lt;,则不会发生任何事情,但out 流的状态设置为失败,并且它将一直保留到您重置它为止。这意味着如果随后您重新启动您的记录器,则不会再向其写入任何内容。

如果您在记录器已经处于活动状态时重新激活它,也是如此:out.open() 也会失败,并且不会再写入任何内容。

如果你想保持这个设计,你必须更新你的记录器,如果out 已经打开(使用out.is_open())检查你的构造函数,如果需要clear() 错误标志。

附录: 如果您担心所有这些&lt;&lt; 的性能会在记录器停用时不必要地处理假设输出,那么您可以考虑为您的记录器类使用自定义operator&lt;&lt;,这将考虑mIsActive。这个SO answer on another question 展示了它是如何工作的。

【讨论】:

  • @MooingDuck 在Logger(true) 的情况下你是完全正确的。但是,在Logger(false) 的情况下,如果之前打开了流,则需要将其关闭;否则记录器可能会继续写入。
【解决方案2】:

无论如何,您的方法开销太大。只需使用 ostream:

std::ostream logger(0);

为了写入它,像往常一样使用插入器,但你也可以检查是否有任何东西连接以避免开销:

if(logger)
    logger << "some output" << endl;

现在,为了将输出定向到某处,您附加了一个流缓冲区:

logger.rdbuf(cout.rdbuf()); // redirect to stdout

您也可以使用文件作为目标:

logger.rdbuf(new filebuf("log.text", ios_base::out)); // redirect to a file

免责声明:我没有查找创建 filebuf 的确切参数,但仍希望您能理解。

【讨论】:

    【解决方案3】:

    我运行了一个非常简单的基准测试:

    void
    do_log(std::ostream& os)
    {
      for (long i = 0L; i < 100000000L; ++i)
        os << i << "\n";
    }
    

    将默认构造的std::ofstream 传递给它(未连接到任何文件)

    std::ofstream sink {};
    do_log(sink);
    

    使程序(使用具有-O3 优化级别的 GCC 4.9 编译)需要 3.1 秒才能完成。相反,我将输出连接到/dev/null

    std::ofstream sink {"/dev/null"};
    do_log(sink);
    

    需要 14.2 秒,是四倍多。所以,是的,写入无效流似乎非常有效。

    但是请注意,上面的代码与您的示例不同。如果我把它写成

    void
    do_log(std::ostream& os)
    {
      for (long i = 0L; i < 100000000L; ++i)
        os << i << std::endl;
    }
    

    相反,如果流断开连接,则需要 3.7 秒(这是有道理的,因为无论如何写入它的任何内容都会被丢弃),但如果连接到 /dev/null,则需要 35.5 秒。因此,如果您关心性能,您可能应该首先摆脱重复的输出刷新(如果您不是绝对需要的话)。

    【讨论】:

      猜你喜欢
      • 2017-10-23
      • 1970-01-01
      • 2023-02-08
      • 2019-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多