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。