1

xcode 6.1、libz.1.dylibでのios開発にgzstream 1.5を使用しようとしています。

このライブラリはかなり前に作成されました。

私はそれを見つけました

class igzstream : public gzstreambase, public std::istream

する必要があります

class igzstream : public gzstreambase, public virtual std::istream

オグズストリームも同様。

ファイルが存在しない場合、最初のバリアントは初期化後に good() に対して true を返すためです。私の知る限り、それは 2 つの祖先 std::ios によるものです。

本当にバグなのか、なぜまだ修正されていないのか不思議です!

4

2 に答える 2

3

C++ 標準では、[lib.istream] に従って、実質的に から派生した [lib.iostream.format]std::istreamの typedef として名前を定義しています。一方、は[lib.iostream.forward] で の typedef として定義されているから仮想的に派生しています。したがって、両方の継承ブランチには、(別名) への仮想継承関係があります。std::basic_istream<char>std::basic_ios<char>gzstreambasestd::iosstd::basic_ios<char>std::iosstd::basic_ios<char>

標準ライブラリの実装が壊れていない場合std::ios、igzstream で 2 つのサブオブジェクトを取得するべきではありませんが、基本クラスを virtual と宣言すると、基本クラスの初期化の順序が変更されるため、さらなる結果が生じます。

クラス igzstream : public gzstreambase、public std::istream

仮想基本クラス (間接的なものであっても) が最初std::iosに初期化されるため、最初に初期化され、次に初期化されますstd::ios_base(それ自体の非仮想基本クラス)。次に、非仮想基本クラスが左から右の順序で初期化されるため、gzstreambase最初にstd::istream.

クラス igzstream : パブリック gzstreambase、仮想パブリック std::istream

仮想基本クラス (間接的なものであっても) が最初std::iosに初期化されるため、最初に初期化され、次に初期化されますstd::ios_base(それ自体の非仮想基本クラス)。次にstd::istream、まだ別の仮想基底クラスであるため、初期化されますが、 が必要std::iosであり、最後に gzstreambase.

これを念頭に置いて、 igzstream のコンストラクターは、継承されたメンバー buf が初期化される前に、buf と呼ばれる gzstreambuf メンバーのアドレスをオブジェクトのコンストラクターに渡すため、からの仮想派生は非常に悪い考えstd::istreamのように思えると判断できます。std::istream

[lib.istream.cons]によると、おそらく問題の原因は をgzstreambase(consth char *, int)呼び出しstd::ios::init()std::istreamコンストラクターが同じように動作することです。関数std::ios::initストリームを良好な状態で初期化することが文書化されています。そのため、istream サブオブジェクトが gzstreambase オブジェクトの後に初期化された場合、ios ベース オブジェクトの 2 番目の init で実際にエラー フラグがクリアされるはずです。これは実際には gzstream ライブラリのバグのようです。正しい構築順序 (最初に gzstreambuf、2 番目に istream、次にファイルを開こうとする) を取得することは、まったく重要な問題のように思えます。元のバージョンには「gzstreambuf、open、istream」があり、istream はオープンの失敗を上書きしますが、提案された修正には「istream、gzstreambuf、open」があり、istream はまだ構築されていない streambuf のアドレスを取得します。

gzstream の開始コンストラクターを使用しないことが回避策ですが、開始コンストラクターを修正するための適切な解決策を考え、結果がわかり次第回答を編集します。

誰に尋ねるかによって、複数の init 呼び出しが可能 ( http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#135の通常の解釈) または未定義 ( http:/ /article.gmane.org/gmane.comp.lib.boost.devel/235659 )。Microsoft コンパイラでは、init を複数回呼び出すとメモリ リークが発生し、Dinkumware (Microsoft が使用する I/O ライブラリを提供) は、標準では複数回の呼び出しでの動作が指定されていないため、未定義の動作であると主張しています。

したがって、実用的な移植動作のために、init を繰り返し呼び出さないでください。しかし、これは gzstream で起こることです。これは実際には、C++ のような多重継承の反対者が正しいと思われる状況の 1 つです。「istream インターフェイス」を提供できるようにするには、std::istream を継承する必要がありますが、一方で、std::istream を継承する必要はありません。これは、コンストラクターが望ましくないことを行うためです。std::istream が「単なるインターフェース」である場合、gzstreambase から派生する実装と一緒に問題なく実装できます。

この場合の唯一の解決策は、open を実行する gzstreambase コンストラクターを削除し、igzstream および ogzstream コンストラクターに open 呼び出しを配置することです (したがって、open の呼び出しを複製します)。このようにして、init が istream/ostream コンストラクターで一度だけ呼び出されることに依存できます。

于 2014-11-22T15:44:12.083 に答える
0

ライブラリを変更せずに考えられる回避策は、既定のコンストラクターを使用し、後でファイルを開くことです。

例:

igzstream noErrorFile("nonExistentFile");  // no error
cout << "error initializing with non-existent file " << noErrorFile.fail() << endl;
igzstream errorFile;
errorFile.open("nonExistentFile");  // error
cout << "error opening with non-existent file " << errorFile.fail() << endl;

結果:

error initializing with non-existent file 0
error opening with non-existent file 1
于 2018-07-24T05:15:10.920 に答える