3

我试图了解当对象在堆栈上销毁时会发生什么。这是我的示例代码:

#include <stdio.h>
struct B {
  ~B() {puts("BBBB");}
};

int main()
{
    B b;
    b.~B();
}

输出是

BBBB
BBBB

根据输出,我可以判断对象被破坏了两次。一个是〜B(),另一个是在“}”之后。一个对象如何以及为什么会被破坏两次?

更新:在我查看回复后,我认为析构函数不会破坏这个对象。它有一种方法可以在对象超出范围“}”之前将其销毁。谢谢

4

8 回答 8

4

您不应该手动调用析构函数。发生的事情是您正在调用析构函数,然后当对象从堆栈中弹出时,编译器会再次自动调用析构函数。

于 2013-09-03T19:31:58.353 回答
4

~B()在销毁之前调用

析构函数通常用于在对象被销毁时为类对象及其类成员释放内存并进行其他清理。当该对象超出范围或被显式删除时,将为类对象调用析构函数。 资源

所以你只是调用了一个函数两次。

于 2013-09-03T19:32:40.737 回答
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;
}

现场演示:http: //ideone.com/ziNjkd

但请记住,它是堆栈。当它超出范围时,它就会消失——如果你不摧毁它,它就会消失。

{
    void* stackStorage = alloca(sizeof(B));
    B* b = new (stackStorage) B(); // "in-place new"
} // (*b)s memory is released but (*b) was not dtord.
于 2013-09-03T21:07:31.093 回答
2

唯一一次手动调用析构函数是当你有理由使用placement new

于 2013-09-04T01:35:55.477 回答
1

请记住,析构函数与任何其他函数一样。与其他函数的唯一区别是它在对象被释放时自动调用。你不能把它看作一个事件。你有机会在物体被歼灭之前清理一切。手动调用析构函数不会释放对象。

于 2013-09-03T19:52:44.320 回答
1

C++ 中的对象构造/销毁遵循这个简单的规则:

  1. 任何自动分配(和构造)的东西都会自动销毁。

  2. 任何明确分配的东西都会new通过delete.

  3. 任何显式构造的东西new()都必须通过调用析构函数来显式破坏。

在所有三种情况下都必须调用析构函数,区别在于对象的内存是如何分配的:

  1. 对象在堆栈上,它的分配由编译器管理。

  2. 对象在堆上,它的分配由程序员管理。

  3. 对象在任何地方,构造和销毁与分配无关。

在前两种情况下,我们结合了分配和构造,因此结合了销毁和释放。第三种情况完全不同,但语言完全支持,以及允许显式调用析构函数的原因;因为在这种情况下,一个对象是在没有为其分配内存的情况下构造的。因此,它必须在不释放内存的情况下也是可破坏的。这种情况可以这样使用:

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<>就像这样。

所以,是的,破坏确实会破坏你的对象,你不能在破坏后使用它。但是由于您在问题中使用了自动分配和构造的对象,因此编译器还负责破坏它,从而导致双重破坏。这始终是一个错误,对象绝不能被破坏两次,但是语言允许您无论如何都可以这样做。

于 2013-09-03T23:59:27.227 回答
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.~B() 之后资源 b.happy 消失了。这是我第 3 点的证明。

你看 b.hour(int type is not a resource) 还在这里。这是我第 1 点的证明。

于 2013-09-04T02:25:19.670 回答
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;
于 2013-09-03T19:52:41.897 回答