引用或shared_ptr作为关联成员

时间:2014-02-15 17:15:08

标签: c++

我正在写一个班级 Bar 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 的实例被销毁时。

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

2 个答案:

答案 0 :(得分:1)

如果您知道Foo会比Bar更长久使用基于参考的解决方案,因为它简单,并且因为它表达了您想要实现的目标 - 该Bar引用了Foo。如果你使用指针,那么意图就不那么清楚了。

答案 1 :(得分:1)

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

如果你知道一个事实(和/或可以证明),Foo将永远比所有Bar个对象更长(也许你分配你在堆栈中使用的Foo main),使用Bar1没有问题。如果您不确定,Bar2将是您要走的路。虽然Bar2的语义可能有误(也许您不希望BarFoo保持活着。

这引出了第三种选择: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_;
};