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:
- When class B's destructor called, class A's destructor also called, and class A's member
a = 100
has been destroyed, but class B's membera = 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? - When exit the main() function, the destructor of class B called second time automatically, and code breaks with the error code
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.
CodePudding user response:
When class B's destructor called, class A's destructor also called, and class A's member 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?
No, they do not exist, both B
and A
objects have been destroyed, it is just that dereferencing a dangling pointer (p1.get()
during p1->print()
) is undefined behaviour. In this case, the compiler just did not bother with clearing the memory locations used for the storage of the object B
.
When exit the main() function, the destructor of class B called second time automatically, and code breaks with the error code 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?
Well, you destroyed the object held by p1
but in a way p1
was not aware it has been destroyed. Then when ~unique_ptr
has been called on p1
, it tried to destroy already destroyed object. You should use p1.reset()
which will call ~B
and update p1
's state accordingly to prevent the mentioned issue.
CodePudding user response:
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.