防止在执行成员函数时删除shared_ptr拥有的对象

时间:2018-11-08 18:16:35

标签: c++ c++11 shared-ptr

我有两节课。出于说明目的,我使用菜单和菜单项的想法

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引用了)。现在,我要删除一个项目。删除Item后,它需要首先执行其他一些功能(例如将所有成分标记为不必要),然后将其从Menu中删除。

现在实现方式,我们要删除项目时会有一个奇怪的循环,因为调用Item::RemoveThisMenuItem()会调用Menu中的函数,该函数会删除指向Item的智能指针,它调用析构函数~Item(),然后调用析构函数Item::RemoveThisMenuItem()。解决此问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

再复制拥有该物品的shared_ptr,并在其超出范围时将其销毁。

即而不是:

menu_items[n]->RemoveThisMenuItem();

这样做:

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

现在,当RemoveThisMenuItem()从向量中删除元素时,它不是共享Item所有权的最后一个对象,因此直到owner消失后它才被销毁范围。

答案 1 :(得分:0)

我将方法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; 
}