Ctor Initializer:自初始化导致崩溃?
我很难调试生产中的崩溃。只是想与这里的人们确认语义。我们有一个像这样的类...
class Test {
public:
Test()
{
// members initialized ...
m_str = m_str;
}
~Test() {}
private:
// other members ...
std::string m_str;
};
有人更改了初始化以使用 ctor 初始化列表,这在我们的代码语义中是相当正确的。除其他事项外,初始化的顺序及其初始值是正确的。所以类看起来像......
class Test {
public:
Test()
: /*other inits ,,, */ m_str(m_str)
{
}
~Test() {}
private:
// other members ...
std::string m_str;
};
但是代码突然开始崩溃!我将长长的 init 列表与这段代码 m_str(m_str)
隔离开来。我通过链接文本确认了这一点。
难道一定要崩溃吗?标准对此有何规定? (这是未定义的行为吗?)
I had a hard time debugging a crash on production. Just wanted to confirm with folks here about the semantics. We have a class like ...
class Test {
public:
Test()
{
// members initialized ...
m_str = m_str;
}
~Test() {}
private:
// other members ...
std::string m_str;
};
Someone changed the initialization to use ctor initialization-lists which is reasonably correct within our code semantics. The order of initialization and their initial value is correct among other things. So the class looks like ...
class Test {
public:
Test()
: /*other inits ,,, */ m_str(m_str)
{
}
~Test() {}
private:
// other members ...
std::string m_str;
};
But the code suddenly started crashing! I isolated the long list of inits to this piece of code m_str(m_str)
. I confirmed this via link text.
Does it have to crash? What does the standard say about this? (Is it undefined behavior?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(5)
原来通过赋值的“初始化”是完全多余的。
除了浪费处理器周期之外,它没有造成任何损害,因为默认情况下,在赋值时 m_str 成员已经被初始化。
在第二个代码片段中,默认初始化被重写,以使用尚未初始化的成员来初始化自身。这是未定义的行为。这是完全没有必要的:只需删除它(并且不要重新引入原来的浪费时间的内容,只需删除即可)。
通过调高编译器的警告级别,您可能会收到有关此代码和类似的简单不良代码的警告。
不幸的是,您遇到的问题不是技术问题,而是更为根本的问题。这就像汽车工厂的工人对他们在新汽车品牌上安装的方形车轮提出了疑问。那么问题不在于方形轮子不起作用,而是很多工程师和经理都参与了使用外观精美的方形轮子的决定,并且没有人反对 - 其中一些人无疑没有反对。我不知道方轮不起作用,但我怀疑,他们中的大多数人只是害怕说出他们百分百确定的事情。所以这很可能是管理问题。抱歉,但我不知道解决办法...
未定义的行为不一定会导致崩溃——它几乎可以做任何事情,从继续工作,就好像根本没有问题一样,到立即崩溃,到做一些非常奇怪的事情,但后来会导致看似不相关的问题。规范的说法是它会让“恶魔从你的鼻子里飞出来”(又名“导致鼻恶魔”)。曾经,该相位的发明者有一个(非常酷的)网站,讲述了由某人在“DeathStation 9000”中引起未定义行为而引发的核战争。
编辑:标准中的确切措辞是(§:1.3.12):
1.3.12未定义行为[defns.undefined]
行为,例如可能在使用错误的程序构造或错误的数据时出现的行为,对此
国际标准没有提出任何要求。当出现这种情况时,也可能会出现未定义的行为
国际标准省略了任何明确的行为定义的描述。 [注:允许未定义
行为范围从完全无视结果不可预测的情况,到在
以环境特征的记录方式进行翻译或程序执行(有或没有
发出诊断消息),终止翻译或执行(发出
诊断消息)。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
第一个构造函数相当于
,当您在构造函数中进行赋值时,
m_str
已经被隐式初始化为一个空字符串。因此,对 self 的赋值虽然完全没有意义且多余,但不会导致任何问题(因为std::string::operator=()
,正如任何编写良好的赋值运算符应该检查的 self 赋值并且不执行任何操作在这种情况下)。但是,在第二个构造函数中,您尝试在初始化程序列表中使用其自身来初始化
m_str
- 此时它尚未初始化。所以结果是未定义的行为。更新:对于原始类型,这仍然是未定义的行为(导致字段具有垃圾值),但它不会崩溃(通常 - 请参阅下面的注释以了解异常),因为根据定义,原始类型没有构造函数、析构函数不包含指向其他对象的指针。
对于任何不包含具有所有权语义的指针成员的类型也是如此。
std::string
特此证明不是其中之一:-)The first constructor is equivalent to
that is, by the time you get to the assignment within the constructor,
m_str
has already been implicitly initialized to an empty string. So the assignment to self, although completely meaningless and superfluous, causes no problems (sincestd::string::operator=()
, as any well written assignment operator should, checks for self assignment and does nothing in this case).However, in the second constructor, you are trying to initialize
m_str
with itself in the initializer list - at which point it is not yet initialized. So the result is undefined behaviour.Update: For primitive types, this is still undefined behaviour (resulting in a field with garbage value), but it does not crash (usually - see the comments below for exceptions) because primitive types by definition have no constructors, destructors and contain no pointers to other objects.
Same is true for any type that does not contain pointer members with ownership semantics.
std::string
is hereby demonstrated to be not one of these :-)