shared_ptr存在的缺陷:
使用shared_ptr开辟内存实际上会开辟两部分内存:
- 1、堆内存的资源 ,也就是我们想托管的资源
- 2、引用计数对象,也是new出来的
如果在这个过程中, 开辟我们想要的内存(new int(10))成功了,但是开辟引用计数内存失败了,这时就对导致我们无法通过引用计数来判断何时需要析构函数。
换成make_shared:
make_shared把开辟对象资源内存和引用对象计数资源的内存合并到一起了
这样的话,就是new一次,要么都成功,要么都失败,不会存在托管的资源new成功而引用计数对象new失败造成智能指针对象创建失败的这种情况了
make_shared相当于是函数模板,可以直接类型实例化;
- 这个10相当于我们原本写的new int(10),这个10也是在堆上的,只不过这个内存不需要我们自己去开辟去书写new int 了;
- 我们只需要把10这种用户想要赋值的数据给它,然后把相应的类型进行实例化< int >,然后它就会帮我们组织底层的内存;
- 把我们存放资源的内存和存放引用计数的内存放在一块,打包成一个结构,然后new一次,要么全成功,要么全失败。全失败的话,意味着没有开辟任何的内存,没有占用任何的内存,也就不需要析构释放了。
#include <iostream> using namespace std; class Test { public: Test(int a) { cout << "Test(int)" << endl; } Test(int a, int b) { cout << "Test(int,int)" << endl; } }; int main() { shared_ptr<int> sp1(new int(10)); shared_ptr<Test> sp2(new Test(10)); auto sp3 = make_shared<int>(10); *sp3 = 20; cout << *sp3 << endl; auto sp4 = make_shared<Test>(10); //new Test(10); auto sp5 = make_shared<Test>(10, 20); //new Test(10,20); return 0; }
运行结果
也可以写成:
auto sp4(make_shared<Test>(10)); //new Test(10) auto sp5(make_shared<Test>(10,20); //new Test(10,20)
make_shared存在的问题
原始的shared_ptr资源内存和引用计数内存是分开的,当最后一个引用该资源的智能指针出作用域后,uses从1减到0,就直接把内存释放掉了,这时不用考虑weaks数量,就是不用管还有多少个weak_ptr在观察。
对于make_shared,因为现在资源内存和引用计数内存是放在一起的,一起开辟就要一起释放,假如现在引用该资源的所有强智能指针都出作用域了,但是引用计数对象还不能释放,因为它的weaks不为0,还有弱智能指针在观察它呢。
make_unique
template <typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... args);
它接受一个可变参数模板参数
Args
,用于传递给T
类型的构造函数的参数。make_unique
在内部使用new
运算符创建一个对象,并返回一个包含该对象的std::unique_ptr
。make_unique可以更简洁的创建动态分配的对象并将其封装在std::unique_ptr中,同时避免了手动调用new和delete提高代码的安全性和可读性。
std::unique_ptr<int> ptr = std::make_unique<int>(42);