异步编程中存在一种场景,需要在类中将该类的对象注册到某个回调类或函数中,不能简单地将this传递给回调类中,很可能因为回调时该对象不存在而导致野指针访问(也有可能在析构函数解注册时被回调,造成对象不完整)。若通过智能指针管理对象,也不能直接将this构造成shared_ptr,因为无法通过裸指针获得引用计数块信息(而侵入式智能指针的引用计数块和对象内存连续,可以简单地通过偏移引用计数块大小得到),强行构造会导致对象内存多次释放。《C++20高级编程》
书中用例:
struct Obj {
void submit() {
// 内存风险,无法通过裸指针获得引用计数块信息
executor.submit([this]{ this->onComplete(); });
}
}
// 奇异递归模板模式
struct Obj : enable_shared_from_this<Obj> {
void submit() {
// weak_from_this()获得this指针的弱指针
executor.submit([obj = weak_from_this()]{
// lock提升至共享指针
if (auto spObj = obj.lock()) {
spObj->onComplete();
}
});
}
}
下面演示一下书中说的无法通过裸指针获得引用计数块信息的含义
1、错误示例:
class A
{
public:
A() { std::cout << "A的构造函数" << std::endl; }
~A() { std::cout << "A的析构函数" << std::endl; }
std::shared_ptr<A> getSharedPtr() { return std::shared_ptr<A>(this); }
};
int main()
{
{
// 演示shared_ptr的正确用法(推荐使用std::make_shared()构造,能够使得引用计数块和对象在内存上连续)
std::cout << "代码块【1】开始" << std::endl;
A *pa = new A();
std::shared_ptr<A> sp1(pa);
std::cout << "A的引用计数,sp1.use_count() = " << sp1.use_count() << std::endl;
std::shared_ptr<A> sp2(sp1);
std::cout << "A的引用计数:sp2.use_count() = " << sp2.use_count() << std::endl;
std::cout << "代码块【1】结束" << std::endl;
}
{
std::cout << "代码块【2】开始" << std::endl;
A *pa = new A();
std::shared_ptr<A> sp1(pa);
std::cout << "A的引用计数,sp1.use_count() = " << sp1.use_count() << std::endl;
std::shared_ptr<A> sp2(pa);
std::cout << "A的引用计数:sp2.use_count() = " << sp2.use_count() << std::endl;
std::shared_ptr<A> sp3(pa->getSharedPtr());
std::cout << "A的引用计数:sp3.use_count() = " << sp3.use_count() << std::endl;
std::cout << "代码块【2】结束" << std::endl;
}
system("pause");
return 0;
}
代码块【1】中使用了共享指针的拷贝操作,引用计数是正确的。而代码块【2】中的三个共享指针都是用裸指针pa初始化的,那么得到的三个共享指针sp1、sp2和sp3各不相干,各自维护着自己的引用计数,因而引用计数都是1。也就是书中说的“无法通过裸指针获得引用计数块信息”。
由于引用计数错误,那么在代码块【2】结束的时候,这三个共享指针sp1、sp2和sp3都将释放内存,就会导致内存要被释放3次,这显然会让程序崩溃。因此在第二次释放内存时,程序崩溃,所以图中打印信息只显示2行“A的析构函数”。
2、正确示例
class A : public std::enable_shared_from_this<A>
{
public:
A() { std::cout << "A的构造函数" << std::endl; }
~A() { std::cout << "A的析构函数" << std::endl; }
std::shared_ptr<A> getSharedPtr() { return weak_from_this().lock(); }
};
int main()
{
{
std::cout << "代码块【3】开始" << std::endl;
A *pa = new A();
std::shared_ptr<A> sp1(pa);
std::cout << "A的引用计数,sp1.use_count() = " << sp1.use_count() << std::endl;
std::shared_ptr<A> sp2(sp1); // 修改代码块【2】的裸指针pa为共享指针sp1
std::cout << "A的引用计数:sp2.use_count() = " << sp2.use_count() << std::endl;
std::shared_ptr<A> sp3(pa->getSharedPtr());
std::cout << "A的引用计数:sp3.use_count() = " << sp3.use_count() << std::endl;
std::cout << "代码块【3】结束" << std::endl;
}
system("pause");
return 0;
}