Предотвратить удаление объекта, которым владеет shared_ptr, во время выполнения функции-члена - PullRequest
0 голосов
/ 08 ноября 2018

У меня есть два класса. В иллюстративных целях я использую идею меню и пунктов меню

class Menu {
 public:
  ...
  RemoveItem(Item* item) {
    // Remove appropriate item from menu_items vector
  };

 private:
  std::vector<std::shared_ptr<Item>> menu_items;
}

class Item {
 public:
  Item(Menu* owner) : menu{owner} {}

  ~Item() { RemoveThisMenuItem() }

  void RemoveThisMenuItem() {
    for (const auto& ingredient : ingredients) {
      ingredient.SetNecessary(false);
    }
    menu.RemoveItem(this);
  }
  ...

 private:
  Menu* menu;
  std::vector<Ingredients*> ingredients;
  ...
}

По сути, у меня есть класс Item, который принадлежит классу Menu (и, возможно, где-то на него ссылаются shared_ptr с). Теперь я хочу удалить предмет. Когда Элемент удален, он должен сначала выполнить некоторые другие функции (например, пометить все ненужные ингредиенты), а затем удалить его из Menu.

То, как все реализовано сейчас, есть странный цикл, когда мы хотим удалить элемент, так как вызов Item::RemoveThisMenuItem() вызывает функцию в Menu, которая удаляет умный указатель на Item, который вызывает деструктор ~Item(), который затем вызывает Item::RemoveThisMenuItem(). Какой лучший способ обойти это?

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Я бы изменил метод void RemoveItem(Item* item) на std::shared_ptr<Item> RemoveItem(Item* item) и реализовал бы его так, чтобы он перемещал / копировал соответствующий item-shared_ptr из вектора и возвращал его; если элемент уже был удален, верните пустой общий ptr, чтобы избежать бесконечной рекурсии. Таким образом, вы предотвращаете случайное удаление RemoveItem Item-объекта (в случае, если последний из него shared_ptr был уничтожен) и делегируете тему вызывающей программе RemoveItem. Затем вызывающая сторона может решить, поддерживать ли такой shared_pointer (и, следовательно, соответствующий объект) живым или нет.

Например:

void RemoveThisMenuItem() {
   for (const auto& ingredient : ingredients) {
      ingredient.SetNecessary(false);
   }
   auto ptr = menu.RemoveItem(this);
   // at that point, "this" will have not been destroyed;
   // when the method is left, however, ptr will be destroyed and the destructor will be called; 
}
0 голосов
/ 08 ноября 2018

Сделайте еще одну копию shared_ptr, который владеет предметом, и уничтожит его, когда он выйдет из области видимости.

т.е. вместо того, чтобы делать:

menu_items[n]->RemoveThisMenuItem();

сделать:

{
  auto owner = menu_items[n];
  owner->RemoveThisMenuItem();
}

Теперь, когда RemoveThisMenuItem() удаляет элемент из вектора, это не последний объект, который владеет Item, и поэтому он не будет уничтожен, пока owner не выйдет из области видимости.

...