Have a look at following code:
#include <iostream>
#include <memory>
class A
{
public:
A()
{
std::cout << "A() \n";
}
~A()
{
std::cout << "~A() \n";
}
int a = 100;
};
class B
{
public:
B()
{
ptr.reset(new A());
std::cout << "B() \n";
std::cout << "pointer value" << ptr.get() << std::endl;
std::cout << ptr->a << std::endl;
}
~B()
{
std::cout << "~B() \n";
}
void print()
{
std::cout << "print() " << a << b << "\n";
std::cout << "pointer value" << ptr.get() << std::endl;
std::cout << ptr->a << std::endl;
}
private:
std::unique_ptr<A> ptr;
int a = 10;
int b = 5;
};
int main()
{
std::unique_ptr<B> p1(new B());
p1->~B();
p1->print();
return 0;
}
the output result:
A()
B()
pointer value010ECB60
100
~B()
~A()
print() 105
pointer value010ECB60
-572662307
~B()
~A()
The Question is:
a = 100
has been destroyed, but class B's member a = 10; b = 5
still exist, and it's value not changed, so class B's destructor can't be seen as a function, since it called class A's destructor, how to explain this?HEAP[Destructor.exe]: Invalid address specified to RtlValidateHeap( 00EA0000, 00EACE38 )
, because class B's destructor called first time so object A has been destroyed, this will destroy it twice?I add some print info to track workflow, but i still don't figure out how it worked when called the destructor explicitly.
To really understand what is going on here, you need to understand there are really two related processes that happen when recycling an object's memory. There's destruction (which is calling the class destructor to clean up any other objects the object refers to), and there's deallocation (which makes the memory available for reuse). Normally for objects on the heap, one calls delete
which does both of these things. For std::unique_ptr
, its destructor call its deleter, which by default calls delete
on the object the unique_ptr points at.
When you call a destructor explicitly (with p1->~B()
in your case), it destroys the object but does not deallocate it. That leaves the object in a "broken" state where you can't do anything with it (not even call delete
safely), but where it hasn't been deallocated either, so all you can do is kill any references to it (with .release()
) and let the memory leak. So you should never call the destructor explicitly.
If you try to access an object after it has been destroyed, you get undefined behavior, but if you try it before the object has been deallocated (as you are doing in your example), you'll probably just see the same data, as the memory has not yet been overwritten by anything else.