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

9 c++ reference ofstream ostream

我正在尝试创建一个简单的记录器类,我希望能够记录到通用ostream(cout/ 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;
    }
};
Run Code Online (Sandbox Code Playgroud)

这样做会给我一个错误,即ofstream赋值运算符是私有的.再看一遍,我发现对一个本地对象的引用可能不起作用,并且在堆上创建os一个指向ostream并声明和删除它的指针与该ofstream情况一起工作,尽管不是这种ostream情况,ostream已经存在并且只是被引用os(因为删除的唯一地方os是在构造函数中,我不知道确定是否os指向ofstream堆上创建的方法).

那么我怎样才能使这个工作,即在构造函数中使用文件名初始化os引用ofstream

Set*_*gie 10

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

Log(std::string filename) : os(std::ofstream(filename)) {
    if (!os.is_open())
        // do errorry things
}
Run Code Online (Sandbox Code Playgroud)

但这并不好,因为你正在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; }
};
Run Code Online (Sandbox Code Playgroud)

上面的例子只是为了向您展示正在发生的事情,但您可能希望使用智能指针来管理它.正如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
    }
};
Run Code Online (Sandbox Code Playgroud)

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


Ben*_*igt 8

最简单的方法就是将你的引用绑定到a 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
    }
};
Run Code Online (Sandbox Code Playgroud)

异常安全,不能泄漏,并且编译器生成的特殊成员函数是理智的.