0

重複の可能性:
純粋仮想関数と抽象クラス

クラスがあり、デバッグ情報を出力するために使用される仮想関数を作成したいと考えています。このクラスを継承するすべてのクラスは、この仮想関数を実装する必要があります。std::coutこのようにして、さまざまなクラスが、ラベル、ファイルなどのさまざまな出力デバイスにデバッグ メッセージを出力できます。

基本クラスはメッセージの宛先を認識しません。しかし、これを行う最善の方法がわかりません。printf()任意の数のパラメーターを出力できるようなものを使用することを考えています。しかし、それが何を達成するかはわかりません。何か案は?

4

5 に答える 5

2

あなたが提案するアプローチには欠点があります。すべての派生クラスは、printf のような機能自体を実装する必要があります。これは非常に生産的なアクティビティとは思えず、基本クラスを使用するのが非常に煩わしくなります。

基本クラスで派生クラスが debug を提供することを要求する方が、コード的に負担が少ないかもしれませんostream。または、デフォルトにすることもできますcerr

class Base {
protected:
    virtual std::ostream & debug_stream () const { return std::cerr; }
    //...
};

次に、 の外部ユーザーが にBase情報を挿入できdebug_streamます。

printf スタイルの構文を主張する場合、インターフェイスはFILE *代わりにa を返すことができます。

class Base {
protected:
    virtual FILE * debug_file () const { return stderr; }
    //...
};

したがって、たとえば、派生クラスはこれを行うことができます。

class Derived : public Base {
    static std::ofstream dbgstream;
    std::ostream & debug_stream () const { return dbgstream; }
    //...
};

std::ofstream Derived::dbgstream("/tmp/derived.dbg");

次に、基本クラスのインターフェイスに適切にアクセスできる場合、

void foo (Base *b) {
    //...do something with b
    b->debug_stream()
        << __FILE__ << ":" << __LINE__ << " foo did something"
        << std::endl;
    //...
}
于 2012-07-27T23:08:57.250 に答える
0

The basic idea here is to use a protected virtual function that takes a string as the error/log/debug-message to be printed / logged. Then, your base class exposes a non-virtual function (or set of functions) to construct the string that will be fed to the protected virtual function. To actually create the string, you can use one of many ways, in the style of printf (with varargs or variadic templates (C++11)), or in the style of standard iostreams. Here is one solution in the latter category:

class BaseDebugOutput {
  protected:
    virtual void printMessage(const std::string& aMsg) = 0;

  private:
    std::stringstream temp_stream;
  public:
    virtual ~BaseDebugOutput() { };

    // One << operator for any type.
    template <typename T>
    BaseDebugOutput& operator<<(const T& value) {
      temp_stream << value;
      return *this;
    };

    typedef std::basic_ostream< std::stringstream::char_type,
                                std::stringstream::traits_type >& 
              (*ostream_function_ptr)(
                std::basic_ostream< std::stringstream::char_type,
                                    std::stringstream::traits_type >&);

    // One << operator for things like std::endl.
    BaseDebugOutput& operator<<(ostream_function_ptr p) {
      if(p == ostream_function_ptr(std::endl)) {
        // if the user outputted an end-line, then print the entry:
        temp_stream << std::endl;
        std::string temp_msg;
        std::getline(temp_stream,temp_msg);
        // call the protected virtual function:
        printMessage(temp_msg);
      } else {
        temp_stream << p;
      };
      return *this;
    };
};

An example derived class would be:

class CErrDebugOutput : public BaseDebugOutput {
  protected:
    virtual void printMessage(const std::string& aMsg) {
      std::cerr << "Error reported with message: '" << aMsg << "'." << std::endl;
    };
};

And the use-case would look like this:

int main() {
  BaseDebugOutput* debug_output = new CErrDebugOutput;

  (*debug_output) << "The answer is: " << 42 << "!" << std::endl;

  delete debug_output;
  return;
};

One advantage of the above setup is that you can insert almost anything you want in addition to the error message, such as a time-stamp for example, or just adding the "Error: " string or whatever so that you don't have to repeat that all the time at the points where you issue the messages (call-site).

于 2012-07-27T23:40:41.467 に答える
0

私はこれをするだけです

class Base
{
   virtual int debugfn(const std::string& msg) const = 0;
};

printfつまり、 (フォーマットされた文字列を生成してstdoutに送信する)機能とデバッグ機能を混在させないでください。すでに完成している文字列を引数として取り、派生クラスにそれをどう処理するかを決定させます。int必要に応じてエラーコードにすることができます。それ以外の場合は、を返しますvoid

于 2012-07-27T22:42:29.320 に答える
0

私はこのようなことをします:

class Base 
{ 
protected:
   virtual int doDebug(const std::string& Msg) const = 0; 

public:
   int Debug(const char* MsgFmt, ...) const; 
}; 

int Base::Debug(const char* MsgFmt, ...) const
{
    std::string sMsg;
    va_list args;

    va_start(args, MsgFmt);
    int len = vsnprintf(NULL, 0, MsgFmt, args);
    if (len > 0)
    {
        sMsg.resize(len);
        vsnprintf(&sMsg[0], len + 1, MsgFmt, args);
    }
    va_end(args);

    return doDebug(sMsg); 
}

こうすることで、呼び出し元に柔軟な書式設定を提供できますが、派生クラスは事前に書式設定されたテキストしか与えられないため、それについて心配する必要はありません。

于 2012-07-27T23:14:11.913 に答える
0

頭に浮かぶ最も単純なことは他のすべての回答で提供されていないため、質問を理解していない可能性があります...情報を出力するためのすべての階層に単一のエントリポイントを提供することを意図している場合、これが最も簡単ですアプローチ:

class base {
public:
   virtual std::ostream& print( std::ostream& /*...*/ ) const = 0;
};
std::ostream& operator<<( std::ostream& o, base const & b ) {
   return b.print( o );
}

署名とは異なり、コメント/*...*/が存在するのはoperator<<、署名が固定されていないため、書式設定を制御するために追加の引数を渡すことができるためです (たとえばbool single_lineint indent--if!single_lineを先頭にスペースを挿入int verbosityし、オブジェクトの公開状態のみを出力するかどうかを制御するか、または補助データも...) より豊富な出力を生成します。

単一の実装 (および転送関数) を使用すると、オブジェクトをストリームに出力し、デバッグ用にプログラムの状態でログを生成する方法を取得できます。

一方、派生クラスがさまざまな時点での状態に関するデバッグメッセージを出力するソリューションである場合、メッセージをログに記録する決定と派生型がいつ行うべきかを決定するため、実際には多態的にそれを行うことはできません。 . その場合は、ロギング ライブラリをプルして使用するだけです。ログ レベルがメッセージのタイプよりも低い場合、一般的なログ ライブラリは実行時にほとんどコストがかかりません (つまり、警告のみをログに記録するように構成した場合、デバッグ ログ (生成されない) のコストは非常に小さくなります)。

于 2012-07-28T00:55:56.483 に答える