4

我找到了一段代码,其中包含 UB,并被告知将其保留在代码中,并附有说明它是 UB 的注释。仅使用 MSVC2012。

代码本身有一个原始Foo对象数组,然后将该数组转换为char*with reinterpret_cast<char*>,然后在其上调用delete casted_array(像这样,而不是 delete[])。

像这样:

Foo* foos = new Foo[500];

char* CastedFoos = reinterpret_cast<char*>(foos);

delete CastedFoos;

根据标准 5.3.5/3,这显然是未定义的行为。

显然,这段代码的作用是避免调用析构函数作为优化。

我想知道,实际上是否存在将 UB 留在代码中的地方可以被认为是有效的?

另外,就我而言,将上述内容留在代码中并不聪明,对吗?

4

2 回答 2

7

这完全取决于你的观点。

举一个极端的例子:在 C++03 中,线程是未定义的行为。一旦您拥有多个线程,您的程序的行为就不再由 C++ 标准定义。

然而,大多数人会说线程很有用

当然,根据 C++ 标准,多线程可能是 UB,但个别编译器并未其视为未定义。他们提供了额外的保证,即多线程将按您的预期工作。

在抽象地谈论 C++ 时,UB 没有任何用处。怎么可能?你不知道会发生什么。

但是在特定的应用程序中,特定的编译器编译的特定代码运行在特定的操作系统上,你有时可能知道一块 UB 是 (1) 安全的,并且 (2) 最终会产生某种有益的效果。

于 2013-05-01T13:01:26.107 回答
6

C++ 标准对“未定义行为”的定义如下:

本标准没有要求的行为

因此,如果您希望您的代码可移植到不同的编译器和平台,那么您的代码不应依赖于未定义的行为,因为在这些情况下程序(由编译您的代码的不同编译器生成)所做的可能会有所不同。

如果您不关心可移植性,那么您应该检查您的编译器是否记录了它在感兴趣的情况下的行为方式。如果它没有记录它所做的事情(也没有必要),请注意编译器可能会更改它所做的事情而不会在不同版本之间发出警告。另请注意,它的行为可能是不确定的。因此,例如它可能会在 1% 的时间内崩溃,这在临时测试中您可能不会注意到,但稍后会在它投入生产时回来并咬住您。因此,即使您使用的是一个编译器,依赖未定义的行为可能仍然是一个坏主意。

关于您的具体示例,您可以重写它以实现相同的效果(不是调用析构函数,而是回收内存),但不会导致未定义的行为。分配astd::aligned_storage来保存Foo数组,调用placement new来构造Foo数组aligned_storage,然后当你想释放数组时,释放aligned_storage而不调用placement delete。

当然这仍然是一个糟糕的设计,可能会导致内存泄漏或其他问题,具体取决于Foo::~Foo()应该做什么,但至少它不是 UB。

于 2013-05-01T14:18:30.433 回答