c ++销毁堆栈帧上的对象

时间:2013-09-03 19:29:16

标签: c++

我试着理解一个对象在堆栈上销毁时会发生什么。 这是我的示例代码:

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

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

输出

BBBB
BBBB

根据输出,我可以告诉对象是两次破坏。一个是~B(),另一个是在“}”之后。如何以及为什么一个物体会被摧毁两次?

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

8 个答案:

答案 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;
}

现场演示: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.

答案 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 ++中的对象构造/销毁遵循这个简单的规则:

  1. 自动分配(和构建)的任何内容都会被自动销毁。

  2. 通过new显式破坏了使用delete明确分配的任何内容。

  3. 使用new()明确构造的任何内容都必须通过调用析构函数来明确地破坏。

  4. 必须在所有三种情况下调用析构函数,不同之处在于如何分配对象的内存:

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

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

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

    4. 在前两种情况下,我们将分配和构造相结合,从而将破坏和释放相结合。第三种情况完全不同,但语言完全支持,以及允许您明确地调用析构函数的原因;因为在这种情况下,构造一个对象而不为它分配内存。因此,它必须是可破坏的,而不会释放内存。这种情况可以这样使用:

      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.