c++ - 可观察的行为和未定义的行为——如果我不调用析构函数会发生什么?

标签 c++ destructor language-lawyer undefined-behavior

注意:我见过类似的问题,但没有一个答案足够准确,所以我自己问这个。

这是一个非常挑剔的“语言律师”问题;我正在寻找一个权威的答案。

C++ 标准说:

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

我只是不明白“取决于副作用”是什么意思

一般问题是:

忘记调用析构函数与忘记调用具有相同主体的普通函数有什么不同吗?

一个具体的例子来说明我的观点是:

考虑下面这样的程序。还要考虑明显的变化(例如,如果我在另一个对象之上构造一个对象但我仍然忘记调用析构函数,如果我怎么办?打印输出以观察它等):

#include <math.h>
#include <stdio.h>

struct MakeRandom
{
    int *p;
    MakeRandom(int *p) : p(p) { *p = rand(); }
    ~MakeRandom() { *p ^= rand(); }
};

int main()
{
    srand((unsigned) time(NULL));        // Set a random seed... not so important
    // In C++11 we could use std::random_xyz instead, that's not the point

    int x = 0;
    MakeRandom *r = new MakeRandom(&x);  // Oops, forgot to call the destructor
    new (r) MakeRandom(&x);              // Heck, I'll make another object on top
    r->~MakeRandom();                    // I'll remember to destroy this one!
    printf("%d", x);                     // ... so is this undefined behavior!?!
    // If it's indeed UB: now what if I didn't print anything?
}

说这表现出“未定义的行为”对我来说似乎很荒谬,因为 x 已经是随机的——因此异或另一个随机数并不能真正使程序比以前更“未定义”,可以吗?

此外,在什么时候说程序“依赖”析构函数是正确的?如果值是随机的,或者一般来说,如果我无法区分析构函数与运行与不运行,它会这样做吗?如果我从不读取值怎么办?基本上:

在哪些条件下(如果有),该程序会出现未定义行为?

究竟是哪些表达式或语句导致了这种情况,为什么?

最佳答案

I simply do not understand what "depends on the side effects" means.

这意味着它取决于析构函数正在做什么。在您的示例中,修改 *p 或不修改它。您的代码中有这种依赖关系,因为如果不调用 dctor,输出会有所不同。

在您当前的代码中,打印的数字可能与第二次 rand() 调用返回的数字不同。你的程序调用了未定义的行为,只是这里的 UB 没有不良影响。

如果您不打印该值(或以其他方式读取它),那么就不会对 dcor 的副作用产生任何依赖性,因此也就没有 UB。

所以:

Is forgetting to call a destructor any different than forgetting to call an ordinary function with the same body?

不,在这方面没有任何不同。如果你依赖它被调用,你必须确保它被调用,否则你的依赖是不满足的。

Furthermore, at what point is it correct to say the program "depends" on the destructor? Does it do so if the value was random -- or in general, if there is no way for me to distinguish the destructor from running vs. not running?

随机与否无关紧要,因为代码取决于要写入的变量。仅仅因为很难预测新值是什么并不意味着没有依赖关系。

What if I never read the value?

那么就没有UB了,因为代码写入后对变量没有依赖。

Under which condition(s), if any, does this program exhibit Undefined Behavior?

没有条件。它总是 UB。

Exactly which expression(s) or statement(s) cause this, and why?

表达式:

printf("%d", x);

因为它引入了对受影响变量的依赖。

关于c++ - 可观察的行为和未定义的行为——如果我不调用析构函数会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21110059/

相关文章:

c++ - Eclipse C++ 包括来 self 的源文件夹的头文件

c++ - std::unique_ptr 的自定义删除器是手动调用析构函数的有效位置吗?

c++ - 创建一个将 ostream 作为参数并写入该流的打印函数

c++ - WTL vista/7 原生外观中的 CreateSimpleReBar

c++ - 为什么析构函数被执行了两次?

c++ - 在 C++ 中相互使用的两种类型

c++ - 静态类成员是否保证在调用 `main` 之前被初始化?

c++ - 在其声明语句中修改变量是否定义明确?

c++ - Curiously Recurring Template Pattern 的实现是特定的吗?

c++ - 使用迭代器作为参数重载 << 运算符