This (相当老的)关于 iostreams 和 streambuf 的文章认为,以下代码是可以的:
class DerivedStreamBuf : public std::streambuf {
// ...
};
class DerivedOutputStream : public std::ostream {
public:
DerivedOutputStream():
std::ios(0), std::ostream(&dsb) {} //1
// ...
private:
DerivedStreamBuf dsb;
// ...
};
这可能是有问题的,因为当 ostream
构造dsb
尚未初始化,因此 UB 可能是效果。对于析构函数,它可能是相反的:dsb
已经被破坏,可以在 ostream
的析构函数中使用.然而,文章认为,“C++ 标准要求没有父类的构造函数或析构函数(ios、istream 或 ostream)访问流缓冲区”。
虽然析构函数的情况很简单,例如对于
~ostream
:virtual ~basic_ostream(); Remarks: Does not perform any operations on rdbuf().
constructor 不太清楚:
explicit basic_ostream(basic_streambuf<charT, traits>* sb); Effects: Initializes the base class subobject with basic_ios<charT, traits>::init(sb) ([basic.ios.cons]).
这是否意味着这是唯一可能的效果,这个标准公式是否不允许其他效果,这些是
std::ostream
的实现细节并且可能访问未初始化的 dsb
?我的问题:该标准是否保证上述代码是可移植的,即适用于
std::ostream
的所有符合标准的实现?
最佳答案
Does that standard guarantees, that the above code is portable, i.e. works for all standard-compliant implementations of
std::ostream
?
是的。
在调用
std::ostream(&dsb)
时dsb
DerivedOutputStream
的成员已分配但未初始化。意思是,我们可以通过我们可能还没有读取它的(未初始化的)值来获取它的地址。如问题中所链接,[ostream.cons]指定
std::basic_ostream
的构造函数,特别是在您的示例中选择的是 [ostream.cons]/1explicit basic_ostream(basic_streambuf<charT, traits>* sb);
Effects: Initializes the base class subobject with
basic_ios<charT, traits>::init(sb)
([basic.ios.cons]).Postconditions:
rdbuf() == sb
.
[tab:basic.ios.cons]描述调用
void init(basic_streambuf<charT, traits>* sb)
的效果,特别是上面构造函数中描述的后置条件:rdbuf() == sb
其余的不执行对
sb
的底层缓冲区的读取访问。点。现在,
rdbuf()
只是一个指针,所以这意味着 sb
按值传递给 init 的指针(即指针)将用于初始化 rdbuf()
.但是,读取 sb
的底层缓冲区没有副作用。指向,此时。[...] not allow for other effects, ...?
实际上,特别是
void init(basic_streambuf<charT, traits>*)
的实现功能需要尊重而不是扩展 [tab:basic.ios.cons] 的明确规定的效果.This (pretty old) article ...
请注意,[ostream.cons] 的状态自 C++ 时代以来基本没有变化;我们可以例如看看[lib.input.output]来自 the September 1994 C++ Working Paper
27.2.4.1.1 basic_ostream constructor [lib.basic.ostream.sb.cons]
basic_ostream(basic_streambuf<charT,baggage>* sb);
1 Constructs an object of class basic_ostream, assigning initial values to the base class by calling
basic_ios<charT,baggage>::init(sb)
.
和
27.1.3.1.34 basic_ios::init [lib.basic.ios::init]
void init(basic_streambuf<charT,baggage>* sb_arg);
1 The postconditions of this function are indicated in Table 8:
Table 8--init effects +----------------------------------+ |Element Value | +----------------------------------+ |sb sb_arg | |tiestr a null pointer | |state goodbit if sb_arg is | | not a null pointer, | | otherwise badbit. | |except goodbit | |fmtfl skipws | dec | |wide zero | |prec 6 | |fillch the space character | |loc new locale(), which | | means the default value | | is the current global | | locale;9) | |iarray a null pointer | |parray a null pointer | +----------------------------------+
通过
init()
描述相同的委托(delegate)调用具有明确的效果。
关于c++ - 标准是否要求流构造函数不访问流缓冲区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64276788/