我正在写一个班级 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 的实例被销毁时。
处理这种情况的首选方法是什么?为什么?
答案 0 :(得分:1)
如果您知道Foo
会比Bar
更长久使用基于参考的解决方案,因为它简单,并且因为它表达了您想要实现的目标 - 该Bar引用了Foo。如果你使用指针,那么意图就不那么清楚了。
答案 1 :(得分:1)
我认为处理这种情况的首选方法取决于具体情况。如您所述,Bar1
为用户提供了更长的Foo
生命周期的自由,并且更加危险。它也更有效(略),但可能不足以成为一个问题。
如果你知道一个事实(和/或可以证明),Foo
将永远比所有Bar
个对象更长(也许你分配你在堆栈中使用的Foo
main
),使用Bar1
没有问题。如果您不确定,Bar2
将是您要走的路。虽然Bar2
的语义可能有误(也许您不希望Bar
让Foo
保持活着。
这引出了第三种选择:weak_ptr
。这将使用户可以控制Foo
的生命周期,但仍然允许Bar
在Foo
被销毁时定义行为。
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_;
};