显式调用析构函数不会破坏我的对象,为什么?
我正在调用析构函数来释放内存,但它并没有删除我的对象。其背后的原因是什么?
我的代码是这样的:
class A
{
public:
int a;
A()
{
cout << "a" << endl;
}
};
class B :public A
{
public:
int b;
B()
{
cout << "b" << endl; a = 10; b = 20;
}
~B()
{
cout << a << b << endl;
}
};
我使用它的方式如下:
int main()
{
{
B b;
b.~B();
b.b=100; // why this step is executed?
}
int x;
cin>>x;
return 0;
}
I'm calling the destructor to deallocate memory but it is not deleting my object. What is the reason behind it?
my code is like this:
class A
{
public:
int a;
A()
{
cout << "a" << endl;
}
};
class B :public A
{
public:
int b;
B()
{
cout << "b" << endl; a = 10; b = 20;
}
~B()
{
cout << a << b << endl;
}
};
and I am using it like:
int main()
{
{
B b;
b.~B();
b.b=100; // why this step is executed?
}
int x;
cin>>x;
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
为什么?在语言级别,析构函数不会释放对象本身占用的内存。
重要的析构函数会结束对象的生命周期,但不会结束对象的存储持续时间。这意味着内存仍然被分配,它只是变成“原始”(未初始化)。
所以,从这个意义上说,它正在破坏你的对象。
同时,一个普通的析构函数根本没有任何作用。即使您显式调用它,对象的生命周期也不会结束。
在你的例子中,析构函数
B::~B
是不平凡的,这正式意味着通过调用它你结束了对象的生命周期。你可以像破坏一个本地对象一样破坏它。 但记忆依然存在。尝试将内存作为B
对象访问只会导致未定义的行为。事实上,没有办法手动释放本地对象占用的内存。本地内存始终会自动释放。
Why? At language level destructor does not deallocate memory occupied by the object itself.
A non-trivial destructor ends object's lifetime, but it doesn't end the object's storage duration. This means that memory remains allocated, it just becomes "raw" (uninitialized).
So, in that sense it is destroying your object.
Meanwhile, a trivial destructor has no effect at all. Even if you call it explicitly, the object's lifetime does not end.
In your case the destructor
B::~B
is non-trivial though, which formally means that by calling it you ended your object's lifetime. You destroyed it as much a local object can be destroyed. But the memory remains. Attempting to access that memory as aB
object simply leads to undefined behavior.In fact, there's no way to manually deallocate memory occupied by a local object. Local memory is always deallocated automatically.
您不会这样调用析构函数(嗯,您可以,但通常不会这样做)。
对于像
b
这样的自动变量,当变量超出范围时,将在某个时刻调用析构函数。您不需要显式调用析构函数。对于使用
new
在堆上分配的对象,析构函数将在您删除
之后被调用。在这种情况下,您也不会显式调用析构函数。C++03 在
12.4 析构函数
中声明:您尤其不要做您想要做的事情,因为析构函数将被调用两次,一次由您显式调用,一次当
b
超出范围时隐式调用。来自标准的同一部分:这段文本在我掌握的最新 C++11 草案(n3225,2010 年 11 月)中保持不变,并且在 2011 年 8 月批准之前不太可能发生本质变化。
You do not call a destructor like that (well, you can but it's generally not done).
For automatic variables like your
b
, the destructor will be called at some point when the variable goes out of scope. You don't ever need to call the destructor explicitly.For objects allocated on the heap with
new
, the destructor will be called after youdelete
them. In this case, you also don't call the destructor explicitly.C++03 states in
12.4 Destructors
:You especially don't do what you're trying to do since the destructor will be called twice, once explicitly by you and once implicitly when
b
goes out of scope. From that same section of the standard:This text remains unchanged in the latest draft of C++11 that I have (n3225, November 2010) and it's unlikely it would have changed in essence between that and approval in August 2011.
你所做的实际上是调用未定义的行为......仅仅因为你调用了析构函数,并不意味着内存被清零或必然“回收”并且无法访问(特别是在分配了自动变量的情况下)在堆栈上而不是堆上)。可能是这样,但这取决于实现,并且通常由于性能原因而不会这样做,这通常是首先使用 C++ 的原因。因此,理论上您可以在调用析构函数后访问对象占用的内存地址处的值...但同样,这是未定义的行为,您可能会遇到从分段错误到损坏内存的无提示错误的几乎任何情况其他地方等等
What you're doing is actually invoking undefined behavior ... just because you've called the destructor, does not mean that the memory is zeroed out or necessarily "reclaimed" and inaccessable (especially in the case of an automatic variable that was allocated on the stack and not the heap). It could be, but that is left up to the implementation, and typically that is not done due to performance reasons, which is typically the reason for using C++ in the first place. Therefore you can theoretically access the values at the memory address that the object was occupying after calling the destructor ... but again, it's undefined behavior, and you can run into pretty much anything from a segmentation fault, to a silent error that corrupts memory somewhere else, etc.
它被执行是因为你编写了表示你希望它发生的代码。编译器只是做你告诉它做的事情。
您正在做的事情可能不会像您建议的那样“释放内存”。相反,它只是调用析构函数。析构函数不会释放它们所调用的对象所占用的内存。它们释放对象分配的内存(例如通过调用成员变量的析构函数,或者在其他事情上调用
free
或delete
),但对象本身的内存被释放在其他地方,可以通过delete
语句的内部工作方式,或者通过编译器清理自动变量(这就是您的B b
声明所代表的内容)。即使作用域块的关闭也可能不会为b
释放任何内存;编译器通常会计算出整个子例程需要多少堆栈空间,并在进入时将其全部分配。B
对象占用的内存在进入内部作用域时为b
保留,退出时会自动调用析构函数。It's executed because you wrote the code that said you wanted it to happen. The compiler is simply doing what you told it to do.
What you're doing probably doesn't "deallocate memory," as you suggested it would. Instead, it just calls the destructor. Destructors don't deallocate the memory occupied by the objects they're called on. They deallocate memory allocated by the object (such as by calling destructors of member variables, or calling
free
ordelete
on other things), but the memory of the object itself is deallocated elsewhere, either by the internal workings of thedelete
statement, or by the compiler when cleaning up automatic variables (which is what yourB b
declaration represents). Even the closing of the scope block probably doesn't deallocate any memory forb
; compilers usually figure out how much stack space they'll need for an entire subroutine and allocate it all upon entry. The memory occupied by thatB
object is reserved forb
upon entry to the inner scope, and upon exit, the destructor is called automatically.您的对象已被销毁,但其内存空间仍然存在,直到它超出范围。
Your object has been destroyed but its memory space is still around until it goes out of scope.
为什么不呢?您的对象已被销毁,但其内存空间仍然存在,直到它超出范围,顺便说一下,它将再次被销毁。做你所做的事情是未定义的行为。
Why wouldn't? Your object has been destroyed but its memory space is still around until it goes out of scope, where it will be destroyed again by the way. It's undefined behavior to do what you do.
析构函数并不是为了显式调用它们而设计的。基本上它只是类的另一种(特殊)方法。
如果你想取消初始化你的对象,然后仍然能够使用它,你可以创建我们自己的方法:
否则当超出范围时 B 将被删除:
Destructors were not designed to call them explicitly. Basically it is just another (special) method of class.
If you want to Uninitialize your object and then still be able to use it you could make our own method:
Otherwise B will be deleted when out of scope: