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

Designing 2 classes having references to each other using smart pointers

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

有两个相互了解的 classes 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" 成为一个唯一的指针,从而得到 class Foo 看起来像这样:

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

但现在我正在为 class 酒吧而苦苦挣扎。我的想法是让成员 "m_foo" 成为一个共享指针,结果是:

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;
};

但这会抛出一个 "bad weak pointer" 异常,因为(据我所知)你只能在对象拥有后共享 "this"-指针 (shared_from_this())已创建。

问题:我希望在对象创建期间共享 "this",因为程序必须正确地 运行,如果您在对象创建后通过函数调用执行此操作,它可能会被遗忘。

感谢您的帮助。

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

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

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


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

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

要考虑的另一件事是 Bar 有一个 Foo&。这个决定将取决于您是否希望能够 move a Foo 同时保留 Bar 的原始实例(即不移动 Bar)。参考版本不支持该操作,但指针版本可以与 Foo 的移动构造函数一起修改其 Bar 的后向指针。意思是 Bar 需要成为 Foo 的好友,因为后向指针是私有的。

您遇到的问题是由与对象 Foo 和 Bar 的关系引起的。

您将进行递归析构函数调用。

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

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

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

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

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

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

使用 weak_ptr 需要您也使用 shared_ptr,因为 weak_ptr 不能从 unique_ptr 创建,但即使只有一个 class 对所讨论的对象持有 shared_ptr,这仍然符合它们打算如何使用的精神。

关于M.M.关于所有权语义的观点,shared 或 unique ptr 表示所有权,而 weak_ptr 表示引用,所以这仍然可以。

关于此架构的另外两个技巧:

A:您可以在 Factory, having their relations defined through setters, rather than each class handling the creation of its children (Dependency Injection). Ideally you could follow Alexandrescu's tips on the Factory design.

中创建 classes

或者 B:您可以使用 named constructor idiom,使用 public static create(...) 方法进行实际构建,以规避由于 class 施工期间未完成。

但我更愿意在这里推荐备选方案 A。