18

このような出力用に「空の」ストリームを作成できないのはなぜですか

std::ostream out;

?

clang 3.4この行は、とgcc 4.8.1Linux の両方で明らかに違法libstdc++です。理由は本当にわかりません。つまり、どこからともなくストリームを作成して、好きなように使用できないのはなぜですか? std::ofstream out;代わりに 100% OKであることに注意してください。この背後にあるロジックがわかりません。作成後、このバッファーを使用して他のバッファーと共通のバッファーを共有できるため、オブジェクトの作成直後に何かに初期化するcopyfmt必要がないことを考えると、これはさらに奇妙です。std::ostream

これから逸脱しないでください。必要はありません。ストリームが必要なstringstreamsのは、私がしなければならないことと、iosストリームが提供するメソッドとプロパティのためです。

4

5 に答える 5

16

私もそれを理解していないことを認めます。のデフォルトのコンストラクターがまったく見つかりませんstd::istream。双方向ストリームを作成する場合は、コンストラクターが何も初期化しないため、双方向ストリームが必要になると思いますstd::ios_base。コンストラクターは何も初期化しませが、派生クラスはstd::ios_base::init明示的に呼び出す必要があります。そのコンストラクターで。std::istream多重継承が関係する場合 (つまり、クラスがとの両方から派生する双方向 IO std::ostream)、最も派生したクラスだけが を呼び出すと予想しますstd::ios_base::init。( では std::iostreamstd::ios_base::initは 2 回呼び出されます。) 実は、標準で調べる前に、デフォルトのコンストラクターは保護されていると答えようとしていました。呼び出されなかったからです。std::ios_base::init派生クラスではなく直接使用すると、初期化されていないストリームが発生します。

とにかく、当面の問題には簡単な解決策があります。

std::ostream out( NULL );

また、後でシンクを設定する必要がある関数は、 の非 const バージョンでありrdbuf()、 ではありませんcopyfmt()rdbuf()を読み取り、 へのポインタを設定するために使用され、書式設定フラグをコピーしstreambufます が、 へのポインタには触れませcopyfmt()streambuf

したがって、次のようなことができます。

std::ostream out( NULL );
//  ...
std::filebuf fileBuffer;
if ( filenameGiven ) {
    fileBuffer.open( filename.c_str(), std::ios_base::out );
}
if ( fileIsOpen() ) {
    out.rdbuf( &fileBuffer );
} else {
    out.rdbuf( std::cout.rdbuf() );
}

(私はこれをよくします。実際、ファイルに出力するか、std::cout.

編集:

rdbufさらに別の修正:呼び出しの非 const バージョンなclear()ので、そうする必要はありません。( を呼び出さずにこれを行ったことは知っていましたが、そのセットclear()を見たとき...)initbadbit

とにかく:要約は次のとおりです。通常、有効なストリームバッファへのポインターを のコンストラクターに渡すことが望ましいですstd::ostreamが、それができない場合は、null ポインターを渡し、後で を使用して有効なポインターを設定することは完全に有効です rdbuf()。そして、そうでないと言う答えは単に間違っています。

于 2013-08-03T10:12:30.473 に答える
6

デフォルトコンストラクタstd::basic_ostreamは、通常、を設定せずprotectedに を作成しても意味がなく、デフォルト コンストラクタは実際には初期化を行わないためです (以下を参照)。ただし、ストリーム バッファーを引数として取る別のコンストラクターがあります。何も実行するように設定されていない が本当に必要な場合は、そのコンストラクターを使用できます。std::basic_ostreamstd::basic_streambufstd::ostream

std::ostream out(0);

これstd::ostreamstd::ios_base::badbit、ストリーム バッファが を使用して設定されるまで設定されstd::ios::rdbuf()ます。

std::ostreamのデフォルトのコンストラクターが存在する根本的な理由protectedは、実際には意図的に何も役に立たないからです! 特に、このコンストラクターを [さらに派生したクラスから] 呼び出しても、ストリーム バッファーはまったく初期化されません。標準では、動作を明示的に義務付けていません。 C++2011 を使用して定義されているかのように= default)。ストリーム バッファを初期化するには、 を呼び出す必要がありますstd::ios::init()。この奇妙な動作の理由は、さらに派生したクラスのオブジェクトを構築すると、オブジェクトが 2 回std::iostream初期化されるためです。std::iosstd::ostreamstd::istream

他のいくつかの回答が示唆するのとは異なり、std::ostreamまったく抽象的ではありません。実際には、virtualとにかく 1 つの機能しかなく、それがそのデストラクタです (デストラクタであるのはvirtualたまたま私のせいです。それを強制することが本当に良い考えであったとは完全には確信していません)。

于 2013-08-03T10:15:43.573 に答える
2

std::ostream は一般化されたストリーム クラスです。そのため、ストリーミング先を知る方法がありません。これが、一般化されたクラスを持つことの要点です。

ストリームにデータを送信しても、実際にはデータ自体は保存されません。関連するバッファに転送するだけです。これを考慮すると、一般化されたクラスは空の一般化されたバッファーを作成できないため、このバッファーを割り当てずに一般化されたストリームを作成しても意味がありません。(バッファ自体は具体的な型のバッファである必要があります。これは、パブリック コンストラクタをまったく持たない一般化されたバッファ std::treambuf を見ることでも理解できます)

これを特定のタイプのバッファを持つストリームである std::ofstream と比較すると、ofstream は使用するバッファの種類を認識できるようになり、デフォルトの std::filebuf をインスタンス化できるようになりました。

あなたの具体的な問題を解決するために。

最初に必要なタイプのバッファーを作成してから、パラメーターとしてバッファーを使用して一般化された std::ostream を作成します。その後、std::filebuf::open() を使用してファイルに接続できます。

例:

std::filebuf fileBuffer;
std::ostream myOstream(&fileBuffer); // Hand over the address of the fileBuffer

fileBuffer.open("filename.txt", std::ios::out);
myOstream << "Text to file";
于 2013-08-03T09:45:56.750 に答える