【问题标题】:std::shared_ptr of thisstd::shared_ptr 这个
【发布时间】:2012-07-27 12:04:41
【问题描述】:

我目前正在尝试学习如何使用智能指针。然而,在做一些实验时,我发现了以下情况,我找不到令人满意的解决方案:

假设您有一个 A 类对象是 B 类对象(子对象)的父对象,但两者应该彼此认识:

class A;
class B;

class A
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children->push_back(child);

        // How to do pass the pointer correctly?
        // child->setParent(this);  // wrong
        //                  ^^^^
    }

private:        
    std::list<std::shared_ptr<B>> children;
};

class B
{
public:
    setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    };

private:
    std::shared_ptr<A> parent;
};

问题是 A 类的对象如何将自己的 std::shared_ptr (this) 传递给它的子对象?

有 Boost 共享指针 (Getting a boost::shared_ptr for this) 的解决方案,但是如何使用 std:: 智能指针来处理这个问题?

【问题讨论】:

  • 与任何其他工具一样,您必须在适当的时候使用它。对你正在做的事情使用智能指针是不是
  • 类似提升。见here
  • 这是那个抽象级别的问题。你甚至都不知道“this”指向堆上的内存。
  • 嗯,语言不会,但会。只要您跟踪位置,就可以了。

标签: c++ this shared-ptr this-pointer


【解决方案1】:

std::enable_shared_from_this 就是为了这个目的。你继承它,你可以从类内部调用.shared_from_this()。此外,您在此处创建可能导致资源泄漏的循环依赖项。这可以通过使用std::weak_ptr 来解决。所以你的代码可能看起来像这样(假设孩子依赖父母的存在而不是相反):

class A;
class B;

class A
    : public std::enable_shared_from_this<A>
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children.push_back(child);

        // like this
        child->setParent(shared_from_this());  // ok
        //               ^^^^^^^^^^^^^^^^^^
    }

private:     
    // note weak_ptr   
    std::list<std::weak_ptr<B>> children;
    //             ^^^^^^^^
};

class B
{
public:
    void setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    }

private:
    std::shared_ptr<A> parent;
};

但是请注意,调用.shared_from_this() 要求this 在调用点归std::shared_ptr 所有。这意味着您不能再在堆栈上创建这样的对象,并且一般不能从构造函数或析构函数中调用.shared_from_this()

【讨论】:

  • 感谢您的解释并指出我的循环依赖问题。
  • @Deduplicator 这是一个,原谅我的双关语,相当无意义的共享指针。该构造函数旨在与指向托管对象或其基的成员的指针一起使用。无论如何,你的意思是什么(对不起)?这些非拥有的shared_ptrs 与这个问题无关。 shared_from_this 的先决条件明确指出该对象必须在调用时由某个 shared_ptr 拥有(而不仅仅是指向)。
  • @kazarey 在调用点需要shared_ptr 的所有权,但在典型的使用模式中,例如shared_ptr&lt;Foo&gt; p(new Foo());shared_ptr 仅在对象拥有完全建成。可以通过在用this 初始化的构造函数中创建shared_ptr 并将其存储在非本地的某个地方(例如在引用参数中)来规避这个问题,这样它就不会在构造函数完成时死亡。但这种复杂的场景不太可能是必要的。
  • 鉴于Parent通常应该拥有Children的所有权,在父类中声明一个weak_ptr的children,而children拥有parent的所有权(shared_ptr)是不是很混乱?那么现在谁拥有孩子?
  • 当对子节点的引用只是弱指针并且只直接持有根节点时,是什么将树保持在一起?
【解决方案2】:

您在设计中遇到了几个问题,这似乎源于您对智能指针的误解。

智能指针用于声明所有权。你通过声明父母都拥有所有孩子,而且每个孩子都拥有它的父母来打破这一点。两者都不是真的。

另外,您在getChild() 中返回了一个弱指针。通过这样做,您声明调用者不应该关心所有权。现在这可能是非常有限的,但通过这样做,您必须确保在仍然持有任何弱指针时所讨论的孩子不会被破坏,如果您使用智能指针,它会自行排序.

最后一件事。通常,当您接受新实体时,您通常应该接受原始指针。智能指针对于在父母之间交换孩子可以有自己的意义,但对于一般用途,您应该接受原始指针。

【讨论】:

  • 你好,我知道这是旧的,对不起。你说孩子不应该拥有他们的父母(而父母拥有孩子),因为显然这是一种循环依赖。但是如果子项需要知道它的父项怎么办?它应该存储一个指向父级的弱指针吗?原始指针?还是这个想法有问题?我问的原因是 Qt QAbstractItemModel 要求孩子知道它是父母孩子的哪个索引。它以 child->parent->children.indexOf(child) 类型调用告终,可能是糟糕的设计,但就是这样!
  • @Dan 取决于当地的风格指南。我通常会使用原始指针来表示非拥有引用。我们也可能在某个时候得到一些标准:en.cppreference.com/w/cpp/experimental/observer_ptr
  • 非常感谢!这就是我最终选择的(原始指针)。很高兴了解observer_ptr - 现代 c++ 的发展方向给我留下了深刻的印象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-25
  • 2019-10-20
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多