2010-10-08 456 views
2

我很难调试生产崩溃。只是想在这里与人们确认语义。我们有一类像...Ctor初始化程序:自初始化会导致崩溃?

class Test { 
public: 
    Test() 
    { 
    // members initialized ... 
    m_str = m_str; 
    } 
    ~Test() {} 
private: 
    // other members ... 
    std::string m_str; 
}; 

有人更改为使用构造函数初始化,列出这是我们的代码语义内合理正确的初始化。初始化的顺序及其初始值是正确的。所以班级看起来像...

class Test { 
public: 
    Test() 
    : /*other inits ,,, */ m_str(m_str) 
    { 
    } 
    ~Test() {} 
private: 
    // other members ... 
    std::string m_str; 
}; 

但是代码突然开始崩溃!我将这个长长的列表分隔到这段代码m_str(m_str)。我通过link text确认了这一点。

它是否有崩溃?标准对此有何评论? (它是未定义的行为?)

+4

您正在初始化m_str与自己?你为什么做这个? – Starkey 2010-10-08 15:43:11

+1

未定义的行为意味着它可以格式化您的c盘并安装另一个操作系统。没有人知道什么特定的“未定义的行为”意味着特定的环境。 – 2010-10-08 15:44:08

+0

别人的代码,我会改变它,但需要从标准确认它确实是未定义的行为,因为它不会崩溃像POD类型'int我;我(i)'在初始化列表中 – Abhay 2010-10-08 15:46:14

回答

13

第一个构造相当于

Test() 
    : m_str() 
    { 
    // members initialized ... 
    m_str = m_str; 
    } 

就是由你来分配在构造函数中的时间,m_str已经被隐式地初始化为空字符串。因此,赋予自我,尽管完全无意义和多余,不会引起任何问题(因为std::string::operator=(),正如任何写得很好的赋值运算符应该检查自赋值并且在这种情况下什么也不做)。

但是,在第二个构造函数中,您正试图初始化m_str自身在初始化程序列表中 - 此时尚未初始化。所以结果是未定义的行为。

更新:对于原始类型,这仍然是不确定的行为(导致垃圾值的字段),但它不会崩溃(通常是 - 见注释下面的例外),因为原始类型定义有没有构造函数,析构函数并且不包含指向其他对象的指针。

对于不包含拥有所有权语义的指针成员的任何类型也是如此。 std::string特此证明不是这些中的一个:-)

+2

取决于“原始类型”的含义。即使将不确定值赋值给'int'原则上也会导致崩溃,因为标准允许填充和陷阱表示形式为'int'。你必须深入到'char'类型以便具有定义明确的初始化的可移植代码。 – 2010-10-08 16:20:53

+0

@Alf即使对于'char'类型也是如此,这是未定义的行为。只有当你引用一个'char'类型的指针时,它才会被定义为任何和所有可能的模式(因为它然后从内存中读取并且因为'char'类型没有陷阱)。请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#240上的最后一个注释,*“CWG不认为访问未签名的char可能仍会陷入在一个登记册中分配,并需要重新评估所提出的决议。“*。 – 2010-10-08 19:58:57

+0

@Johannes:谢谢,我没有想到! (但是委员会也没有......)但是,我确信它是关于“ENIAC兼容性”的最后一个喘息,并且没有实践中的问题 - 至少,我真诚地希望如此! :-) – 2010-10-08 20:28:14

2

m_str构造在初始化列表中。因此,当你将它分配给它自己时,它不是完全构建的。因此,未定义的行为。

(什么是自赋值应该无论如何做?)

+0

我不知道,别人的代码。如果确实是未定义的行为,我需要确认。 Becoz它绝对不会为POD类型崩溃。 – Abhay 2010-10-08 15:44:58

+4

你为什么需要确认?这是错误的代码。只需修复它。 – Tim 2010-10-08 15:49:19

+0

@Tim:同意。但它的实时代码,每天都会达到100万次。我需要从更高的人那里得到批准,谁说'它已经工作了很长时间...'所以我需要一个有说服力的参考。 – Abhay 2010-10-08 15:53:41

1

未定义行为并不一定导致崩溃 - 它可以做任何事情,从继续工作,就好像没有问题,立即崩溃,做一些非常奇怪的事情,以后会导致看起来不相关的问题。经典的说法是它使“恶魔从你的鼻子里飞出来”(又名“导致鼻子恶魔”)。有一次,这个阶段的发明者有一个(非常酷的)网站,讲述了在“DeathStation 9000”中引发未定义行为的人开始的核战争。

编辑:从标准的确切措辞(§:1.3.12):

1.3.12未定义的行为[defns.undefined]

行为,例如可能在使用时会出现的错误的程序结构或错误的数据,对此,国际标准没有要求。当国际标准忽略任何显式行为定义的描述时,也可能会出现未定义的行为。 [注意:允许未定义 行为的范围从忽略完全具有不可预知的结果的情况,到在以文档化的方式执行特定于环境(有或没有 发布诊断消息)的翻译或程序执行期间的行为,终止翻译或执行(通过发布 诊断消息)。

0

这是

之间
std::string str; 
str = str; 

std::string str(str); 

前者的作品一样差(虽然这是废话),后者则没有,因为它试图复制结构来自尚未构造的对象的对象。

当然,要走的路将是

Test() : m_str() {} 
2

通过赋值原始的“初始化”是完全多余的。

除了浪费处理器周期之外,它没有造成任何伤害,因为在赋值时m_str成员已经默认初始化了。

在第二个代码片段中,默认初始化被覆盖,以使用尚未初始化的成员来初始化它自己。这是未定义的行为。而这完全没有必要:只是删除它(并且不要重新引入原来的时间浪费,只是删除)。

通过调高编译器的警告级别,您可能会收到有关此类警报以及类似的不良代码的警告。

不幸的是,你遇到的问题不是这个技术问题,它更重要。这就像一家汽车工厂的工人对他们投放在新车品牌上的方形车轮提出质疑。那么问题不在于方形车轮无法工作,而是很多工程师和经理都参与了决定使用看上去很方便的方形车轮,并且他们都没有反对 - 他们中的一些人毫无疑问并没有反对,我不明白方形轮不起作用,但我怀疑其中大多数人都害怕说出他们100%确定的东西。所以这可能是一个管理问题。我很抱歉,但我不知道该修复...

+0

在我的情况下也是如此。我想我可以说服他们使用C++标准作为参考。等待引用..我没有n3092.pdf或 – Abhay 2010-10-08 15:56:46

+0

的那个/ Internet的副本您可以从C++委员会页面[http://www.open-std.org]下载最新的标准草案/ JTC1/SC22/WG21 /]。 – 2010-10-08 16:22:26