web-dev-qa-db-ja.com

std :: ostreamのコンストラクタが保護されているのはなぜですか?

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

_std::ostream out;
_

この行は、Linuxの_clang 3.4_と_gcc 4.8.1_の両方で明らかに違法です。Linuxでは_libstdc++_を使用しているので、理由がわかりません。私がしたいようにそれを使用しますか?代わりに、_std::ofstream out;_は100%OKであることに注意してください。私はこの背後にある論理を理解していません。作成後にこのバッファを使用して、copyfmtを持つ他のバッファと共通のバッファを共有できるため、_std::ostream_を何かに初期化する必要がないことを考えると、これはさらに奇妙です。有用なオブジェクトの作成からすぐに。

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

17
user2485710

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

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

std::ostream out( NULL );

また、後でシンクを設定する必要がある関数は、rdbuf()ではなく、非constバージョンのcopyfmt()です。 rdbuf()はポインタを読み取ってstreambufに設定するために使用され、copyfmt()はフォーマットフラグをコピーしますが、notポインタに触れます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に出力するかを前もって知らなかったときは、通常のイディオムだと思いました。)

編集:

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

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

14
James Kanze

std::ostreamは概念的に抽象的であり、作成することは想定されていません(ただし、詳細については他の回答を参照してください)。

std::stringstreamは、std::ostreamから派生しているため、すべてを提供します。これは、たとえば関数の引数など、std::ostream&が必要な場所であればどこでも、std::stringstreamオブジェクトを渡すことができることも意味します。

あなたは「有用であるために[それ]を初期化する必要はない[...]」と言います。それはまったく意味がありません。初期化されていないanythingは使用できません:intsでも。

9
SteveLove

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

std::ostream out(0);

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

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

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

6
Dietmar Kühl

stringstreamiosストリームです。

しかし、あなたの質問について:

ostreamはどこかに書き込みます。あなたが宣言した場合

std::ostream out;
out << "Hello, world!"<<std::endl

"Hello, world!"はどこかに書く必要があります。しかしここで?これは、特定のostreamごとの実装によって異なります。はい、バッファがありますが、そのバッファは特定の実装にも依存します。

それで、あなたが「私はostreamが欲しい」と言うとき、私は尋ねなければなりません-何かを書くもの...どこに?

あなたの答えがあれば、それはあなたが使用する必要があるostreamの特定のインスタンス/実装を教えてくれます。

2
rabensky

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";
2
Jorgen G Valley