3

古い C/C++ アプリケーションのデバッグ マクロをクリーンアップしているときに、この問題に遭遇しましたostrstream。このような:

Tracer() << "some" << " message" << " here";

チェーンの最初の値が上記のような定数文字列の場合ostrstream::str()、トレーサーを呼び出した結果 (これはデストラクタで実行され、結果をキューに挿入します) には、この文字列へのポインタの 16 進数表現が含まれます。文章。したがって、上記のステートメントは次のような結果になり"0x401a37 message here"ます。これは古いマクロでは発生しませんでした。これは、削除された最初の値として常に長い (スレッド ID) を持っていたためです。

gdb を使用してステップインすると、最初の挿入でoperator<<(void const*)は ostrstream が呼び出され、その後の挿入では呼び出しが行われることがわかりましたoperator<< <...>(basic_ostream<...>&, char const*)(読みやすくするためにテンプレートを削除しました)。

誰かがこの動作を説明できますか? これを修正するためのクリーンな方法は何でしょうか? << left最初の引数として使用する簡単な回避策を見つけました- これは安全ですか? これを行うためのより良い方法はありますか?

最小化された例を次に示します。

#include <strstream>
#include <iostream>

using namespace std;

class Trace : public ostrstream {
    public:
        Trace();
        virtual ~Trace();
};

Trace::Trace() : ostrstream() {}
Trace::~Trace() {
    static_cast< ostrstream& >(*this) <<ends;
    char * text = ostrstream::str();
    cout << "MESSAGE: "<< text <<endl;
    delete[] text;
}

int main(){
    Trace() << "some" << " text" << " here";
    Trace() << left << "some" << " text" << " here";
    Trace() << 123 << " text" << " here";
}
4

2 に答える 2

5

まず第一に、引数として受け取るoperator<<which はconst char*非メンバー関数であることに注意してください。void const*そして、引数として取るメンバー関数が存在します。

コードでは、式Trace() << "xyz"はメンバー関数のみを呼び出すことができます。これは、これらの関数が非 const 参照である最初の引数を取るためTrace()、非メンバーoperator<<関数の最初のパラメーターにバインドできないテンポレイを作成するためです。std::ostream&したがって、アドレスを出力する引数として取るmemberTrace() << "xyz"に解決されます! operator<<void*


私のアドバイス:

  • ストリーム クラスから継承しないでください (とにかく非推奨std::ostrstreamです)。
  • むしろ、ストリーム クラスとオーバーロードの単純なラッパーを記述します。operator<<

以下に一例を示します。

#include <sstream> //for std::ostringstream

struct Trace
{
   std::ostringstream ss;

   template<typename T>
   Trace& operator << (T const & data)
   {
        ss << data;
        return *this;
   }
   ~Trace()
   {
       std::cout << ss.str() << std::endl;
   }
};

これで、次のように使用できます。

Trace() << "Hello World\n" << 100 << "\nBye\n";

出力:

Hello World
100
Bye

ライブデモ

于 2013-01-17T14:39:35.030 に答える
5

これTracer()は、 の非 const 参照にバインドできない一時的な (右辺値) であるため、このように機能しoperator<<(basic_ostream<...>&,ます。

operator<<(void const*)ただし、左辺値を必要としないため、 のようなメンバー関数を呼び出すことができます。

次に、メンバー関数は、シーケンス内の次の呼び出しに使用できるストリーム オブジェクトへの参照を返します。operator<<

Tracer() << leftorのように、この方法で任意のメンバー関数を呼び出してTracer() << flush、参照を左辺値参照に「変換」することは、非常に安全です。

C++11 準拠のコンパイラを使用している場合、標準ライブラリにoperator<<(basic_ostream<...>&&,はこれを行う も含まれています。その場合、回避策はもう必要ありません。

于 2013-01-17T14:40:12.940 に答える