文章目录
😁介绍
自C++11起,有三大智能指针:
unique_ptr
shared_ptr
weak_ptr
都是内存管理中的非常重要的一部分动态内存管理 - cppreference.com。
其中shared_ptr
在实际应用中具有非常广泛的应用。而拷贝操作也是非常常见和重要的操作。在类外可以直接使用默认的拷贝构造和拷贝赋值,而类内呢?显然这两种默认的拷贝操作均不适用。
本文就是讲解使用std::enable_shared_from_this::shared_from_this()
来处理该问题。
🤔类外操作
😅错误操作
使用原始指针构造。
这是一种非常经典的错误。虽然构造出的智能指针都被多个smart_ptr掌管了,但是并非同一个控制块来操作,是随着每次构造都有一个控制块。
因此下方示例代码会产生两次析构。严重情况下可以让项目程序直接崩溃。
#include <iostream>
#include <memory>
struct Node {
~Node() {
std::printf("[%p] %s\n", this, __func__);
}
};
void test(Node* p) {
// errro 同一个对象析构了两次
std::shared_ptr<Node> sp1(p);
std::shared_ptr<Node> sp2(p);
}
int main() {
Node* p = new Node{};
test(p);
std::printf("[%u] %s end\n", __LINE__, __func__);
}
😂正确操作
直接使用拷贝操作。
对于shared_ptr
默认的拷贝操作,可以由多个shart_ptr都指向同一个对象,并由同一个控制块维护。
#include <iostream>
#include <memory>
struct Node {
~Node() {
std::printf("[%p] %s\n", this, __func__);
}
};
void test(Node* p) {
std::shared_ptr<Node> sp1(p);
// 执行拷贝构造,同时维护同一个p
std::shared_ptr<Node> sp2 = sp1;
}
int main() {
Node* p = new Node{};
test(p);
std::printf("[%u] %s end\n", __LINE__, __func__);
}
🤔类内操作
😮std::enable_shared_from_this<>
😮奇异递归模板 CRTP(Curiously Recurring Template Pattern)
CRTP是一种模板元编程技术,它允许一个类模板通过基类的形式捕获自身的类型。
这种模式的名字来源于其自身的递归使用,“Curiously” 指的是这种模式的非直观性,因为在通常情况下,基类通常是在类定义之后才被引用的,而在这个模式中,基类是在类定义的开始就引用了尚未完全定义的类。
😮基本原理
CRTP的基本原理是利用模板类和继承。在CRTP中,一个类通过继承一个模板类,并将自身作为模板参数传递给基类模板。这样做的好处是可以在派生类中使用基类模板中的成员,而这些成员依赖于派生类的类型。
😮std::enable_shared_from_this<>
std::enable_shared_from_this
能让其一个对象(假设其名为 t
,且已被一个 std::shared_ptr 对象 pt
管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ...
),它们都与 pt
共享对象 t
的所有权。
其中有两个核心函数:
shared_from_this
weak_from_this()
#include <memory>
// 奇异递归模板 CRTP(Curiously Recurring Template Pattern)
struct Node : public std::enable_shared_from_this<Node> { };
😅错误操作
😅shared_ptr 经典错误
还是先展示一个经典错误。
由于在类内的成员函数,(没有额外操作的情况下)只能访问到this
指针。
#include <iostream>
#include <memory>
struct Node {
~Node() {
std::printf("[%p] %s\n", this, __func__);
}
std::shared_ptr<Node> make_this_share_ptr() {
// 经典错误
return std::shared_ptr<Node>(this);
}
};
void test(Node* p) {
std::shared_ptr<Node> sp1 = p->make_this_share_ptr();
std::shared_ptr<Node> sp2 = p->make_this_share_ptr();
}
int main() {
Node* p = new Node{};
test(p);
std::printf("[%u] %s end\n", __LINE__, __func__);
}
😅enable_shared_from_this 经典错误
回到本文的重头戏,这里使用奇异递归模板的方式继承std::enable_shared_from_this<>
,并使用shared_from_this()
创建出指向自己的share_ptr。
但如果直接使用,还是会出现一个经典错误。
#include <iostream>
#include <memory>
struct Node : public std::enable_shared_from_this<Node> {
~Node() {
std::printf("[%p] %s\n", this, __func__);
}
std::shared_ptr<Node> make_this_share_ptr() {
return shared_from_this();
}
};
void test(Node* p) {
// 事先没有创建任何`std::shared_ptr`实例
std::shared_ptr<Node> sp = p->make_this_share_ptr();
}
int main() {
Node* p = new Node{};
test(p);
std::printf("[%u] %s end\n", __LINE__, __func__);
}
即:在没有使用构造出std::shared_ptr
,就直接调用了shared_from_this()
,会抛出异常。
ref: 若在先前未由 std::shared_ptr 共享的对象上调用
shared_from_this
,则抛出 std::bad_weak_ptr
在mingw-gcc中输出如下:
terminate called after throwing an instance of 'std::bad_weak_ptr'
what(): bad_weak_ptr
😂正确操作
正确操作即为先构造出一个std::shared_ptr再使用shared_from_this()
。
保证了前提安全后,不论是std::shared_ptr
还是裸指针
去操作shared_from_this()
都不会出现问题。
#include <iostream>
#include <memory>
struct Node : public std::enable_shared_from_this<Node> {
~Node() {
std::printf("[%p] %s\n", this, __func__);
}
std::shared_ptr<Node> make_this_share_ptr() {
return shared_from_this();
}
};
void test(Node* p) {
// safe
std::shared_ptr<Node> sp0(p);
std::shared_ptr<Node> sp1 = sp0;
std::shared_ptr<Node> sp2 = sp1->make_this_share_ptr();
std::shared_ptr<Node> sp3 = p->make_this_share_ptr();
}
int main() {
Node* p = new Node{};
test(p);
std::printf("[%u] %s end\n", __LINE__, __func__);
}
😁END
ref
关注我
关注我,学习更多C/C++,算法,计算机知识
B站:
👨💻主页:天赐细莲 bilibili