Циклическая зависимость в MVC std::shared_ptr

Я пытаюсь создать эту структуру MVC для своего проекта. Я впервые использую shared_ptr и weak_ptr и получаю много проблем, кроме круговой зависимости.

Модель доступна для наблюдения. Вид — это наблюдатель.

class Observable
{
    std::set< std::shared_ptr<Observer> > observers;
public:
    Observable( void );
    void registerObserver( std::shared_ptr<Observer>  );
    void removeObserver( std::shared_ptr<Observer> & );
    void notifyObservers( void );
    virtual ~Observable( void );
};


class Observer
{
public:
    Observer(void);
    virtual void update() = 0;
    virtual ~Observer(void);
};

    class Model : public Observable 
{
public:
    Model(void);
    void internalStateChange();
    void funcForController();
    int getSomethingForView() const;
    ~Model(void);
};

class View :
public Observer
{
std::weak_ptr<Model> model;
std::shared_ptr<Controller> controller;
public:
View( const std::shared_ptr<Model> &, const std::shared_ptr<Controller> & );
void update() override;
~View(void);
};

class Controller
{
std::shared_ptr<Model> model;
std::shared_ptr<View> view;
public:
Controller( std::shared_ptr<Model> & );
void changeHappened() const;
~Controller(void);
};

Мой вывод: введите здесь описание изображения

Это показывает, что он где-то зависает, а затем примерно через 50 секунд он завершается без другого вывода. введите здесь описание изображения

Контроллер не уничтожается. :(

Как решить проблему?


person Ashish Negi    schedule 25.06.2013    source источник
comment
Рассматриваете возможность использования std::weak_ptr?   -  person Mark Garcia    schedule 25.06.2013
comment
Я не понимаю, где я должен использовать это?. Я использую его для модели в поле зрения.   -  person Ashish Negi    schedule 25.06.2013


Ответы (1)


Эта проблема также хорошо задокументирована в документации по boost, так что посмотрите. Я сам написал очень короткую заметку об использовании слабых указателей для разрыва циклов ссылок в качестве приложения к моей магистерской диссертации (найдите ее здесь). Обязательно проверяйте ссылки, так как тема не может быть полностью раскрыта в две строки.

Стандартная библиотека предоставляет программисту два разных класса (на самом деле есть и другие умные указатели), а именно shared_ptr и weak_ptr. Общий указатель — это интеллектуальный указатель, содержащий указатель на объект и дополнительный указатель на общий счетчик ссылок. Каждый раз, когда создается копия интеллектуального указателя, счетчик ссылок автоматически увеличивается на 1. Когда общий указатель уничтожается (или используется для ссылки на другой объект), счетчик ссылок для этого объекта (или счетчик для его предыдущего объект) уменьшается. Общие указатели, созданные из необработанных указателей, изначально имеют счетчик ссылок, равный 1. Когда счетчик ссылок достигает 0, указанный объект автоматически уничтожается. Слабые указатели используются для разрыва циклов в структуре ссылок. Их можно использовать для получения общего указателя на тот же объект и проверки того, не был ли объект уже уничтожен. Они не включают подсчет ссылок. Слабые указатели не предназначены для прямого разыменования (т. е. их нельзя использовать для получения доступа к объекту, на который ссылаются, поскольку это может привести к сбоям или случайному поведению; на самом деле они могут быть висячими указателями).

Что касается вашей конкретной проблемы, то дело в следующем: у вас есть класс

class Controller
{
public:
std::shared_ptr<View> view;
};

и еще класс:

class View :
public Observer
{
public:
std::shared_ptr<Controller> controller;
};

и вы «назначаете» друг другу, т.е.

std::shared_ptr<Controller> c(new Controller);
std::shared_ptr<View> v(new View);
c->view = v;
v->controller = c;

когда c и v выходят за рамки, диструкторы не вызываются, так как есть еще одна ссылка для c (v->controller) и одна для v (c->view).

Решение в двух словах состоит в том, чтобы заменить один из двух shared_ptr на weak_ptr. Конечно, вы не просто выбираете один из двух наугад, какой из них заменить, зависит от вашего дизайна, и вы вполне можете в конечном итоге заменить их обоих (все зависит от семантики владения). Однако если заменить (хотя бы) один из них, проблема решится.

То же самое относится ко всем эталонным циклам, которые есть в вашем дизайне.

person Stefano Falasca    schedule 02.07.2013