auto_ptr | 用于解决资源自动释放问题,见如下代码: if (error occur) |
unique_ptr | unique_ptr作为auto_ptr替代品。它对引用对象所有权专一,因此得名unique unique_ptr<Obj> ap(new Obj() ); unique_ptr<Obj> two = one; //error 也就是unique_ptr对对象的引用专一,不允许随意转移。 2) 可进行移动构造和移动赋值操作 |
shared_ptr | auto_ptr和unique_ptr只能用一个智能指针引用对象,而shared_ptr则是可以多个智能指针同时拥有一个对象。shared_ptr实现方式是采用引用计数。引用计数原理即多个智能指针同时引用一个对象,每当引用一次,引用计数加1,当一个智能指针销毁,引用计数减1,当引用计数减少到0的时释放引用对象。这种引用计数增减发生在智能指针的构造函数,复制构造函数,赋值操作符,析构函数中。这种方式使得多个智能指针同时对所引用的对象有拥有权,同时在引用计数减到0之后自动释放资源,也实现了auto_ptr和unique_ptr的资源释放功能。 } |
weak_ptr | shared_ptr是一种强引用的关系,智能指针直接引用对象。那么这个代码有一个隐含的问题,就是循环引用,从而造成内存泄漏。示例看一个循环引用的例子。 |
应用总结 | 尽量使用unique_ptr而不是auto_ptr shared_ptr满足大部分需求; weak_ptr避免递归的依赖关系; |
make_shared | 1. 在初始化unique_ptr或者shared_ptr时,优先使用std::make_unique和std::make_shared。原因: 1)异常安全性 假设有如下函数声明: intcomputePriority(); processInvestment(std::shared_ptr<Investment>(newInvestment()),computePriority()); 由于在C++中函数参数的执行顺序不固定,所以在上面对函数processInvestment调用中,函数参数的执行顺序很可能是: new Investment computePriority() std::shared_ptr constructor 这种执行顺序的风险是,如果在第二步,执行computePriority的过程中出现异常,那么在第一步中new出来的对象将变得不可访问,从而造成内存泄漏。 2)执行效率(对于shared_ptr而言) std::shared_ptr<Investment>ptr(new Investment); //方式1,new的方式在方式1中,会涉及到两次动态内存分配: 第1次是new Investment时,为Investment对象分配空间; 第2次是为控制块(Control Block)分配空间。 auto pIn = std::make_shared<Investment>();//方式2,make_shared的方式 在方式二中,一次动态内存分配就足够了,这是由于make_shared会为Investment对象和控制块(Control block)一次性分配一大块内存。由于只有一次内存分配,因而方式二提高了程序的执行效率。 2、make_xxx函数的弊端 既然使用make_xxx有这么多好处,是否应处处使用make函数而完全放弃new方式?当然不是,make函数存在以下限制: 1) make函数不支持用户自定义释放器。由于make函数有自己的内存分配和析构规则,所以不适用于自定义分配器和释放器的对象。 2) make函数不支持大括号初始化方式。对于下面这句代码: auto spv = std::make_shared<vector<int>>(10,20); 意为spv指向一个vector,该vector有10个元素,每个元素值为20。如果想实现的是这个vector有两个元素,分别为10,20的话,只能用new方式。 3) 内存释放不够灵活。 在使用new的方式中,有两块独立的堆内存,一块存放资源对象,一块存放控制块,当资源对象的引用计数为0的时候,资源对象会被销毁,它所占用的内存也会被随之销毁。 在使用make函数的方式中,make函数一次性为资源对象和控制块分配了一块内存,当资源对象的引用计数为0是,对象被销毁,但是资源对象占用的内存却不会被销毁,只有当控制块占用的内存被销毁是才会将资源对象所占内存一并释放。那么,控制块内存什么时候被释放呢?这就涉及到控制块中另一个引用计数了,这个引用计数被称为“Weak Count”,其作用是用来计数指向该资源的weak_ptr的数量。当这个weak count的值为0时,控制块才会被释放。当资源对象非常庞大时,使用make函数的方式将造成不小的资源浪费。 |