我们什么时候应该使用 std::enable_shared_from_this
When should we use std::enable_shared_from_this
我刚知道 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';
}
上面的代码是"not so good",因为在调用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
对象来创建一个新对象,而不是使用原始指针?
有些用例不能使用模板 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 的加法,该表达式派生自具有两个指向表达式的指针的表达式,因此可以递归地计算表达式。但是,我们需要在某处结束评估,所以让数字自行评估。
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()
的 "obvious" 方式已损坏,因为它会导致双重删除。
有关 std::enable_shared_from_this<T>
何时有用的提示在其名称中:当根据某些请求生成对象时,可能需要 return 指向对象本身的指针。如果结果应该是 std::shared_ptr<T>
,则有必要从成员函数中 return 这样的指针,其中通常没有 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>
到当前对象 returned。
我刚知道 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';
}
上面的代码是"not so good",因为在调用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
对象来创建一个新对象,而不是使用原始指针?
有些用例不能使用模板 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 的加法,该表达式派生自具有两个指向表达式的指针的表达式,因此可以递归地计算表达式。但是,我们需要在某处结束评估,所以让数字自行评估。
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()
的 "obvious" 方式已损坏,因为它会导致双重删除。
有关 std::enable_shared_from_this<T>
何时有用的提示在其名称中:当根据某些请求生成对象时,可能需要 return 指向对象本身的指针。如果结果应该是 std::shared_ptr<T>
,则有必要从成员函数中 return 这样的指针,其中通常没有 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>
到当前对象 returned。