打破 shared_ptr 和 unique_ptr 之间的循环依赖

Breaking a circular dependency between a shared_ptr and a unique_ptr

鉴于此代码:

#include <iostream>
#include <memory>

class Controller;

class View {
public:
    ~View() {
        std::cout << "Disposing View" << std::endl;
    }

    void SetObserver(std::shared_ptr<Controller> a_observer) {
        observer = a_observer;
    }
private:
    std::shared_ptr<Controller> observer;
};

class Controller : public std::enable_shared_from_this<Controller> {
public:
    static std::shared_ptr<Controller> Create(std::unique_ptr<View> view) {
        //Can't use std::make_shared due to visibility rules :(
        auto controller = std::shared_ptr<Controller>(new Controller(std::move(view)));

        controller->Init();

        return controller;
    }

    ~Controller() {
        std::cout << "Disposing Controller" << std::endl;
    }
private:
    std::unique_ptr<View> view;

    explicit Controller(std::unique_ptr<View> a_view) : view(std::move(a_view)) {}

    Controller(const Controller&) = delete;

    void Init() {
        view->SetObserver(shared_from_this());
    }
};

int main() {
    auto view = std::make_unique<View>();

    auto controller = Controller::Create(std::move(view));

    return 0;
}

我认为 controller 对象永远不会被释放(由 running it 确认)。

为了缓解这个问题,将 observer 变量设为 weak_ptr 而不是 shared_ptr 就足够了吗?

除此之外,考虑到上述设计,还有其他我应该注意的潜在问题吗?

是的,正如您所说 std::weak_ptr:

In addition, std::weak_ptr is used to break circular references of std::shared_ptr.

将成员更改为 std::weak_ptr 和 运行,得到

$ ./a.out 
Disposing Controller
Disposing View

当你需要它的时候,只要调用lock(检查return值),得到一个std::shared_ptr(你应该而不是 会员店):

void doSomethingWithView() {
    auto obs = observer.lock();                                                                                                                                                                          
    if(obs) {
        // Still valid
    }   
}   

一个可能的警告与 std::weak_ptr and multithreading 有关。

使用 std::weak_ptr 没问题,但考虑到 View 的生命周期与其 Controller 相关联,您可以只存储指向 [=13] 的常规指针=].这样,Controller 不必存储在 std::shared_ptr 中,您可以摆脱 std::enable_shared_from_this 两阶段初始化混乱。

出于安全原因,我也会将 SetObserver 设为私有,并将 Controller 设为 View 的好友。毕竟他们已经紧密耦合了。

#include <memory>

class Controller;

class View {
  friend class Controller;

private:
    void SetObserver(Controller* a_observer) {
        observer = a_observer;
    }

private:
    Controller* observer = nullptr;
};

class Controller {
public:
    explicit Controller(std::unique_ptr<View> a_view) :
        view(std::move(a_view)) {
        view->SetObserver(this);
    }

private:
    std::unique_ptr<View> view;
};

int main() {
    Controller controller(std::make_unique<View>());
}

附带说明一下,当它成为标准的一部分时,您可以使用 std::observer_ptr 来表明您的意图。