【问题标题】:Obtain a std::ostream either from std::cout or std::ofstream(file)从 std::cout 或 std::ofstream(file) 获取 std::ostream
【发布时间】:2010-09-26 21:17:58
【问题描述】:

如何根据特定的程序条件将std::ostream 绑定到std::coutstd::ofstream 对象?尽管由于多种原因这无效,但我想实现在语义上等同于以下内容的东西:

std::ostream out = condition ? &std::cout : std::ofstream(filename);

我见过一些不是异常安全的例子,比如来自http://www2.roguewave.com/support/docs/sourcepro/edition9/html/stdlibug/34-2.html

int main(int argc, char *argv[])
{
  std::ostream* fp;                                           //1
  if (argc > 1)
     fp = new std::ofstream(argv[1]);                         //2
  else
     fp = &std::cout                                          //3

  *fp << "Hello world!" << std::endl;                         //4
  if (fp!=&std::cout) 
     delete fp;
}

有谁知道更好的、异常安全的解决方案?

【问题讨论】:

    标签: c++ exception-handling iostream


    【解决方案1】:
    std::streambuf * buf;
    std::ofstream of;
    
    if(!condition) {
        of.open("file.txt");
        buf = of.rdbuf();
    } else {
        buf = std::cout.rdbuf();
    }
    
    std::ostream out(buf);
    

    这将 cout 或输出文件流的底层 streambuf 关联到 out。之后,您可以写入“out”,它将最终到达正确的目的地。如果你只是想让std::cout 的所有内容都进入一个文件,你也可以这样做

    std::ofstream file("file.txt");
    std::streambuf * old = std::cout.rdbuf(file.rdbuf());
    // do here output to std::cout
    std::cout.rdbuf(old); // restore
    

    第二种方法的缺点是它不是异常安全的。您可能想编写一个使用 RAII 执行此操作的类:

    struct opiped {
        opiped(std::streambuf * buf, std::ostream & os)
        :os(os), old_buf(os.rdbuf(buf)) { }
        ~opiped() { os.rdbuf(old_buf); }
    
        std::ostream& os;
        std::streambuf * old_buf;
    };
    
    int main() {
        // or: std::filebuf of; 
        //     of.open("file.txt", std::ios_base::out);
        std::ofstream of("file.txt");
        {
            // or: opiped raii(&of, std::cout);
            opiped raii(of.rdbuf(), std::cout);
            std::cout << "going into file" << std::endl;
        }
        std::cout << "going on screen" << std::endl;
    }
    

    现在,无论发生什么,std::cout 都处于干净状态。

    【讨论】:

    • 哦,我喜欢 std::streambuf* 胜过 std::ostream*
    • 但是如果有什么东西抛出,你将在你的streambuf*的cout中留下一个悬空指针,现在被销毁的ofstream。所以要小心,最好写一个 RAII 方法。
    • 我不喜欢劫持 std::cout。这意味着 cout 和 printf 不再等价,这是我想很多开发人员认为理所当然的事情。
    • 如果out 是班级成员,我也可以以某种方式使用这种方法吗?
    • 第一种情况下如何关闭当前与对象关联的文件of.open("file.txt");?
    【解决方案2】:

    这是异常安全的:

    void process(std::ostream &os);
    
    int main(int argc, char *argv[]) {
        std::ostream* fp = &cout;
        std::ofstream fout;
        if (argc > 1) {
            fout.open(argv[1]);
            fp = &fout;
        }
        process(*fp);
    }
    

    编辑:Herb Sutter 在文章 Switching Streams (Guru of the Week) 中解决了这个问题。

    【讨论】:

    • 这似乎并不比原始代码更安全。
    • 是的。如果在处理时抛出异常,则原始代码存在内存泄漏 (*fp
    • 为什么这个答案得到我的投票是因为第一个答案打破了我的一条旧规则“不要弄乱其他对象的内部结构”。仅仅因为你可以替换 rdbuf 并不意味着你应该。
    • Herb 的代码,使用 ?: 运算符和不同类型的第二个和第三个参数以及左值/右值,是无效的,并且不能与现代编译器(如 Comeau Online 或 MSVC 10.0)一起编译。我已经给他发了邮件。但是在修复之前,也许在答案中记下链接到 GOTW 代码是无效的。干杯,
    【解决方案3】:
    std::ofstream of;
    std::ostream& out = condition ? std::cout : of.open(filename);
    

    【讨论】:

    • 如何编译? std::ofstream::open 的返回类型为void
    【解决方案4】:

    引用自this post

    您可以应用类似的方法。

    struct noop {
        void operator()(...) const {}
    };
    
    std::shared_ptr<std::ostream> of;
    if (condition) {
        of.reset(new std::ofstream(filename, std::ofstream::out));
    } else {
        of.reset(&std::cout, noop());
    }
    

    【讨论】:

      【解决方案5】:

      作为 C++ 的新手,我不知道这是否是异常安全的,但这是我通常的做法:

      std::ostream& output = (condition)?*(new std::ofstream(filename)):std::cout;
      

      【讨论】:

      • 永远不要使用原始的new,尤其不要这样。
      【解决方案6】:

      以下简单代码对我有用:

      int main(int argc, char const *argv[]){   
      
          std::ofstream outF;
          if (argc > 1)
          {
              outF = std::ofstream(argv[1], std::ofstream::out); 
          }
      
          std::ostream& os = (argc > 1)? outF : std::cout;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-05
        • 2020-07-12
        • 2021-11-24
        相关资源
        最近更新 更多