shared_ptrs удаляется дважды - PullRequest
7 голосов
/ 20 февраля 2020

Я хочу сохранить общие указатели на класс Object в векторе:

Тестовый код:

#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";
}

Вывод

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 не понимаю, почему деструкторы вызываются дважды.

ОТО, векторный контент - это то, что я хочу.

Ответы [ 2 ]

14 голосов
/ 20 февраля 2020

Я не понимаю, почему деструкторы вызываются дважды.

Из-за создания ненужных временных объектов здесь:

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

и здесь:

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

Вместо этого должно быть

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

и

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

Почему?

Потому что std::make_shared создает объект типа T и помещает его в std :: shared_ptr, используя args в качестве списка параметров для конструктора T. И в вашем коде вы создаете один дополнительный объект, который немедленно уничтожается, вызывая деструктор .

Почему вы не видите, как конструктор Object(int n); вызывается для временного объекта?

Object(int n); конструктор действительно вызывается для временного объекта, но так как объект, удерживаемый std::shared_ptr, создается с помощью конструктора копирования (поэтому, копируя временный объект), вы не увидите вызов Object(int n); для него, а вызов Object(Object const& other);.

В demo , вы можете увидеть первый Object(int n); конструктор, вызываемый для временного объект, а затем вызов для копирования конструктора Object(Object const& other); для фактического объекта, на который ссылается std::shared_ptr.

5 голосов
/ 20 февраля 2020

Это потому, что вам нужно уничтожить временные значения.

Функция std::make_shared принимает любое количество параметров и создает из него значение данного типа.

Вы создаете Object и передайте его std::make_shared, который, в свою очередь, создаст значение, используя new. Затем временные уничтожаются. Позже, общие указатели также будут уничтожены.

Просто измените это в своем коде:

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

// ...  

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

И вы увидите только один деструктор для каждого значения.

...