设计2个使用智能指针相互引用的类

时间:2017-11-05 22:21:24

标签: c++ c++11 smart-pointers

我目前正在通过用智能指针替换所有原始指针类成员来更新我的代码。我目前正在处理的方案如下:

有两个类Foo和Bar彼此了解(使用原始指针):

class Bar;

class Foo {
public:b
    Foo(){
        m_bar = new Bar(this);
    }
private:
    Bar* m_bar;
};

class Bar {
public:
    Bar(Foo* foo) {
        m_foo = foo;
    }
private:
    Foo* m_foo;
};

因为Foo是" m_bar"的创建者。并且应该拥有一个永远不会共享的独特实例,我想要成为会员" m_bar"一个唯一的指针,导致类Foo看起来像这样:

class Foo {
public:
    Foo() {
        m_bar = std::unique_ptr<Bar>(new Bar(this));
    }
private:
    std::unique_ptr<Bar> m_bar;
};

但是现在我正在和Bar一起挣扎。我的想法是成为会员&#34; m_foo&#34;共享指针,结果是:

class Bar;

class Foo : public std::enable_shared_from_this<Foo> {
public:
    Foo() {
        m_bar = std::unique_ptr<Bar>(new Bar(shared_from_this()));
    }
private:
    std::unique_ptr<Bar> m_bar;
};

class Bar {
public:
    Bar(std::shared_ptr<Foo> foo) {
        m_foo = foo;
    }
private:
    std::shared_ptr<Foo> m_foo;
};

但是这会引发一个&#34;糟糕的弱指针&#34;异常因为(据我所知)你只能在创建对象后共享&#34;这个&#34; -pointer(shared_from_this())。

问题:我想&#34;这个&#34;在对象创建期间共享,因为程序正常运行是必要的,如果在创建对象后通过函数调用执行此操作,则可能会忘记它。

感谢您的帮助。

3 个答案:

答案 0 :(得分:4)

我建议您在实际需要共享所有权语义时仅使用shared_ptr,而在需要唯一所有权语义时仅使用unique_ptr。否则,您不仅会混淆自己,还会阅读其他人。

您的文字说明表明Foo唯一拥有每个Bar,因此Foo应该有unique_ptr<Bar>

但是,Bar在任何意义上都不拥有Foo,因此Bar既不应该有unique_ptr<Foo>也不应该shared_ptr<Foo>

您可以保留Bar Foo *。没有必要使事情过于复杂;如果你在你的代码中采用一个约定,那么带有所有权语义的指针使用智能指针类,那么原始指针就没有所有权语义。

有一个提案observer_ptr<Foo>,它只是原始指针的包装器,但应该自我记录它确实没有所有权语义。

要考虑的另一件事是BarFoo&。此决定取决于您是否希望move Foo同时保留Bar的原始实例(即不移动Bar)。参考版本不支持该操作,但指针版本可以与Foo的move-constructor一起修改其Bar的后向指针。含义Bar将需要成为Foo的朋友,因为后指针是私有的。

答案 1 :(得分:1)

另一个选择是你在Foo中使用std :: shared_ptr来保存Bar引用,然后使用std::weak_ptr来保存对Bar的引用。

编辑:我相信我推荐的是最接近std智能指针的精神的那个。您应该使用unique_ptr / shared_ptr来描述(和强制执行)所有权,并且当您想要保持对不拥有它的类中的对象的引用时,应使用weak_ptr。这样你就可以完全避免使用裸指针。

使用weak_ptr要求你也使用shared_ptr,因为不能从unique_ptr创建weak_ptr,但即使只有一个类将shared_ptr保存到有问题的对象,这仍然是他们打算如何使用它们的精神

关于M.M.关于所有权语义的观点,共享或唯一的ptr表示所有权,而weak_ptr表示引用,所以这仍然没问题。

这个架构的另外两个提示:

答:您可以在Factory中创建自己的类,通过setter定义关系,而不是每个类处理其子项(Dependency Injection)的创建。理想情况下,您可以关注Alexandrescu's tips on the Factory design

或者B:您可以使用named constructor idiom,使用公共静态create(...)方法进行实际构造,以避免因构造过程中类不完整而发生的错误。

但我宁愿在这里推荐备选方案A.

答案 2 :(得分:0)

由于与对象Foo和Bar的关系而导致的问题。

你将有递归的析构函数调用。

想象一下,你有一个代码: C ++ 17

std::unique_ptr<Foo> r = std::make_unique<Foo>();
r.reset();

会发生什么?您将输入Foo析构函数,稍后将进入Bar析构函数。 Bar析构函数接下来会进入Foo等的析构函数。

我认为你必须首先重新考虑你的班级关系。