小白学习C++智能指针
智能指针的本质
类。
智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。
为什么要使用智能指针?
c++的内存管理是让很多人头疼的事,当我们写一个new语句时,一般就会立即把delete语句直接也写了,但是我们不能避免程序还未执行到delete时就跳转了或者在函数中没有执行到最后的delete语句就返回了,如果我们不在每一个可能跳转或者返回的语句前释放资源,就会造成内存泄露。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。
智能指针的原理
智能指针是利用RAII或者“获取资源即初始化”思想来实现的。RAII的主要原则是为将任何堆分配资源的所有权提供给其析构函数包含用于删除或释放资源的代码以及任何相关清理代码的堆栈分配对象。也就是,利用C++对象生命周期的概念来控制程序的资源,确保资源能得到及时释放。
当然上述思想仅是基础,在此之上还需要添加诸多实现才能得到我们的智能指针。其中一种比较通用的技术是使用引用计数,将一个计数器与类指向的对象相关联,引用技术跟踪该类有多少个对象的指针指向同一对象。
比如常见的智能指针shared_ptr,多个指针共享一个引用计数器,其引用规则如下:
• 当一个shared_ptr被赋值或者拷贝构造给其他shared_ptr之时,共享引用计数器自增1。
• 当一个shared_ptr被析构或者被用于管理其他裸指针之时,共享引用计数器自减1。
• 当引用计数器为0时,也就是这是管理该指针的最后一个shared_ptr,此时会释放该指针指向的资源。
在RAII的基础上建立合适的引用计数规则,确保智能指针所指向的对象,总能得到正确的释放。
智能指针的类型
智能指针,类型上可以分为两类:
• 独占型:如std::unique_ptr,一份资源,仅能由一个std::unique_ptr对象管理;
• 共享型:如std::shared_ptr,一份资源,可以由多个std::shared_ptr对象共同管理,当没有std::shared_ptr对象指向这份的资源,资源才会被释放,即基于引用技术原理。
细分: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被c++11弃用。
auto_ptr
这个指针,现在已经被C++11标准所抛弃,是C++03的失败品。
auto_ptr的出现,主要是为了解决“被异常抛出时发生资源泄漏”的问题。即如果我们让资源在局部对象构造时分配,在局部对象 析构时释放。这样即使在函数执行过程时发生异常退出,也会因为异常能保证局部对象被析构从而保证资源被释放。auto_ptr就是基于这个理念而设计。
特点
- 1.析构函数会删除它所拥有的对象,并且释放内存;auto_ptr析构的时候肯定会删除他所拥有的那个对象,所有我们就要注意了,一个萝卜一个坑,两个auto_ptr不能同时拥有同一个对象。像这样:
int* p = new int(0);
auto_ptr ap1(p);
auto_ptr ap2(p);
因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr.
-
2.auto_ptr对指针完全占有,也就是说一个”裸“指针不能同时被两个以上的auto_ptr所拥有。 因此在拷贝构造与赋值时:
-
- (1). 对智能指针进行赋值时,如ptest2 =ptest,ptest2会接管ptest原来的内存管理权,ptest会变为空指针,如果ptest2原来不为空,则它会释放原来的资源,基于这个原因,应该避免把auto_ptr放到容器中,因为算法对容器操作时,很难避免STL内部对容器实现了赋值传递操作,这样会使容器中很多元素被置为NULL。
缺陷
auto_ptr采用copy语义来转移指针资源,转移指针资源的所有权的同时将原指针置为NULL,这跟通常理解的copy行为是不一致的(不会修改原数据),而这样的行为在有些场合下不是我们希望看到的。
unique_ptr
unique_ptr是取代auto_ptr的产物,也是它的升级版。
unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权,包括:
- 拥有它指向的对象
- 无法进行复制构造,无法进行复制赋值操作。即无法使两个unique_ptr指向同一个对象。但是可以进行移动构造和移动赋值操作
- 保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象
功能
unique_ptr
可以实现如下功能:
- 为动态申请的内存提供异常安全
- 讲动态申请的内存所有权传递给某函数
- 从某个函数返回动态申请内存的所有权
- 在容器中保存指针
实验
#include<iostream>
#include<Windows.h>
#include<memory>
using namespace std;
class Test
{
public:
Test(string s)
{
str = s;
cout << "Test creat\n";
}
~Test()
{
cout << "Test delete:" << str << endl;
}
string& getStr()
{