在 C++11 中使用没有托管共享指针的 shared_from_this()

Using shared_from_this() without managed shared pointer in C++11

假设我有一个 class,它是 enable_shared_from_this 的 child class。这个基 class 的文档说在调用 shared_from_this 之前应该有一个拥有这个 class 的共享指针。用 new 分配 class 并调用 shared_from_this 来管理 object 是否安全?

不,这不安全。如果对象由 shared_ptr 管理,而不是通过 new 分配(没有关联的 shared_ptr),您应该只调用 shared_from_this。例如这段代码

struct Test: std::enable_shared_from_this<Test> {
  std::shared_ptr<Test> getptr() {
    return shared_from_this();
  }
};

Test *test = new Test;
std::shared_ptr<Test> test2 = test->getptr();

将抛出 std::bad_weak_ptr(至少在使用 libstdc++ 时)。但这没关系:

std::shared_ptr<Test> test(new Test);
std::shared_ptr<Test> test2 = test->getptr();

来自标准:

§ 20.8.2.4

shared_ptr shared_from_this();

shared_ptr shared_from_this() const;

7 *Requires: enable_shared_from_this shall be an accessible base class of T. this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t.

8 Returns: A shared_ptr object r that shares ownership with p.

9 Postconditions: r.get() == this

如果您在不受 shared_ptr 管理的 class 中调用 shared_from_this(),结果将是 未定义的行为,因为您有未满足该方法记录的先决条件之一。

我从经验中知道,在 [当前版本] libc++ 中,结果是抛出异常。然而,像所有未定义的行为一样,不能依赖这一点。

The documentation of this base class says there should be a shared pointer which owns this [object] before calling shared_from_this.

好的,很酷。

Is it safe to allocate the [object] with new and call shared_from_this to manage the object?

没有。在调用 shared_from_this.

之前应该有一个拥有此 [object] 的共享指针

正如其他用户已经提到的,在不属于 shared_ptr 的实例上调用 shared_from_this 将导致未定义的行为(通常是异常,但没有保证)。

所以,为什么要多一个答案?

因为我自己做了一次同样的问题并得到了几乎相同的答案,然后我开始纠结于另一个问题,这个问题紧随其后 - 我如何保证所有实例都由shared_ptr?

为了完整起见,我添加了另一个答案,其中包含有关这方面的一些细节。
这里有一个以前没有提到的简单解决方案。

如此简单的解决方案,确实:私有构造函数工厂方法可变参数模板 s.
它遵循一个片段,在一个最小的例子中将所有这些混合在一起:

#include<memory>
#include<utility>

class C: public std::enable_shared_from_this<C> {
    C() = default;
    C(const C &) = default;
    C(C &&) = default;
    C& operator=(const C &) = default;
    C& operator=(C &&c) = default;

public:
    template<typename... Args>
    static std::shared_ptr<C> create(Args&&... args) noexcept {
        return std::shared_ptr<C>{new C{std::forward<Args>(args)...}};
    }

    std::shared_ptr<C> ptr() noexcept {
        return shared_from_this();
    }
};

int main() {
    std::shared_ptr<C> c1 = C::create();
    std::shared_ptr<C> c2 = C::create(*c1);
    std::shared_ptr<C> c3 = c2->ptr();
    // these won't work anymore...
    // C c4{};
    // std::shared_ptr<C> c5 = std::make_shared<C>();
    // std::shared_ptr<C> c6{new C{}};
    // C c7{*c1};
    // ... and so on ...
}

基本的(微不足道的?)想法是禁止显式构造新实例,但是通过使用此处称为 create 的工厂方法。
可变参数模板用于避免编写多个工厂方法,仅此而已。完美转发帮助我们以正确的方式做到这一点。

很简单,不是吗?
不管怎样,我花了一段时间才弄清楚这一点,所以我希望这能帮助未来的读者解决同样的问题。