我试着理解一个对象在堆栈上销毁时会发生什么。 这是我的示例代码:
#include <stdio.h>
struct B {
~B() {puts("BBBB");}
};
int main()
{
B b;
b.~B();
}
输出
BBBB
BBBB
根据输出,我可以告诉对象是两次破坏。一个是~B(),另一个是在“}”之后。如何以及为什么一个物体会被摧毁两次?
更新: 在我回顾之后,我认为析构函数不会破坏这个对象。它有一种方法可以在它超出范围“}”之前销毁它。 感谢
答案 0 :(得分:4)
你不应该手动调用析构函数。发生的事情是你正在调用析构函数,然后当对象从堆栈中弹出时,编译器会自动再次调用析构函数。
答案 1 :(得分:4)
~B()
在>强制破坏之前被称为
析构函数通常用于释放内存并进行其他清理 对象被销毁时的类对象及其类成员。 当该对象消失时,将为类对象调用析构函数 范围或明确删除。 Source
所以你只是两次调用函数。
答案 2 :(得分:3)
C ++中存在最小到无垃圾收集,当对象超出范围时,它们就被简单地销毁了。所以你可以将测试替换为:
#include <iostream>
struct B {
B() { std::cout << "B()" << std::endl; }
~B() { std::cout << "~B()" << std::endl; }
};
int main()
{
std::cout << "start main" << std::endl;
{ // scope
std::cout << "start scope" << std::endl;
B b;
std::cout << "end scope" << std::endl;
} // <-- b gets destroyed here.
std::cout << "end main" << std::endl;
}
如果您希望堆栈上的对象可以控制其生命周期,您可以执行以下操作:
#include <iostream>
#include <memory.h>
struct B {
B() { std::cout << "B()" << std::endl; }
~B() { std::cout << "~B()" << std::endl; }
};
int main()
{
std::cout << "start main" << std::endl;
{ // scope
std::cout << "start scope" << std::endl;
void* stackStorage = alloca(sizeof(B));
std::cout << "alloca'd" << std::endl;
// use "in-place new" to construct an instance of B
// at the address pointed to by stackStorage.
B* b = new (stackStorage) B();
std::cout << "ctord" << std::endl;
b->~B(); // <-- we're responsible for dtoring this object.
std::cout << "end scope" << std::endl;
} // <-- b gets destroyed here, but it's just a pointer.
std::cout << "end main" << std::endl;
}
但请记住,这是堆栈。当它超出范围时,它会消失 - 如果你不破坏它,它就会消失。
{
void* stackStorage = alloca(sizeof(B));
B* b = new (stackStorage) B(); // "in-place new"
} // (*b)s memory is released but (*b) was not dtord.
答案 3 :(得分:2)
只有当你有理由使用placement new
时,你才能手动调用析构函数答案 4 :(得分:1)
请记住析构函数与任何其他函数一样。与其他函数的唯一区别在于,在取消分配对象时会自动调用它。你可以看到这个像一个事件。在对象被消灭之前,你有机会清理所有东西。手动调用析构函数不会释放对象。
答案 5 :(得分:0)
你正在做的是变量超出范围的一个很好的例子。
int main()
{
//Main is started
B b;
/* What happens is that `b` is constructed as a local
variable and put on the runtime stack.*/
b.~B();
/*You then invoke the destructor manually which is bad practice.
But it is possible and it will execute the code.
However, the state of the resulting instance is undefined.*/
}
/*Main ends and local variables defined in the
current scope get destroyed. Which also means that B's destructor
is called the second time */
仅供参考 - 你应该手动销毁某个对象的唯一一次是将它放在堆上这样的:
// create an instance of B on the heap
B* b = new B();
// remove instance and implicitly call b's destructor.
delete b;
答案 6 :(得分:0)
C ++中的对象构造/销毁遵循这个简单的规则:
自动分配(和构建)的任何内容都会被自动销毁。
通过new
显式破坏了使用delete
明确分配的任何内容。
使用new()
明确构造的任何内容都必须通过调用析构函数来明确地破坏。
必须在所有三种情况下调用析构函数,不同之处在于如何分配对象的内存:
对象在堆栈上,其分配由编译器管理。
对象在堆上,其分配由程序员管理。
对象在任何地方,构造和销毁与分配无关。
在前两种情况下,我们将分配和构造相结合,从而将破坏和释放相结合。第三种情况完全不同,但语言完全支持,以及允许您明确地调用析构函数的原因;因为在这种情况下,构造一个对象而不为它分配内存。因此,它必须是可破坏的,而不会释放内存。这种情况可以这样使用:
void* buffer = (void*)new char[sizeof(Foo)]; //allocation
Foo* myFoo = new(buffer) Foo(); //construction
myFoo->~Foo(); //destruction
Foo* anotherFoo = new(buffer) Foo(); //reuse the buffer to construct another object in it
anotherFoo->~Foo(); //destruction of the second object
delete buffer; //deallocation
注意,这实际上是在同一个地方一个接一个地构造两个对象,在重用内存之前明确地破坏它们。缓冲区也可以是一大堆内存来存储许多对象,std::vector<>
就像这样工作。
所以,是的,破坏会破坏你的物体,破坏后你不能使用它。但是由于您在问题中使用了自动分配和构造的对象,编译器也会负责破坏它,从而导致双重破坏。这永远是一个错误,对象绝不能被破坏两次,但语言允许你无论如何都要这样做。
答案 7 :(得分:0)
我想回答我自己的问题。
经过大量阅读后,这是我的总结。1. destructor doesnt destroy it's object. the object stays at stack
until out of scope.
2. nothing can destroy a stack object.
3. the destructor did destroy RESOURCE inside the object.
示例代码:
struct B {
B() {happy="today is a good day"; hour = 7;}
~B() {puts("BBBB");}
std::string happy;
int hour;
};
int main()
{
B b;
std::cout << b.happy << b.hour <<std::endl;
b.~B();
std::cout << b.happy << b.hour <<std::endl;
}
输出:
today is a good day7
BBBB
7
BBBB
我们可以看到资源b.happy在b。~B()被调用后消失了。这证明了我的观点3。
你看到b.hour(int类型不是资源)仍在这里。这证明了我的观点1.