Вызов метода * shared_ptr * - что происходит с подсчетом ссылок? - PullRequest
0 голосов
/ 09 мая 2018

Я пытаюсь выявить неприятную ошибку, из-за которой объект самопроизвольно повреждается, находясь внутри карты, и после нескольких часов отладки, я думаю, что я, возможно, не полностью осознал идею std :: shared_ptr .

Вот контекст : Внутри метода я объявляю std :: shared_ptr и инициализирую его, чтобы он указывал на клон текущего объекта (созданный new ). Затем - после некоторых модификаций объекта - я вызываю тот же метод для этого указателя (рекурсивно).
Внутри следующей рекурсии метода принято решение вставить этот объект в std :: unordered_map (который является атрибутом класса, поэтому он доступен на всех уровнях рекурсии).
Это некоторый псевдокод для иллюстрации того, что я имею в виду:

class A
{
  ...
  void DoSomething(void); // the recursive function
  A* Clone(void) const { return new A(this); }  // the clone method
  ...
  static std::unordered_map<std::shared_ptr<A>,int> myMap{};
};

void A::DoSomething(void)
{
  ...
  if (condition) myMap.insert({this,5});  // in a deeper recursive call, condition is true
  ...
  std::shared_ptr<A> pA(Clone());   // make a copy
  pA->...  // modify it 
  pA->DoSomething();  // here is the recursive call
  ...
}

Проблема: Иногда объект за указателем внутри std :: unordered_map уничтожается, и он кажется , как это происходит, когда исходный std :: shared_ptr выходит из области видимости.

Мое (предварительное) понимание: Вызов метода объекта, на который указывает std :: shared_ptr, не увеличивает счетчик ссылок - внутри вызываемого метода у меня есть доступ к this , то есть ptr, на который указывает std :: shared_ptr, но то, что я делаю с this , не влияет на исходный std :: shared_ptr.
Чтобы проверить это, я добавил код, чтобы сделать клон extra в дополнительный std :: shared_ptr, прямо в момент вставки в карту, а затем все работает нормально (только медленнее и использует удвоенную память) , что является проблемой - класс А имеет много сложных данных).

Вопрос: Правильно ли мое понимание? Если нет, как бы я вызвал метод std :: shared_ptr , чтобы this внутри метода по-прежнему оставался 'std :: shared_ptr'? Или это невозможно, и я должен использовать другой дизайн?

Относительно дубликатов: Должны ли мы передавать shared_ptr по ссылке или по значению? , кажется, указывает на этот путь, но о передаче параметров по значению или по ссылке, которая не является у меня есть выбор с указателем .

1 Ответ

0 голосов
/ 10 мая 2018

Ваше понимание в основном верно. Эта линия - ваша проблема:

if (condition) myMap.insert({this,5});

Из-за необработанного this в этой строке создается полностью независимый shared_ptr с собственным независимым счетчиком ссылок. Позже на внешнем уровне рекурсии в конце DoSomething() исходный shared_ptr pA выходит из области видимости, его счетчик ссылок падает до 0, объект уничтожается, а второй shared_ptr на карте начинает свисать.

Раствор 1

Вы можете решить ее с помощью std :: enable_shared_from_this :

class A : public std::enable_shared_from_this<A> { ... }

// Btw: Lose the void pseudo-parameter. This is not C. ;)
void A::DoSomething()
{
    if (condition) {
        myMap.insert({shared_from_this(), 5});
    }
}

Потенциальное решение 2

Из отрывков кода, которые вы показываете, я нахожу весьма сомнительным, что вам вообще нужен shared_ptr. Ничто из того, что вы показываете, не указывает на совместное владение. Если это действительно так, переключитесь на unique_ptr с и std::move() их. Это тоже избавляет от проблемы.

...