gzstream 库打开不存在的文件



我正在尝试在xcode 6.1,libz.1.dylib下使用gzstream 1.5进行iOS开发。

这个库是很久以前写的。

我发现

class igzstream : public gzstreambase, public std::istream

应该是

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

ogzstream也是如此。

因为如果文件不存在,第一个变体在初始化后返回 true 表示 good()。AFAIK 这是因为两个祖先 std::ios。

我想知道它真的是一个错误,为什么它还没有修复!

C++标准将名称std::istream定义为 [lib.iostream.format] 中 typedef 的 std::basic_istream<char>,根据 [lib.istream] 的说法,它实际上是从std::basic_ios<char>派生出来的。另一方面,gzstreambase实际上是从 std::ios 派生而来的,在 [lib.iostream.forward] 中定义为 std::basic_ios<char> 的 typedef。因此,两个继承分支都与std::ios(又名std::basic_ios<char>)具有虚拟继承关系。

如果你的标准库实现没有被破坏,你不应该在 igzstream 中获取两个std::ios子对象,但是通过更改基类初始化的顺序,声明基类 virtual 会产生进一步的后果。

类 IGZSTREAM : public gzstreambase, public std::istream

虚拟基类(甚至是间接基类)首先初始化,因此首先初始化std::ios基类,进而初始化std::ios_base(自身的非虚拟基类)。然后按从左到右的顺序初始化非虚拟基类,因此先gzstreambase,然后std::istream

类 IGZSTREAM: public gzstreambase, virtual public std::istream

虚拟基类(甚至是间接基类)首先初始化,因此首先初始化std::ios基类,进而初始化std::ios_base(自身的非虚拟基类)。然后初始化std::istream,因为它仍然是另一个虚拟基类,但需要std::ios,最后gzstreambase

考虑到这一点,您可以确定从std::istream虚拟派生似乎是一个非常糟糕的主意,因为 igzstream 的构造函数在继承的成员 buf 初始化之前将其名为 buf 的 gzstreambuf 成员的地址传递给std::istream对象的构造函数。

根据 [lib.istream.cons] 的说法,您的问题的原因可能是gzstreambase(consth char *, int)调用std::ios::init(),并且std::istream构造函数的行为就像它做同样的事情一样。记录了函数std::ios::init以初始化状况良好的流。因此,如果在 gzstreambase 对象之后初始化 istream 子对象,则 ios base 对象的第二个 init 确实应该清除错误标志。这实际上看起来像是gzstream库中的一个错误,确实。获得正确的构造顺序(首先是gzstreambuf,其次是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++似乎是对的。你确实需要继承 std::istream 才能提供"istream 接口",而另一方面,你不需要继承 std::istream,因为它的构造函数会做你不想要的事情。如果 std::istream "只是一个接口",你可以毫无问题地实现它,同时从 gzstreambase 派生实现。

在这种情况下,我看到的唯一解决方案是删除执行打开的gzstreambase构造函数,并在igzstream和ogzstream构造函数中放置打开调用(从而复制对打开的调用)。这样,可以依赖 init 被调用一次,并且在 istream/ostream 构造函数中只调用一次。

在不更改库的情况下,一种可能的解决方法是使用默认构造函数并稍后打开文件。

例:

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

最新更新