参考或shared_ptr为协会成员

Reference or shared_ptr as member for association

本文关键字:成员 ptr shared 参考      更新时间:2023-10-16

我正在写一个类Bar 需要访问另一个类 Foo 才有用。因此,Foo的实例必须比使用它的Bar实例活得更久。

我无法在两种写法之间做出决定。下面是一个示例:

#include <iostream>
#include <memory> 
using namespace std; 
struct Foo {
    Foo(int _x) : x_(_x) {}
    ~Foo() {}
    int x_; 
}; 
struct Bar1 {  
    Bar1(Foo& _foo) : foo_(_foo) {}
    void print_foo() {cout << foo_.x_ << endl;}
private: 
    Foo& foo_; 
};
struct Bar2 {  
    Bar2(shared_ptr<Foo> _foo) : foo_{move(_foo)} {}
    void print_foo() {cout << foo_->x_ << std::endl;}
private:
    shared_ptr<Foo> foo_;
};
int main() 
{
    Foo f1{1}; 
    shared_ptr<Foo> f2 = make_shared<Foo>(2);
    Bar1 b1(f1); 
    b1.print_foo();
    Bar2 b2(f2); 
    b2.print_foo();
    return 0; 
}

我认为,Bar1 让用户在如何管理 Foo 的生命周期方面有了更大的自由度。而且它可能更有效率。但它进入一个未定义的(不确定是否这是正确的词)状态,当foo_所指的 Foo 实例被销毁时。

处理这种情况的首选方法是什么,为什么?

我认为处理这种情况的首选方法取决于情况的具体情况。 正如您所确定的,Bar1Foo的生命周期内为用户提供了更多的自由,并且更加危险。 它也更有效(稍微),但可能不足以成为一个问题。

如果您知道一个事实(和/或可以证明)Foo将始终比所有Bar对象寿命长(也许您在main中分配了在堆栈上使用的Foo),那么使用 Bar1 没有问题。 如果您不确定,Bar2将是要走的路。 尽管Bar2的语义可能是错误的(也许你不希望Bar让你的Foo保持活力)。

这导致我们进入第三种选择: weak_ptr . 这将让用户控制Foo的生命周期,但仍然允许BarFoo被销毁时具有定义的行为。

struct Bar3 {
    Bar3(std::weak_ptr<Foo> _foo) : foo_(_foo) {}
    void print_foo_v1() {
        // throws if foo_'s object has been destroyed
        std::shared_ptr<Foo> foo(foo_);
        std::cout << foo->x_ << std::endl;
    }
    void print_foo_v2() {
        // returns nullptr if foo_'s object has been destroyed
        std::shared_ptr<Foo> foo(foo_.lock());
        if (foo) {
            std::cout << foo->x_ << std::endl;
        }
    }
private:
    std::weak_ptr<Foo> foo_;
};
如果您

知道Foo会比使用基于引用的解决方案Bar因为它简单且因为它表达了您想要实现的目标 - 那么 Bar 引用了 Foo。如果您使用指针,则意图并不那么明确。