学习博客:
https://blog.csdn.net/zk3326312/article/details/79108690
https://zhuanlan.zhihu.com/p/348650382
https://blog.csdn.net/u013745174/article/details/52900870?ops_request_misc=&request_id=&biz_id=102&utm_term=enable_shared_from_this&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-4-.nonecase&spm=1018.2226.3001.4187
c++ shared_from_this
enable_shared_from_this是一个模板类,定义于头文件,其原型为:
template< class T > class enable_shared_from_this;
std::enable_shared_from_this 能让一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, … ) ,这些 pt 共享对象 t 的所有权。
若一个类 T 继承 std::enable_shared_from_this ,则会为该类 T 提供成员函数: shared_from_this 。 当 T 类型对象 t 被一个为名为 pt 的 std::shared_ptr 类对象管理时,调用 T::shared_from_this 成员函数,将会返回一个新的 std::shared_ptr 对象。
使用原因
1.把当前类对象作为参数传给其他函数时,为什么要传递share_ptr呢?直接传递this指针不可以吗?
一个裸指针传递给调用者,谁也不知道调用者会干什么?假如调用者delete了该对象,而share_tr此时还指向该对象。
2.这样传递share_ptr可以吗?share_ptr
这样会造成两个非共享的share_ptr指向一个对象,最后造成2次析构该对象。
实现原理
enable_shared_from_this的一种实现方法是,其内部有一个weak_ptr类型的成员变量_Wptr,当shared_ptr构造的时候,如果其模板类型继承了enable_shared_from_this,则对_Wptr进行初始化操作,这样将来调用shared_from_this函数的时候,就能够通过weak_ptr构造出对应的shared_ptr。
使用场景
在什么情况下要使类A继承enable_share_from_this?
当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。
我们就使类A继承enable_share_from_this,然后通过其成员函数share_from_this()返回当指向自身的share_ptr。
在很多情况下,我们会在基类中继承 std::enable_shared_from_this 来使得在该类的方法中能够使用 shared_ptr 托管的 this 指针。例如:
class Base : public std::enable_shared_from_this<Base>
{
public:
Base() { /* ... */ }
~Base() { }
void method() {
// ...
// 传递该类shared_ptr托管的this指针
function1(shared_from_this());
}
// ...
private:
// ...
};
void function1(std::shared_ptr<Base> base_i) {
// ...
}
但如果在子类中使用 shared_from_this() 函数,就会发生错误。例如:
class Derived : public Base
{
public:
Derived() { /* ... */ }
~Deried() { }
void method(std::shared_ptr<Derived> derived) {
// ...
// 错误:
// 不存在用户定义的从“std::shared_ptr<Base>”到“std::shared_ptr<Derived>”的转换
function2(shared_from_this());
}
private:
// ...
};
void function2(std::shared_ptr<Derived> derived) {
// ...
}
int main() {
std::shared_ptr<Derived> derived = std::make_shared<Derived>();
derived->method();
return 0;
}
分析其原因,是因为 Base 在继承enable_shared_from_this时,向模板T传递的类型为Base,也就是 shared_from_this() 函数返回值类型被设定为了 std::shared_ptr。所以在 Derived 中调用 shared_from_this(),返回值就是std::shared_ptr了。
由于无法将裸指针或是智能指针从基类隐性转换至派生类,所以会发生以上错误。
这时就需要显性的对返回值类型进行转换。
解决方法是使用std::dynamic_pointer_cast(shared_from_this())将返回的指针转换为派生类类型的指针:
class Derived : public Base
{
public:
Derived() { /* ... */ }
virtual ~Derived() { }
void method(std::shared_ptr<Derived> derived) {
// ...
// shared_from_this()的返回值类型为std::shared_ptr<Base>,
// 使用std::dynamic_pointer_cast<Derived>函数将其转为std::shared_ptr<Derived>类型
std::shared_ptr<Derived> pointer = std::dynamic_pointer_cast<Derived>(shared_from_this());
function2(pointer);
}
private:
// ...
};
最后需要注意:
-
使用std::dynamic_pointer_cast()需要基类中存在虚函数,这是由于这个转换函数使用输入的类型和目标类型中是否存在相同签名的虚函数作为转换能否成功的标识。最简单也是正确的解决方法是将基类中的析构函数声明为虚函数。
-
不能在构造函数中使用shared_form_this()。这是由于std::enable_share_from_this在实现时使用了一个对象的weak_ptr,而这个weak_ptr需要对象的shared_ptr进行初始化。由于此时对象尚未构造完成,所以会抛出std::bad_weak_ptr的异常。
关于这点目前没有较为完美的方案,可以尝试写一个init()函数,在对象构造后手动调用。或是手动写一个std::shared_ptr(this)使用,但这种解决方案可能造成循环引用。
更多方案请查阅StackOverFlow。
shared_from_this基本用法
#include <iostream>
#include <string>
#include <memory>
#include <functional>
using namespace std;
class TcpConnection : public std::enable_shared_from_this<TcpConnection>
{
typedef function<void (shared_ptr<TcpConnection>)> CloseCallback;
public:
TcpConnection() { cout << "construct" << endl; };
~TcpConnection() { cout << "destroy" << endl; };
void handleClose() {
closeCallback(shared_from_this());
}
void setCloseCallback(CloseCallback cb) {
closeCallback = cb;
}
private:
CloseCallback closeCallback;
};
void closeInfo(shared_ptr<TcpConnection> tc) {
// 打印当前 shared_ptr 的引用计数
cout << "ref count is " << tc.use_count()<< endl; // 2
}
// 当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数
// 传给其他函数时,需要传递一个指向自身的share_ptr。
int main(){
shared_ptr<TcpConnection> tc = make_shared<TcpConnection>();
tc->setCloseCallback(closeInfo);
cout << "ref count is " << tc.use_count() << endl; // 1
tc->handleClose();
cout << "ref count is " << tc.use_count() << endl; // 1
return 0;
}
//======================================================================
this 为什么不能替换 shared_from_this
【方式1】
#include <iostream>
#include <string>
#include <memory>
#include <functional>
using namespace std;
class TcpConnection {
typedef function<void(TcpConnection*)> CloseCallback;
public:
TcpConnection() { cout << "construct" << endl; };
~TcpConnection() { cout << "destroy" << endl; };
void handleClose() {
closeCallback(this);
}
void setCloseCallback(CloseCallback cb) {
closeCallback = cb;
}
private:
CloseCallback closeCallback;
};
shared_ptr<TcpConnection> gtc = make_shared<TcpConnection>(); //设为全局方便看它的计数
void closeInfo(TcpConnection* tc) {
// 这里如果对 tc 动手脚,那不就搞笑了
cout << "ref count is " << gtc.use_count() << endl; // 1
}
int main() {
cout << "ref count is " << gtc.use_count() << endl; // 1
gtc->setCloseCallback(closeInfo);
gtc->handleClose();
cout << "ref count is " << gtc.use_count() << endl; // 1
return 0;
}
【方式2】
#include
#include
#include
#include
using namespace std;
class TcpConnection : public std::enable_shared_from_this
{
typedef function<void(shared_ptr)> CloseCallback;
public:
TcpConnection() { cout << “construct” << endl; };
~TcpConnection() { cout << “destroy” << endl; };
void handleClose() {
// 此处重构一个不相干的 shared_ptr ,导致 this 被析构两次,这个说法有误
// 运行到此处报错的原因:同一普通指针不能同时为多个 shared_ptr 对象赋值,
// 否则导致程序发生异常
closeCallback(shared_ptr(this));
}
void setCloseCallback(CloseCallback cb) {
closeCallback = cb;
}
private:
CloseCallback closeCallback;
};
void closeInfo(shared_ptr tc) {
// 打印当前 shared_ptr 的引用计数
cout << "ref count is " << tc.use_count() << endl;
}
int main() {
shared_ptr tc = make_shared();
tc->setCloseCallback(closeInfo);
cout << "ref count is " << tc.use_count() << endl; // 1
tc->handleClose();
cout << "ref count is " << tc.use_count() << endl; // 1
return 0;
}
//======================================================================
派生类中 shared_from_this 的使用
【如下方式没问题】
#include <iostream>
#include <string>
#include <memory>
#include <functional>
using namespace std;
class TcpConnection : public std::enable_shared_from_this<TcpConnection>
{
typedef function<void(shared_ptr<TcpConnection>)> CloseCallback;
public:
TcpConnection() { cout << "construct" << ", " << __FUNCTION__ << endl; };
~TcpConnection() { cout << "destroy" << ", " << __FUNCTION__ << endl; };
void handleClose() {
closeCallback(shared_from_this());
}
void setCloseCallback(CloseCallback cb) {
closeCallback = cb;
}
CloseCallback closeCallback;
};
class TcpIPV4Connection : public TcpConnection
{
public:
TcpIPV4Connection() { cout << "construct" << ", " << __FUNCTION__ << endl; };
~TcpIPV4Connection() { cout << "destroy " << ", " << __FUNCTION__ << endl; };
void handleTcpIPV4Close() {
closeCallback(shared_from_this());
}
};
void closeInfo(shared_ptr<TcpConnection> tc) {
// 打印当前 shared_ptr 的引用计数
cout << "ref count is " << tc.use_count() << ", " << __FUNCTION__ << endl; // 2
}
// 当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数
// 传给其他函数时,需要传递一个指向自身的share_ptr。
int main() {
shared_ptr<TcpIPV4Connection> tc = make_shared<TcpIPV4Connection>();
tc->setCloseCallback(closeInfo);
cout << "ref count is " << tc.use_count() << ", " << __FUNCTION__ << endl; // 1
tc->handleTcpIPV4Close();
cout << "ref count is " << tc.use_count() << ", " << __FUNCTION__ << endl; // 1
return 0;
}
【如下方式没问题】
#include <iostream>
#include <string>
#include <memory>
#include <functional>
using namespace std;
class TcpConnection : public std::enable_shared_from_this<TcpConnection>
{
typedef function<void(shared_ptr<TcpConnection>)> CloseCallback;
public:
TcpConnection() { cout << "construct" << ", " << __FUNCTION__ << endl; };
virtual ~TcpConnection() { cout << "destroy" << ", " << __FUNCTION__ << endl; };
void handleClose() {
closeCallback(shared_from_this());
}
void setCloseCallback(CloseCallback cb) {
closeCallback = cb;
}
CloseCallback closeCallback;
};
class TcpIPV4Connection : public TcpConnection
{
typedef function<void(shared_ptr<TcpIPV4Connection>)> TcpIPV4CloseCallback;
public:
TcpIPV4Connection() { cout << "construct" << ", " << __FUNCTION__ << endl; };
virtual ~TcpIPV4Connection() { cout << "destroy " << ", " << __FUNCTION__ << endl; };
void handleTcpIPV4Close() {
// 不加 dynamic_pointer_cast 的话,预处理报错
// 提示:closeCallback 的形参不对,应该是 shared_ptr<TcpConnection>
// 见下方图片
// 加了 dynamic_pointer_cast 的话,编译报错
// 提示:error C2683: “dynamic_cast”:“TcpConnection”不是多态类型
// 所以最快解决方式:把基类和派生类的析构函数置为 virtual
closeCallback(dynamic_pointer_cast<TcpIPV4Connection>(shared_from_this()));
}
void setTcpIPV4CloseCallback(TcpIPV4CloseCallback cb) {
closeCallback = cb;
}
TcpIPV4CloseCallback closeCallback;
};
void closeInfo(shared_ptr<TcpConnection> tc) {
// 打印当前 shared_ptr 的引用计数
cout << "ref count is " << tc.use_count() << ", " << __FUNCTION__ << endl; // 2
}
void closeTCPIPV4Info(shared_ptr<TcpIPV4Connection> tc) {
// 打印当前 shared_ptr 的引用计数
cout << "ref count is " << tc.use_count() << ", " << __FUNCTION__ << endl; // 2
}
// 当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数
// 传给其他函数时,需要传递一个指向自身的share_ptr。
int main() {
shared_ptr<TcpIPV4Connection> tc = make_shared<TcpIPV4Connection>();
tc->setTcpIPV4CloseCallback(closeTCPIPV4Info);
cout << "ref count is " << tc.use_count() << ", " << __FUNCTION__ << endl; // 1
tc->handleTcpIPV4Close();
cout << "ref count is " << tc.use_count() << ", " << __FUNCTION__ << endl; // 1
return 0;
}