Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shared_ptrs being deleted twice

I want to store shared pointers to the Object class in a vector:

Test code:

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

using namespace std;   // only for brevity

class Object
{
public:
  int n;
  Object(int n) : n(n) { cout << "Object("  << n <<")\n"; }
  ~Object() { cout << "~Object(" << n << "))\n"; n = 0xdddddddd; }

};

void Test()
{
  std::shared_ptr<Object> p1(make_shared<Object>(Object(123)));
  std::vector<shared_ptr<Object>> v;

  cout << "before push_back\n";
  v.push_back(std::make_shared<Object>(Object(2)));
  v.push_back(p1);
  cout << "after push_back\n";

  cout << "Vector content:\n";
  for (auto& p : v)
    cout << "  " << p->n << "\n"; ;
}

int main()
{
  Test();
  cout << "after Test()\n";
}

The output is

Object(123)
~Object(123))        <<< why is the destructor called here?
before push_back
Object(2)
~Object(2))          <<< why is the destructor called here?
after push_back
Vector content:
  2
  123
~Object(2))          <<< destructor called again 
~Object(123))
after Test()

I don't understand why the destructors are called twice.

OTOH the vector content is what I want.

like image 787
Jabberwocky Avatar asked Feb 20 '20 14:02

Jabberwocky


People also ask

Does shared_ptr delete?

the last remaining shared_ptr owning the object is destroyed. the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().

What happens when shared_ptr goes out of scope?

All the instances point to the same object, and share access to one "control block" that increments and decrements the reference count whenever a new shared_ptr is added, goes out of scope, or is reset. When the reference count reaches zero, the control block deletes the memory resource and itself.

Should I use unique_ptr or shared_ptr?

Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.

Can shared_ptr be copied?

The ownership of an object can only be shared with another shared_ptr by copy constructing or copy assigning its value to another shared_ptr . Constructing a new shared_ptr using the raw underlying pointer owned by another shared_ptr leads to undefined behavior.


1 Answers

I don't understand why the destructors are called twice.

Because of creating unnecessary temporary objects here:

std::shared_ptr<Object> p1(make_shared<Object>(Object(123)));
                                               ^^^
                                               temporary object

and here:

v.push_back(std::make_shared<Object>(Object(2)));
                                     ^^^
                                     temporary object

It should instead be

std::shared_ptr<Object> p1(make_shared<Object>(123));

and

v.push_back(std::make_shared<Object>(2));

Why?

Because std::make_shared constructs an object of type T and wraps it in a std::shared_ptr using args as the parameter list for the constructor of T. And in your code, you are making one extra object which immediately gets destroyed, thus calling the destructor.

Why don't you see Object(int n); constructor being called for temporary object?

Object(int n); constructor is indeed being called for the temporary object, but since the object held by the std::shared_ptr is created through copy constructor (so, by copying the temporary object) you won't see call to Object(int n); for it but call to Object(Object const& other);.

In the demo, you can see first Object(int n); constructor being called for the temporary object and then the call to copy constructor Object(Object const& other); for the actual object being referenced by the std::shared_ptr.

like image 77
NutCracker Avatar answered Oct 21 '22 19:10

NutCracker