我们什么时候应该使用std::enable_shared_from_this

When should we use std::enable_shared_from_this

本文关键字:enable from this shared std 什么时候 我们      更新时间:2023-10-16

我只知道std::enable_shared_from_this形成这个链接。
但是看完下面的代码后,我不知道什么时候使用它。

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对象具有相同的控制块,以便我们可以避免双重删除问题。但是,如果我必须在开始时提醒自己创建一个shared_ptr,为什么不提醒自己使用shared_ptr对象来创建一个新对象,而不是使用原始指针呢?

关于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>的某种条件。

有些用例不能使用模板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);
}

假设我想表示一个计算树。我们将有一个加法表示为从表达式派生的类,其中包含两个指向表达式的指针,因此可以递归计算表达式。但是,我们需要在某个地方结束评估,所以让我们让数字自己评估。

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

住在科里鲁

请注意,使用return std::shared_ptr<Number>(this);实现Number::evaluate()的"明显"方式被破坏了,因为它会导致双重删除。