我们啥时候应该使用 std::enable_shared_from_this
Posted
技术标签:
【中文标题】我们啥时候应该使用 std::enable_shared_from_this【英文标题】:When should we use std::enable_shared_from_this我们什么时候应该使用 std::enable_shared_from_this 【发布时间】:2016-12-28 15:08:08 【问题描述】:我只知道std::enable_shared_from_this
形成this link。
但是看了下面的代码,不知道什么时候用。
try
Good not_so_good;
std::shared_ptr<Good> gp1 = not_so_good.getptr();
catch(std::bad_weak_ptr& e)
// undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17)
std::cout << e.what() << '\n';
上面的代码“不太好”,因为在调用getptr()
之前没有现有的shared_ptr
。所以好的应该是:
std::shared_ptr<Good> gp1 = std::make_shared<Good>(); // having a shared_ptr at the beginning
std::shared_ptr<Good> gp2 = gp1->getptr();
但是,如果我已经有一个shared_ptr
对象,我为什么不简单地编写如下代码:std::shared_ptr<Good> gp2 = gp1;
,这意味着我根本不需要std::enable_shared_from_this
。
在我看来,使用std::enable_shared_from_this
是为了确保多个shared_ptr
对象具有相同的控制块,这样我们就可以避免the double-delete problem。但是如果我必须在开始时提醒自己创建一个shared_ptr
,为什么我不提醒自己使用shared_ptr
对象来创建一个新对象,而不是使用原始指针呢?
【问题讨论】:
void f(Good& g); /* ... */ f(*gp1); /* ... */ void f(Good& g) /* now what? */
@milleniumbug 对不起,我不明白你想说什么。
f
无权访问shared_ptr
,因为它需要一个引用,而不是共享指针。有时您可以进行更改,以便改为使用引用,但不适用于成员函数 - this
始终是原始指针。
@milleniumbug 我想你误解了我的问题。我的问题是:使用std::enable_shared_from_this
并不能让我们免于双重删除的风险。如果是这样,std::enable_shared_from_this
就没用了。查看示例:ideone.com/FlvIWw
你错了。 std::enable_shared_from_this
不应该是防止滥用shared_ptr
界面的保护措施。有关它的用例,请参阅我的答案。
【参考方案1】:
std::enable_shared_from_this<T>
何时有用的提示在其名称中:当基于某些请求产生对象时,可能需要返回指向对象本身的指针。如果结果应该是std::shared_ptr<T>
,则有必要从通常无法访问std::shared_ptr<T>
的成员函数中返回这样的指针。
从std::enable_shared_from_this<T>
派生提供了一种获取std::shared_ptr<T>
的方法,只需一个T
类型的指针。但是,这样做会假定该对象已经通过std::shared_ptr<T>
进行管理,并且如果该对象被分配在堆栈上,则会造成混乱:
struct S: std::enable_shared_from_this<S>
std::shared_ptr<S> get_object()
return this->shared_from_this();
;
int main()
std::shared_ptr<S> ptr1 = std::make_shared<S>();
std::shared_ptr<S> ptr2 = ptr1->get_object();
// ...
在现实场景中,可能会在某些情况下返回当前对象的std::shared_ptr<T>
。
【讨论】:
【参考方案2】:有些用例不能像不透明指针一样使用模板std::shared_ptr<T>
。
在这种情况下,这样做很有用:
在 some_file.cpp 中
struct A : std::enable_shared_from_this<A> ;
extern "C" void f_c(A*);
extern "C" void f_cpp(A* a)
std::shared_ptr<A> shared_a = a->shared_from_this();
// work with operation requires shared_ptr
int main()
std::shared_ptr<A> a = std::make_shared<A>();
f_c(a.get());
在 some_other.c 中
struct A;
void f_cpp(struct A* a);
void f_c(struct A* a)
f_cpp(a);
【讨论】:
我有点困惑,为什么要在 f_cpp 中使用 sharea_ptr?如果 f_cpp 需要,它应该接受 shared_ptr 而不是原始 pointef A*。【参考方案3】:假设我想表示一个计算树。我们将一个加法表示为从表达式派生的类,该类具有两个指向表达式的指针,因此可以递归地评估表达式。但是,我们需要在某个地方结束评估,所以让我们自己评估数字。
class Number;
class Expression : public std::enable_shared_from_this<Expression>
public:
virtual std::shared_ptr<Number> evaluate() = 0;
virtual ~Expression()
;
class Number : public Expression
int x;
public:
int value() const return x;
std::shared_ptr<Number> evaluate() override
return std::static_pointer_cast<Number>(shared_from_this());
Number(int x) : x(x)
;
class Addition : public Expression
std::shared_ptr<Expression> left;
std::shared_ptr<Expression> right;
public:
std::shared_ptr<Number> evaluate() override
int l = left->evaluate()->value();
int r = right->evaluate()->value();
return std::make_shared<Number>(l + r);
Addition(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right) :
left(left),
right(right)
;
Live on Coliru
请注意,用return std::shared_ptr<Number>(this);
实现Number::evaluate()
的“明显”方式被破坏了,因为它会导致双重删除。
【讨论】:
evaluate
的返回类型应该是std::shared_ptr<Number>
,让你免于所有邪恶的铸造。
您忘记指定Number
和Addition
的基类。
@BenVoigt 是的,在这个示例代码中,只需这样做。我的程序有不同的类型,这就是我最初没有这样做的原因。 (我不得不使用std::shared_from_this()
的演员表也很遗憾,但我想这就是生活)
@Oktalist 已修复。还添加了一个指向 coliru 的链接。
您必须将shared_ptr<Expression>
显式转换为shared_ptr<Number>
,原因与使用原始指针时必须将Expression*
显式转换为Number*
相同。如果Expression::evaluate
的返回类型是shared_ptr<Expression>
(这似乎是合理的),那么shared_ptr<Number>
将不符合协变返回类型,这很不幸。以上是关于我们啥时候应该使用 std::enable_shared_from_this的主要内容,如果未能解决你的问题,请参考以下文章
我们啥时候应该使用 PreparedStatement 而不是 Statement?