Dan Flick
Dan Flick

Reputation: 99

Destructor and erase for shared pointers

As far as I know destructor is called when an element is erased from container, please correct me if I'm wrong..But Destr X prints shouldn't appear before after erase

#include <iostream>
#include <memory>
#include <vector>

struct X 
{
    X()
    {
        std::cout<<"Contr X"<<std::endl;
    }
    ~X()
    {
        std::cout<<"Destr X"<<std::endl;
    }
};

using Xptr = std::shared_ptr<X>;

int main()
{
    
    std::shared_ptr<X> x = std::make_shared<X>();
    Xptr x2 = std::make_shared<X>();
    std::vector<Xptr> v;
    v.emplace_back(x);
    v.emplace_back(x2);
    v.erase(v.begin());
    std::cout<<"after erase\n";
    return 0;
}

Upvotes: 1

Views: 819

Answers (2)

Galik
Galik

Reputation: 48605

"As far as I know destructor is called when an element is erased from container..."

That is correct. But when that element is a std::shared_ptr it is the std::shared_ptr whose destructor is called, not the destructor of whatever the std::shared_ptr is pointing to.

The std::shared_ptr will only call the destructor of the object it points to if it is the last std::shared_ptr pointing to it. But in your code you have 2 std::shared_ptrs pointing to each of the objects you created. You have one each in the std::vector and one each as local std::shared_ptr variables x and x2 before they are added to the std::vector.

So when you delete the elements from the std::vector the std::vector delets the std::shared_ptrs it contains but the objects pointed to by those std::shared_ptrs are not destroyed until the std::shared_ptr variables x and x2 go out of scope at the end of main().

Upvotes: 1

Olaf Dietsche
Olaf Dietsche

Reputation: 74018

Since you use a shared_ptr, you have two references to both objects. These are x and x2, and the shared_ptrs inside vector v.

When you erase(), the destructor of shared_ptr is called, and the reference count is decremented, but still 1. Therefore object X still exists. After leaving the scope of main(), the count is decremented again. Now the count goes down to zero, and the destructor of object X is called.

Hence the output "after erase" is shown first, and only then "Destr X".


When you put x and x2 in an inner scope, you will see the expected behaviour

std::vector<Xptr> v;

{
    std::shared_ptr<X> x = std::make_shared<X>();
    Xptr x2 = std::make_shared<X>();
    v.emplace_back(x);
    v.emplace_back(x2);
}

std::cout<<"before erase\n";
v.erase(v.begin());
std::cout<<"after erase\n";

Now you see

Contr X
Contr X
before erase
Destr X
after erase
Destr X

Upvotes: 1

Related Questions