我怎样才能使ostream成为一个流? (C ++)

时间:2012-02-27 01:50:49

标签: c++ reference ofstream ostream

我正在尝试创建一个简单的记录器类,我希望能够登录到通用ostreamcout / cerr)或文件。我想到的设计是允许构造函数采用ostream&或文件名,在后一种情况下创建ofstream&并将其分配给类'private ostream&这样:

class Log {
private:
    std::ostream& os;
public:
    Log(std::ostream& os = std::cout): os(os) { }
    Log(std::string filename) {
        std::ofstream ofs(filename);
        if (!ofs.is_open())
            // do errorry things
        os = ofs;
    }
};

这样做会给我一个错误,ofstream的赋值运算符是私有的。再看一遍,我想到,对本地对象的引用可能不起作用,并使os指向ostream并在堆上声明和删除它的工作与ofstream案例ostream虽然不是ostream案例,os已经存在且os仅被os引用(因为删除ofstream的唯一地方将在构造函数中,我不知道如何确定os是否指向在堆上创建的ofstream

那么我怎样才能使这项工作成功,即让{{1}}引用在构造函数中用文件名初始化{{1}}?

4 个答案:

答案 0 :(得分:10)

首先,您无法在创建引用后重新绑定引用,只能初始化它们。您可能认为可以这样做:

Log(std::string filename) : os(std::ofstream(filename)) {
    if (!os.is_open())
        // do errorry things
}

但这并不好,因为你让os引用一个临时变量。

当你需要一个必须可选的引用时,也就是说,它需要引用某些东西而不是其他时间,你真正需要的是指针

class Log {
private:
    std::ostream* os;
    bool dynamic;
public:
    Log(std::ostream& os = std::cout): os(&os), dynamic(false) { }
    Log(std::string filename) : dynamic(true) {
        std::ofstream* ofs = new std::ofstream(filename);

        if (!ofs->is_open())
            // do errorry things and deallocate ofs if necessary

        os = ofs;
    }

    ~Log() { if (dynamic) delete os; }
};

以上示例只是为了向您展示正在发生的事情,但您可能希望使用智能指针进行管理。正如Ben Voigt指出的那样,有许多问题会导致你的程序出现无法预料和不受欢迎的行为;例如,当你试图制作上述类的副本时,它会击中粉丝。以下是使用智能指针的上述示例:

class Log {
private:
    std::unique_ptr<std::ostream, std::function<void(std::ostream*)>> os;
public:
    Log(std::ostream& os = std::cout): os(&os, [](ostream*){}) { }

    Log(std::string filename) : os(new std::ofstream(filename), std::default_delete<std::ostream>()) {
        if (!dynamic_cast<std::ofstream&>(*os).is_open())
            // do errorry things and don't have to deallocate os
    }
};

异常os(&os, [](ostream*){})使指针指向给定的ostream&,但当它超出范围时什么都不做;它赋予它一个什么也不做的删除函数。你可以在没有lambdas的情况下做到这一点,这个例子更容易。

答案 1 :(得分:8)

最简单的做法是将您的引用绑定到ofstream,并确保ofstream只要您的对象存在:

class Log
{
    std::ofstream byname;
    std::ostream& os;
public:
    Log(std::ostream& stream = std::cout) : byname(), os(stream) { }
    Log(std::string filename) : byname(filename), os(this->byname)
    {
        if (!os)
            // handle errors
    }
};

异常安全,无法泄漏,并且编译器生成的特殊成员函数是理智的。

答案 2 :(得分:0)

您必须在构造函数的初始化列表中初始化os,就像您在Log(std::ostream& os = std::cout): os_(os) { }中所做的那样,因为os是一个引用,在初始化后无法分配

答案 3 :(得分:0)

在我的Log / Debug类中,我发现创建静态成员变量很有用:

class debug {
  public:
    ...

    // Overload operator() for printing values.
    template<class Type1>
      inline debug&
      operator()(const std::string& name1,
                 const Type1& value1)
      {
        // Prettify the name/value someway in another inline function.
        _stream << print_value(name1, value1) << std::endl;

        return *this;
      }

  private:
    ...
    static std::ostream& _stream;
};

然后在我的debug.cc文件中:

std::ostream& debug::_stream = std::cerr;