11

単純なロガー クラスを作成しようとしていますが、汎用ostream( cout/ cerr) またはファイルにログを記録する機能が必要です。私が念頭に置いている設計は、コンストラクターがostream&ファイル名またはファイル名のいずれかを取得できるようにすることです。後者の場合は、を作成し、それをクラスのプライベートに次のようofstream&に割り当てます。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に気づきました。すでに存在し、参照されているだけです(削除する唯一の場所はコンストラクター内であり、ヒープ上に作成されたものを指しているかどうかを判断する方法がわからないため)。ofstreamostreamostreamosososofstream

では、どうすればこれを機能させることができますか、つまり、コンストラクターでファイル名で初期化されたos参照を作成できますか?ofstream

4

4 に答える 4

11

1 つには、一度作成された参照を再バインドすることはできず、初期化しかできません。あなたはこれができると思うかもしれません:

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&、範囲外になると何もしません。何もしないdeleter機能を提供します。ラムダなしでもこれを行うことができます。この例の方が簡単です。

于 2012-02-27T01:57:12.007 に答える
9

最も簡単なことは、参照を にバインドし、オブジェクトが存続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
    }
};

例外は安全で、リークすることはなく、コンパイラによって生成された特別なメンバー関数は正常です。

于 2012-02-27T02:26:33.127 に答える
0

は、初期化後に割り当てることができない参照であるため、 でos行ったのと同じように、コンストラクターの初期化リストでを初期化する必要があります。Log(std::ostream& os = std::cout): os_(os) { }os

于 2012-02-27T01:58:47.240 に答える
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;
于 2012-02-27T02:51:16.940 に答える