1

我正在尝试在 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 也一样。

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

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

4

2 回答 2

3

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

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

igzstream 类:公共 gzstreambase,公共 std::istream

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

igzstream 类:公共 gzstreambase,虚拟公共 std::istream

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

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

根据[lib.istream.cons] ,您的问题的原因可能是gzstreambase(consth char *, int)调用std::ios::init(),并且std::istream构造函数的行为就像它一样。功能std::ios::init已记录以在良好状态下初始化流。因此,如果 istream 子对象在 gzstreambase 对象之后初始化,则 ios 基础对象的第二个 init 确实应该清除错误标志。这实际上看起来像是 gzstream 库中的一个错误。获得正确的构造顺序(首先是 gzstreambuf,其次是 istream,然后尝试打开文件)似乎是一个完全不简单的问题。原始版本具有“gzstreambuf,open,istream”,其中 istream 破坏了打开失败,而您建议的修复具有“istream,gzstreambuf,open”,其中 istream 获取尚未构建的 streambuf 的地址。

一种解决方法是不使用 gzstream 的开放构造函数,但我会想一个好的解决方案来修复开放构造函数,并在我得到结果后立即编辑答案。

根据您的要求,多个初始化调用都可以(通常解释为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 派生实现的同时实现它,而不会出现问题。

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

于 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 回答