可观察的行为和未定义的行为 - 如果我不调用析构函数会发生什么? [英] Observable behavior and undefined behavior -- What happens if I don't call a destructor?

查看:104
本文介绍了可观察的行为和未定义的行为 - 如果我不调用析构函数会发生什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:我已经看过类似的问题,但没有一个答案足够精确,所以我自己问。



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



C ++标准说:


程序可以通过重用对象占用的存储器或通过显式调用具有非平凡析构函数的类类型的对象的析构函数来结束任何对象的生命周期。对于具有非平凡析构函数的类类型的对象,程序不需要在对象占用的存储被重新使用或释放​​之前明确地调用析构函数;然而,如果没有对析构函数的显式调用,或者如果不使用delete-expression来释放存储,则析构函数不应被隐式地调用,并且任何程序取决于由



一般问题是:



是否忘记调用析构函数而不是忘记调用同一主体的普通函数?



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



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

  #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)); //设置随机种子...不是那么重要
//在C ++ 11中,我们可以使用std :: random_xyz,这不是点

int x = 0;
MakeRandom * r = new MakeRandom(& x); //哎,忘了调用析构函数
new(r)MakeRandom(& x); // Heck,我将另一个对象放在顶部
r->〜MakeRandom(); //我会记得摧毁这个!
printf(%d,x); // ...这是未定义的行为!
//如果确实是UB:现在如果我没有打印什么?
}

对我来说这个展览未定义的行为似乎是可笑的, code> x 已经是随机的了,因此XORing它另一个随机数不能真正使程序比以前更未定义,可以吗?



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



这个程式在哪些条件下显示未定义行为?



哪个表达式或语句导致这种情况,为什么?

解决方案


不明白取决于副作用是什么意思。


这意味着它取决于析构函数所做的事情。在您的示例中,修改 * p 或不修改它。



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



如果你不打印值(或以其他方式阅读)对于dcor的副作用没有任何依赖性,因此没有UB。



所以:


是否忘记调用析构函数而不是忘记调用同一主体的普通函数?


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


它是正确的说程序依赖的析构函数?如果值是随机的,或者一般来说,如果我没有办法区分析构函数与运行或不运行,它是这样做的吗?


随机或不重要,因为代码取决于要写入的变量。因为很难预测新值是什么,并不意味着没有依赖性。


如果我从来没有读过值? / p>

然后没有UB,因为代码在写入之后对变量没有依赖。


此程式在哪些条件下(如果有的话)显示未定义行为?


没有条件。它总是UB。


究竟哪个表达式或语句导致这种情况,以及为什么?


表达式:

  printf(%d X); 

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


Note: I've seen similar questions, but none of the answers are precise enough, so I'm asking this myself.

This is a very nitpicky "language-lawyer" question; I'm looking for an authoritative answer.

The C++ standard says:

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.

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

The general question is:

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

A specific example to illustrate my point is:

Consider a program like this below. Also consider the obvious variations (e.g. what if I don't construct an object on top of another one but I still forget to call the destructor, what if I don't print the output to observe it, etc.):

#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?
}

It seems ridiculous to me to say this exhibits "undefined behavior", because x is already random -- and therefore XORing it another random number cannot really make the program more "undefined" than before, can it?

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? Basically:

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

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

解决方案

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

It means that it depends on something the destructor is doing. In your example, modifying *p or not modifying it. You have that dependency in your code, as the output would differ if the dctor wouldn't get called.

In your current code, the number that is printed, might not be the same number that would have returned by the second rand() call. Your program invokes undefined behavior, but it's just that UB here has no ill effect.

If you wouldn't print the value (or otherwise read it), then there wouldn't be any dependency on the side effects of the dcor, and thus no UB.

So:

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

Nope, it's not any different in this regard. If you depend on it being called, you must make sure it's called, otherwise your dependency is not satisfied.

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?

Random or not doesn't matter, because the code depends on the variable being written to. Just because it's difficult to predict what the new value is doesn't mean there's no dependency.

What if I never read the value?

Then there's no UB, as the code has no dependency on the variable after it was written to.

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

There are no conditions. It's always UB.

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

The expression:

printf("%d", x);

because it introduces the dependency on the affected variable.

这篇关于可观察的行为和未定义的行为 - 如果我不调用析构函数会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆