2016-11-07 12 views
3

Предположим, у меня есть коллекция Foo объектов, и каждый Foo владеет одним или несколькими объектами Bar. Специфические Foo или Bar могут быть удалены пользователем моего интерфейса; когда удаляется Foo, все удаленные Bar. Пока что каждый Foo набор unique_ptr<Bar> - это все, что мне нужно для автоматического управления этой моделью. Однако у меня есть дополнительное требование: после того, как Foo останется без Bar, его также следует удалить.Как элегантно использовать интеллектуальные указатели для моделирования сложных сроков жизни в C++?

Конечно, я мог бы просто написать код, который явно позаботится об этом, но мне интересно, есть ли более идиоматический способ достичь того же. Это звучит ужасно много, как shared_ptr, но не совсем ...

+1

Кто владеет 'коллекции Foo'? Вероятно, это должно произойти удаление Foos. – drRobertz

+0

Вы должны выполнить это условие по коду, это настолько специфично, что я сомневаюсь, что вы нашли бы «элегантный» идиоматический способ сделать это. Просто спроектируйте звуковую систему – Dredok

+0

@Yaron Tausky: пожалуйста, посмотрите мой пост и отметьте его как точный ответ на свой вопрос! – Roland

ответ

1

Поскольку удаление всех Bar сек из Foo следует удалить Foo, это может действительно звук, как вам нужно несколько shared_ptr с от Bar с их Foo ,

Однако, эта модель поставит срок службы Foo в руках его Bar с: Вы не могли бы непосредственно удалить Foo, вместо этого вы должны были бы выслеживать все его Bar с и удалить эти.

Foo будет держать Bar* сек вместо unique_ptr<Bar> с, так как он не может умереть, прежде чем его Bar с. Но тогда вы должны дать право собственности на Bar с до кого- ...

Вы могли бы закончить с еще одним объектом, который содержит коллекцию unique_ptr<Bar> s, соответствующие каждому из Foo с, но тогда вы должны держите все это в синхронизации, так как Bar s приходят и уходят. Это то же самое бухгалтерия, которого вы пытаетесь избежать, но намного больше, сложнее и хрупче в результате, с утечками памяти и указателями изгоев в качестве случаев сбоя.


Таким образом, вместо всего этого, я предлагаю вам придерживаться первого unique_ptr Приведено идеи. Первый переход на реализацию может выглядеть так:

struct Foo { 
private: 
    friend void remove(std::unique_ptr<Foo> &foo, Bar const *bar); 

    // Removes the bar from this Foo. 
    // Returns true iff the Foo is now empty and should be deleted. 
    bool remove(Bar const *bar) { 
     auto i = std::find_if(begin(_bars), end(_bars), [&](auto const &p) { 
      return p.get() == bar; 
     }); 

     assert(i != end(_bars)); 
     _bars.erase(i); 

     return _bars.empty(); 
    } 

    std::vector<std::unique_ptr<Bar>> _bars; 
}; 

// Removes the bar from the Foo. 
// Deletes the Foo if it becomes empty. 
void remove(std::unique_ptr<Foo> &foo, Bar const *bar) { 
    if(foo->remove(bar)) 
     foo.reset(); 
} 
1

Нет идиоматического решения. Используйте shared_ptr и unique_ptr внутри агрегата и weak_ptr как привязку к агрегату. Вот мой развернутый пример вашей проблемы:

#include <memory> 
#include <iostream> 
#include <vector> 
using std::cout; 
using std::endl; 
using std::vector; 
using std::shared_ptr; 
using std::unique_ptr; 
using std::weak_ptr; 
using std::make_shared; 
using std::make_unique; 

struct Foo; 
struct Bar 
{ int b; 
    shared_ptr<Foo> f; 
    Bar(int b, shared_ptr<Foo> f): b(b), f(f) { cout << "Constructor B with: " <<b << std::endl; } 
    ~Bar() { cout << "Destructor B with: " <<b << endl; } 
}; 
struct Foo 
{ 
    Foo() { cout << "Constructor Foo" << endl; } 
    ~Foo() { cout << "Destructor Foo" << endl; } 
    void clear() { vb.clear(); } 
    vector<unique_ptr<Bar>> vb; 
}; 


int main(int argc, char* argv[]) 
{ weak_ptr<Foo> wf; // Anchor to Aggregate 
    { // Construction of Aggregate 
     vector<shared_ptr<Bar>> vb2; 
     shared_ptr<Foo> f =std::make_shared<Foo>(); 
     f->vb.emplace_back(make_unique<Bar>(1, f)); 
     f->vb.emplace_back(make_unique<Bar>(2, f)); 
     wf =f; 
    } 
    shared_ptr<Foo> f3 =wf.lock(); 
    if (f3) 
    { 
     if (argv[1][0] =='f') 
     { 
      cout <<"Destroy Foo" <<endl; 
      f3->clear(); 
     } 
     if (argv[1][0] =='b') 
     { 
      cout <<"Destroy Bar" <<endl; 
      f3->vb[0].reset(); 
      f3->vb[1].reset(); 
     } 
    } 
} 

Вызов программы с аргументом «F» или «B» и выход будет:

Constructor Foo 
Constructor B with: 1 
Constructor B with: 2 
Destroy ??? 
Destructor B with: 1 
Destructor B with: 2 
Destructor Foo 

 Смежные вопросы

  • Нет связанных вопросов^_^