std::unique_ptr
std::unique_ptr
是一种独占的语义,即只允许一个智能指针引用裸指针,这区别于 std::shared_ptr
允许多个 shared_ptr
引用同一个裸指针,它没有引用计数,它的性能比 shared_ptr
会高一点。
在用法上 std::unique_ptr
和 std::shared_ptr
是类似的,主要的不同是 std::unique_ptr
之间的赋值需要通过 std::move
实现。
在 code2 目录下新建一个 code5.cpp 文件:
#include
#include
#include
struct A{
~A(){
std::cout<<"destruct A"< p(new int(1));
std::cout<<*p< p(new A);
std::unique_ptr p1 = std::move(p);
}
{
std::unique_ptr p(new A[3]);
}
{
std::unique_ptr> p(new A, [](A* ptr){
std::cout << "delete from a custom deleter...\n";
delete ptr;
});
}
}
编译和运行代码:在 build 目录下执行
g++ ../code5.cpp -o code5 -std=c++11 && ./code5
输出结果:
1
destruct A
destruct A
destruct A
destruct A
delete from a custom deleter...
destruct A
构造 unique_ptr
的时候第二个参数是一个自定义删除器,如果不填写自定义删除器,就会使用默认的删除器,一般情况下我们用默认的删除器就可以了,如果有需要也可以写自定义的删除器。注意 C++11 中构造 std::unique_ptr
不能像 std::shared_ptr
那样(通过 make_shared
创建)通过 make_unique
方式去创建,make_unique
在 C++14 中才提供,当然也可以自己实现一个,具体实现这里不再赘述。
std::weak_ptr
std::weak_ptr 是用来监视 std::shared_ptr 的,通过 weak_ptr 就可以得知它监视的 shared_ptr 是否已经销毁了。
在 code2 目录下新建一个 code6.cpp 文件:
#include
#include
std::weak_ptr gw;
void f()
{
if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr
std::cout << *spt << "\n";
}
else {
std::cout << "gw is expired\n";
}
}
int main()
{
{
auto sp = std::make_shared(42);
gw = sp;
f();
}
f();
}
编译和运行代码:在 build 目录下执行
g++ ../code6.cpp -o code6 -std=c++11 && ./code6
输出结果:
42
gw is expired
std::weak_ptr
需要通过 std::shared_ptr
构造,当所监视的 shared_ptr
析构之后,通过 weak_ptr
的 lock
方法返回的是一个 nullptr
,如果 shared_ptr
仍然存在则返回 shared_ptr
。
https://www.shiyanlou.com/courses/1414/learning/?id=14993
通过 shared_ptr 保证安全地回调
shared_ptr
的一个重要作用就是在回调的时候是安全的,因为回调的时候,该回调函数的宿主对象可能已经被销毁了,那么在后面某个时刻回调的时候,实际上该对象已经不存在了,这时候再执行回调函数就是错误的。通过 shared_ptr
可以解决这个问题,保证安全地回调。
本小结内容可以先无脑动手做一下,到了实验四的时候就可以理解这样做的好处了。
在 code2 目录下新建一个 code7.cpp 文件:
#include
#include
#include
class person : public std::enable_shared_from_this {
public:
void update(){
name_ = "jack";
callback_();
}
void init_callback(){
callback_ = [this]{
test();
};
}
void test(){
auto self = this->shared_from_this();
std::cout< callback_;
std::string name_ = "tom";
};
int main(){
person* ptr = nullptr;
{
std::shared_ptr p = std::make_shared();
p->init_callback();
ptr = p.get();
}
ptr->update();
}
编译和运行代码:在 build 目录下执行
g++ ../code7.cpp -o code7 -std=c++11 && ./code7
输出结果:
terminate called after throwing an instance of 'std::bad_weak_ptr'
what(): bad_weak_ptr
[1] 209 abort (core dumped) ./code7
会出现一个运行错误,因为在调用 ptr->update()
时,该 ptr
已经被前面的 shared_ptr
释放了,这时候这个 ptr
实际上是无效的。
为了解决这个问题,我们可以通过派生于 std::enable_shared_from_this
来通过对象自身得到 shared_ptr
,然后在设置回调的 lambda
中捕获该 shared_ptr
,这样,lambda
表达式中的 shared_ptr
和 lambda
回调函数的生命周期一致了。
在 code2 目录下新建一个 code8.cpp 文件:
#include
#include
#include
class person : public std::enable_shared_from_this {
public:
void update(){
name_ = "jack";
callback_();
}
void init_callback(){
auto self = this->shared_from_this();
callback_ = [this, self]{
test();
};
}
void test(){
auto self = this->shared_from_this();
std::cout< callback_;
std::string name_ = "tom";
};
int main(){
person* ptr = nullptr;
{
std::shared_ptr p = std::make_shared();
p->init_callback();
ptr = p.get();
}
ptr->update();
}
编译和运行代码:在 build 目录下执行
g++ ../code8.cpp -o code8 -std=c++11 && ./code8
输出结果:
jack
关键的一句是 lambda
捕获了 shared_ptr
,即使外面的 shared_ptr
重置了,但是 lamdba
表达式中捕获的 shared_ptr
仍然存在,引用计数不会减为 0,可以保证回调回来的时候,该对象仍然存在。
尽量使用std::make_unique和std::make_shared而不直接使用new
让我们从对齐std::make_unique 和 std::make_shared这两块开始。std::make_shared是c++11的一部分,但很可惜std::make_unique不是。它是在c++14里加入标准库的。假如你在使用c++11,也别担心,你很容易写出一个基本的版本。看这里:
template
std::unique_ptr make_unique(Ts&&... params)
{
return std::unique_ptr(new T(std::forward(params)...));
}
正如你看到的,make_unique完美传递了参数给对象的构造函数,从一个原始指针构造出一个std::unique_ptr,返回创建的std::unique_ptr。这个形式的函数不支持数组和定制删除器(见条款18),但它证明了一点点的努力就可以根据需要创建一个make_unique。要记住的是不要把你的版本放到std命名空间里,因为你不想当升级到c++14后会和库提供的标准实现冲突吧。
尽量使用的原因:https://blog.csdn.net/bandaoyu/article/details/112197053