文章目录
前言
- 我们知道RAII思想让我们想要用智能指针来管理资源,但shared_ptr和make_shared()以及weak_ptr的用法让我们感觉到疑惑,下面是我个人总结。
1.shared_ptr
1.1 构造
- 可以通过以下方式构造shared_ptr
struct TP {
};
int main() {
// 1.
shared_ptr<TP> sptr(new TP());
// 2.make_shared
shared_ptr<TP> sptr1 = make_shared<TP> ();
// 3.自定义分配器使用std::allocate_shared<TP> ();
return 0;
}
1.2 shared_ptr可能存在的问题
- 如下由于CPP在
当参数的求值顺序不影响函数结果时,编译器可以自由选择参数调用顺序
,所以main函数中的func的参数的调用顺序可能是:
- new TP()
- new TP()
- shared_ptr
- shared_ptr
- 这样可能造成的问题就是资源没有及时的绑定到shared_ptr上,如果第二个TP对象创建失败了那么第一步创建的TP对象就无处释放了。
struct TP {
};
void func(shared_ptr<TP> &a,shared_ptr<TP> &b){
...
...
}
int main() {
func(shared_ptr<TP>(new TP()),shared_ptr<TP>(new TP()));
return 0;
}
2. make_shared
- std::make_shared 提供了更好的异常安全性,因为它保证了控制块和对象的内存是一次性分配的,并且
控制块是在对象之前构造的
(意味着即使new 资源失败了,控制块也有正确的引用计数,可以正确的管理资源,而shared_ptr那时候的控制块还没创建没法管理资源,导致资源泄露)。这有助于避免在对象构造过程中发生异常时出现资源泄漏和其他问题。
2.1 优点
- 同时申请资源所占的内存和控制块内存,它将数据空间和控制块空间的申请合并为一次,更高效
- 异常安全:如上所述,make_shared由于先创建控制块对象,所以可以在发生异常的情况下正确的处理。
2.2 缺点
- 网上都说它的缺点是如果有weak_ptr仍指向控制块的内存,且仍在作用域内就无法释放整块内存,但是我的实验中看不出这个缺点,欢迎指正~
#include <bits/stdc++.h>
using namespace std;
struct Ta {
~Ta() {cout <<"a is destructed"<< endl;}
};
int main() {
weak_ptr<Ta> wptr;
{
shared_ptr<Ta> aptr = make_shared<Ta> ();
wptr = aptr;
}
cout << "before end" << endl;
return 0;
}
- destructed将会在before_end之前输出,证明weak_ptr不会指向控制块不会影响内存的释放。
3. weak_ptr
- weak_ptr的设计目的,我认为
- 避免循环引用
- 不影响资源生命周期的观察对象(可用于观察者模式)
3.1 weak_ptr基本使用
struct TP {};
int main() {
shared_ptr<TP> sptr = make_shared<TP> ();
weak_ptr<TP> wptr = sptr;
cout <<sptr.use_count<< endl; // 1.查看引用计数
if(sptr.expired()) {
cout <<"resouce is expired"<< endl; // 2.查看资源是否还有效
}
shared_ptr<TP> newptr = wptr.lock();
if(newptr) {
cout << "resouce exist and create success" << endl; // 3. 资源有效的时候可以创建shared_ptr来管理
}
return 0;
}
- 我认识的主要使用方式就这三种
- 还有就是shared_ptr对象的reset不会影响到它之前监控的资源。
这是我对shared_ptr的简单实现