显式调用析构函数是否会导致此处的未定义行为?
在我看来,以下代码(来自一些 C++ 问题)应该会导致 UB,但看起来并非如此。这是代码:
#include <iostream>
using namespace std;
class some{ public: ~some() { cout<<"some's destructor"<<endl; } };
int main() { some s; s.~some(); }
答案是:
some's destructor
some's destructor
我从 c++ faq lite 中了解到,我们不应该显式调用析构函数。我认为在显式调用析构函数之后,应该删除对象 s 。程序完成后自动再次调用析构函数,应该是UB。但是,我在 g++ 上尝试了一下,得到了与上面答案相同的结果。
是因为类太简单(不涉及new/delete)吗?或者在这种情况下根本不是UB?
In my opinion, the following code (from some C++ question) should lead to UB, but the it seems it is not. Here is the code:
#include <iostream>
using namespace std;
class some{ public: ~some() { cout<<"some's destructor"<<endl; } };
int main() { some s; s.~some(); }
and the answer is:
some's destructor
some's destructor
I learned form c++ faq lite that we should not explicitly call destructor. I think after the explicitly call to the destructor, the object s should be deleted. The program automatically calls the destructor again when it's finished, it should be UB. However, I tried it on g++, and get the same result as the above answer.
Is it because the class is too simple (no new/delete involved)? Or it's not UB at all in this case?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
该行为是未定义的,因为对同一对象调用析构函数两次:
对生命周期已结束的对象调用析构函数会导致每个 C++ 的未定义行为03§12.4/6:
根据 §3.8/1 调用其析构函数时,对象的生命周期结束:
请注意,这意味着如果您的类有一个简单的析构函数,则该行为是明确定义的,因为这种类型的对象的生命周期在其存储空间被释放之前不会结束,对于自动变量而言,直到函数结束时才会发生这种情况。当然,我不知道为什么你会显式调用析构函数,如果它是微不足道的。
什么是平凡的析构函数? §12.4/3 说:
正如其他人提到的,未定义行为的一个可能结果是您的程序似乎继续正确运行;另一个可能的结果是你的程序崩溃。任何事情都有可能发生,而且没有任何保证。
The behavior is undefined because the destructor is invoked twice for the same object:
Invoking the destructor on an object whose lifetime has ended results in undefined behavior per C++03 §12.4/6:
An object's lifetime ends when its destructor is called per §3.8/1:
Note that this means if your class has a trivial destructor, the behavior is well-defined because the lifetime of an object of such a type does not end until its storage is released, which for automatic variables does not happen until the end of the function. Of course, I don't know why you would explicitly invoke the destructor if it is trivial.
What is a trivial destructor? §12.4/3 says:
As others have mentioned, one possible result of undefined behavior is your program appearing to continue running correctly; another possible result is your program crashing. Anything can happen and there are no guarantees whatsoever.
这是未定义的行为——但与任何 UB 一样,一种可能性是它(或多或少)似乎有效,至少对于工作的某些定义而言。
本质上,您需要(或想要)显式调用析构函数的唯一时间是与放置 new 结合使用(即,使用放置 new 在指定位置创建对象,并使用显式 dtor 调用来销毁该对象)。
It's undefined behavior -- but as with any UB, one possibility is that it (more or less) appears to work, at least for some definition of work.
Essentially the only time you need (or want) to explicitly invoke a destructor is in conjunction with placement new (i.e., you use placement new to create an object at a specified location, and an explicit dtor invocation to destroy that object).
来自 http://www.devx.com/tips/Tip/12684
在您的情况下,它不会崩溃,因为析构函数不操作任何字段;实际上,您的类根本没有任何数据成员。如果确实如此,并且在析构函数体内以任何方式操纵它,则在第二次调用析构函数时可能会出现运行时异常。
From http://www.devx.com/tips/Tip/12684
In your case it doesn't crash because the destructor doesn't manipulate any field; actually, your class doesn't have any data members at all. If it did and in destructor's body you manipulated it in any way, you would likely get a run-time exception while calling destructor for the second time.
这里的问题是删除/释放和析构函数是单独且独立的构造。很像 new / 分配和构造函数。可以仅执行上述操作之一而不执行其他操作。
在一般情况下,这种情况确实缺乏用处,只会导致与堆栈分配值的混淆。在我的脑海中,我想不出一个好的场景,你会想要这样做(尽管我确信可能有一个)。然而,可以想象这种行为是合法的人为场景。
注意:请不要以这种方式编写类代码。而是使用显式的 Release 方法。
The problem here is that deletion / deallocation and destructors are separate and independent constructs. Much like new / allocation and constructors. It is possible to do only one of the above without the other.
In the general case this scenario does lack usefulness and just lead to confusion with stack allocated values. Off the top of my head I can't think of a good scenario where you would want to do this (although I'm sure there is potentially one). However it is possible to think of contrived scenarios where this would be legal.
Note: Please don't ever code a class this way. Have an explicit Release method instead.
它很可能工作正常,因为析构函数不引用任何类成员变量。如果您尝试在析构函数中
删除
变量,那么当它第二次被自动调用时,您可能会遇到麻烦。话又说回来,对于未定义的行为,谁知道呢? :)
It most likely works fine because the destructor does not reference any class member variables. If you tried to
delete
a variable within the destructor you would probably run into trouble when it is automatically called the second time.Then again, with undefined behavior, who knows? :)
main 函数的作用是在堆栈上保留空间,调用 some 的构造函数,最后调用 some 的析构函数。无论您在函数中放入什么代码,这种情况总是会发生在局部变量上。
您的编译器不会检测到您手动调用了析构函数。
无论如何,你不应该手动调用对象的析构函数,除了使用placement-new创建的对象。
What the main function does is reserving space on the stack, calling some's constructor, and at the end calling some's destructor. This always happens with a local variable, whatever code you put inside the function.
Your compiler won't detect that you manually called the destructor.
Anyway you should never manually call an object's destructor, except for objects created with placement-new.
我相信,如果您希望代码正常,您只需调用placement new 并在退出之前将其填回即可。对析构函数的调用不是问题,这是离开作用域时对析构函数的第二次调用。
I believe that if you want your code to be OK you simply need to call placement new and fill it back in before exiting. The call to the destructor isn't the issue, it's the second call to the destructor made when you leave scope.
您能定义您期望的未定义行为吗?未定义并不意味着随机(或灾难性):给定程序的行为在调用之间可能是可重复的,它只是意味着您不能依赖任何特定行为,因为它是未定义的并且无法保证会发生什么。
Can you define the undefined behaviour you expect? Undefined doesn't mean random (or catastrophic): the behaviour of a given program may be repeatable between invocations, it just means you can't RELY on any particular behaviour because it is undefined and there is no guarantee of what will happen.
这是未定义的行为。未定义的行为是双重析构函数调用,而不是析构函数调用本身。如果将示例修改为:
其中 [在此插入任何代码] 可以替换为任意代码。结果具有不可预测的副作用,这就是它被认为是未定义的原因。
It is undefined behaviour. The undefined behaviour is the double destructor call and not with the destructor call itself. If you modify your example to:
where [INSERT ANY CODE HERE] can be replaced with any arbitrary code. The results have unpredictable side effects, which is why it is considered undefined.