在 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
的工厂方法。
可变参数模板用于避免编写多个工厂方法,仅此而已。完美转发帮助我们以正确的方式做到这一点。
很简单,不是吗?
不管怎样,我花了一段时间才弄清楚这一点,所以我希望这能帮助未来的读者解决同样的问题。
假设我有一个 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
的工厂方法。
可变参数模板用于避免编写多个工厂方法,仅此而已。完美转发帮助我们以正确的方式做到这一点。
很简单,不是吗?
不管怎样,我花了一段时间才弄清楚这一点,所以我希望这能帮助未来的读者解决同样的问题。