3

I try to understand what happen when an object destroy on stack. here is my sample code:

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

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

output is

BBBB
BBBB

based on the output, I can tell the object is destroy twice.one is ~B(), another one is after "}". how and why can a object get destroy twice?

update: after i review replies, I think the destructor doesnt destroy this object. it there a way to destroy an object before it reach out of scope "}". Thanks

cppython
  • 1,209
  • 3
  • 20
  • 30
  • possible duplicate of [When is a C++ destructor called?](http://stackoverflow.com/questions/10081429/when-is-a-c-destructor-called) – Jacob Krall Sep 03 '13 at 19:31

8 Answers8

4

You are not supposed to invoke the destructor by hand. What's happening is you are invoking the destructor and then when the object gets popped off the stack the destructor is called again automatically by the compiler.

Mike Dinescu
  • 54,171
  • 16
  • 118
  • 151
4

~B() is called before the destruction

Destructors are usually used to deallocate memory and do other cleanup for a class object and its class members when the object is destroyed. A destructor is called for a class object when that object passes out of scope or is explicitly deleted. Source

So you are just calling a function twice.

Pol0nium
  • 1,346
  • 4
  • 15
  • 31
3

There's minimal-to-no garbage collection in C++, objects are simply destroyed when they go out of scope. So you could replace your test to this:

#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;
}

If you want an object on the stack over which you have control over the lifetime of, you can do something like this:

#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;
}

Live demo: http://ideone.com/ziNjkd

Remember, though, it's the stack. When it goes out of scope, it goes away - if you don't destroy it, it just dissapears.

{
    void* stackStorage = alloca(sizeof(B));
    B* b = new (stackStorage) B(); // "in-place new"
} // (*b)s memory is released but (*b) was not dtord.
kfsone
  • 23,617
  • 2
  • 42
  • 74
  • A standard way to obtain that stack space would be to use [`std::aligned_storage`](http://en.cppreference.com/w/cpp/types/aligned_storage) -- e.g. declare an object of type `std::aligned_storage::type`, and then use placement new to construct your object at its address. – Benjamin Lindley Sep 03 '13 at 21:20
2

The only time you would manually call a destructor is when you've got grounds to use placement new

namezero
  • 2,203
  • 3
  • 24
  • 37
1

Keep in mind that the destructor is like any other function. The only difference with other function is that it is automatically called when the object is deallocated. You cann see this like an event. You get a chance to clean everything before the object get annihilated. Calling the destructor by hand does not deallocate the object.

Nico
  • 6,395
  • 4
  • 25
  • 34
1

Object construction/destruction in C++ follows this simple rule:

  1. Anything automatically allocated (and constructed) is automatically destructed.

  2. Anything explicitely allocated with new is explicitely destructed via delete.

  3. Anything explicitely constructed with new() must be explicitely destructed by calling the destructor.

The destructor has to be called in all three cases, the difference is in how the memory for the object is allocated:

  1. The object is on the stack, its allocation is managed by the compiler.

  2. The object is on the heap, its allocation is managed by the programmer.

  3. The object is anywhere, construction and destruction is independent of allocation.

In the first two cases, we have a combination of allocation and construction, and consequently a combination of destruction and deallocation. The third case is entirely different, but fully supported by the language, and the reason that you are allowed to explicitely call a destructor; because in this case an object is constructed without allocating memory for it. Consequently, it must be destructible without deallocating memory as well. This case can be used like this:

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

Note, that this actually constructs two objects, one after the other in the same place, destructing them explicitely before the memory is reused. The buffer could also be a large slap of memory to store many objects, std::vector<> works like this.

So, yes, destruction does destroy your object, you must not use it after destruction. But since you used an automatically allocated and constructed object in your question, the compiler also took care of destructing it, leading to the double destruction. This is always a bug, objects must never be destructed twice, but the language allows you to do it anyway.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • thanks for your reply. if destruction does destroy my object, it will be against your rule #1 "Anything automatically allocated (and constructed) is automatically destructed." because the object is automatically allocated and it should not be destructed manually (b.~B()). right? – cppython Sep 04 '13 at 00:56
  • Yes, you must only explicitely call `b->~B()` if you have constructed it with `new(buffer) B()`. Unfortunately, the language does not stop you from doing it wrong, it is your responsibility not to let a destructed object go out of scope or call `delete` on it. – cmaster - reinstate monica Sep 04 '13 at 09:07
0

What you are doing is a pretty good example of a variable running out of scope.

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 */

FYI - the only time you are supposed to do manual destruction of an object is when it is put on the heap like this:

// create an instance of B on the heap
B* b = new B();

// remove instance and implicitly call b's destructor.
delete b;
Jesko R.
  • 827
  • 10
  • 21
0

i would like to answer my own question.

after a lot of reading, Here is my summary.

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.

sample code:

 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;

}

output:

today is a good day7
BBBB
7
BBBB

we can see a resource b.happy is gone after b.~B() is called. this is a proof of my point 3.

you see the b.hour(int type is not a resource) is still here. this is a proof of my point 1.

cppython
  • 1,209
  • 3
  • 20
  • 30
  • 1
    I'd add: 4. It is illegal to destroy an object multiple times, and so is to use an object (e.g. printing its members like here) after it has been destroyed. That's probably _undefined behavior_. Besides, it _appears_ to "work" but it causes a bad _memory corruption_: http://ideone.com/LzxW27 _(wow, ideone was just reskinned while typing this!)_ – gx_ Sep 04 '13 at 07:22