Home > OS >  Is object can be destroyed when called the destructor explicitly?
Is object can be destroyed when called the destructor explicitly?

Time:12-31

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:

  1. 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?
  2. 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.

  • Related