假设我有一个按值返回STL容器的函数,比如std :: list
std::list<Foo> get_Foolist()
{
std::list<Foo> lst;
//populate lst
return lst;
}
或
class SomeClass
{
std::list<Foo> get_Foolist()
{
return m_foolst;
}
...
private:
std::list<Foo> m_foolst;
};
现在,我有一段代码,它使用此函数获取列表并按以下方式迭代它,
std::list<Foo>::iterator i = get_Foolist().begin();
//use i like ... cout << *i << endl;
当我看到这段代码时,我觉得它应该不起作用,因为我们在一个临时对象上使用迭代器,它会在执行表达式后被删除。但令我惊讶的是它正在发挥作用。
这适用于STLPort5.2和Microsoft Visual Studio 2008。
后来,当我们删除STLPort并开始使用编译器提供的STL实现时,我们开始面临崩溃。
据透露,上面的代码不适用于VS 2008列表实现,但它可以与STLPort一起使用。
我尝试在其他各种编译器上运行它,结果如下,
(GCC和Clang来自http://melpon.org/wandbox/)
现在我的问题是,
答案 0 :(得分:5)
- 哪种实施方法是正确的(按照标准)?
醇>
大概都是他们所有人。
- 为什么它在VS以外的所有实现上都成功?
醇>
未定义的行为 ,因为您正在取消引用无效的迭代器。所以它可以以无数种方式失败。或者似乎工作,这只是另一种失败模式。
答案 1 :(得分:3)
在C ++中有许多哲学设计规则(其中一些规则与另一个相反),但经常应用的是“信任程序员”。
此规则允许实现者简单地忽略程序员方面的错误:当您编写执行错误的代码(如删除对象两次,或者迭代不再存在的容器)时,编译器可以自由地只是忽略会发生什么而不是引发运行时错误。这就是所谓的“未定义行为”。
理由是你永远不会那样做,并且在运行时甚至只需要一纳秒来检查这些情况就不值得浪费。
如果您因为操作系统阻止在您分配的虚拟地址空间之外的访问而立即崩溃,您可以认为自己非常幸运。
如果你得到一个显示疯狂结果的随机值,你仍然可以认为自己很幸运...不幸的是,有时会发生的是你得到一个“合理”的结果,这会长时间隐藏错误,因为错误的代码会做“正确的事情”,因此程序员将继续实施其他功能,并在错误的代码之上添加更多代码。
换句话说,“未定义的行为”意味着任何事情都可能发生,包括什么都没有。
通常情况下,代码只有在损坏程度很高时才会失败(即当代码处于生产状态并且您对愤怒的客户感到匆忙时)。