我找到了一段有UB的代码,并被告知要将其保留在代码中,并注明其为UB。仅使用MSVC2012。
代码本身有一个Foo
个对象的原始数组,然后使用char*
将该数组转换为reinterpret_cast<char*>
,然后调用delete casted_array
(就像这样,不删除[] )就此而言。
像这样:
Foo* foos = new Foo[500];
char* CastedFoos = reinterpret_cast<char*>(foos);
delete CastedFoos;
根据标准5.3.5 / 3,这显然是未定义的行为。
显然,这段代码的功能是避免将析构函数作为优化调用。
我想知道,实际上是否存在将代码中的UB视为有效的地方?
另外,就我而言,在代码中留下上述内容并不聪明,我是对的吗?
答案 0 :(得分:7)
这完全取决于你的观点。
举一个极端的例子:在C ++ 03中,线程是未定义的行为。只要有多个线程,程序的行为就不再由C ++标准定义。
然而,大多数人会说线程有用。
当然,根据C ++标准,多线程可能是UB,但是个别编译器并没有将视为未定义。它们提供了额外的保证,即多线程将按照您的预期工作。
在抽象地谈论C ++时,UB没有任何用处。怎么可能呢?你不知道会发生什么或者会发生什么。
但是在特定应用程序中,特定编译器编译的特定代码在特定操作系统上运行时,您有时可能知道一块UB是(1)安全的,(2)最终会产生某种有益效果。 / p>
答案 1 :(得分:6)
C ++标准定义了“未定义的行为”,如下所示:
此标准没有要求的行为
因此,如果您希望代码可以移植到不同的编译器和平台,那么您的代码不应该依赖于未定义的行为,因为在这些情况下,程序(由编译代码的不同编译器生成的程序)可能会有所不同。
如果您不关心可移植性,那么您应该检查您的编译器是否记录了它在感兴趣的情况下的行为。如果它没有记录它的作用(并且不必记录),请注意编译器可以在不同版本之间没有警告的情况下改变它的作用。还要注意其行为可能是不确定的。因此,例如它可能会在1%的时间内崩溃,您可能在临时测试中没有注意到,但是在它投入生产时会回来并咬你。因此,即使您使用的是一个编译器,依赖于未定义的行为仍然是一个坏主意。
关于您的具体示例,您可以重写它以实现相同的效果(不调用析构函数,但回收内存),不会导致未定义的行为。分配一个std::aligned_storage
来保存Foo数组,调用 placement new 来构造aligned_storage
上的Foo数组,然后当你想要释放数组时,释放{{1没有调用展示位置删除。
当然这仍然是一个糟糕的设计,可能会导致内存泄漏或其他问题,具体取决于aligned_storage
应该做什么,但至少它不是UB。