本文阅读目录
1、智能指针设计思想
2、智能指针简单使用
3、为什么不建议使用auto_ptr
4、如何选择使用哪种智能指针
智能指针设计思想
在前面C/C++指针使用常见的坑一文中总结了C/C++中使用指针时可能会遇到的各种坑,其中最常见的是内存泄露,大家还记得下面这个例子吧。
在这个例子中,由于抛出异常,delete语句没有机会执行,造成内存泄露。当然对于这个几十行规模的程序来说,你可以在catch语句中再加一句delete t可以避免。
但是如果是一个几十W行的工程项目了?我想你一定会崩溃吧!那么有没有更好的办法呢?答案是肯定的。
我们知道,对于普通的局部变量(非静态局部变量),当离开它的作用域时,操作系统会自动将其释放的;并且我们也知道对于类对象在释放的时候是会自动调用该类的析构函数。
于是我们就想:如果是Test *t不是一个普通的指针变量,而是一个类对象的话,并且在类的析构函数中实现了释放动态内存的步骤;那么只要该指针变量一退出作用域时就会调用析构函数,达到了释放动态内存的目的。这就是智能指针的思想。
根据前面的设想,自己也不难实现一个简易的智能指针,如下:
class SmartPointer
{
private:
Test* ptr;
public:
SmartPointer(Test* p)
{
ptr = p;
}
~SmartPointer() //析构函数释放资源
{
delete p;
}
};
那么如何使用了,只需要在原来代码的基础上改动一点点即可,如下:
C++智能指针简单使用
当然,前面我们自己实现的智能指针只是为了说明智能指针设计的思想,是非常简陋的。在平时使用中是不会需要自己去定义智能指针的,C++已经给我们提供了现成的智能指针可以使用。
C++ STL为我们提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr和weak_ptr;其中auto_ptr是C++98提供,在C++11中建议摒弃不用,至于原因,后面会讲到;而unique_ptr、shared_ptr和weak_ptr则是随着C++11的到来而加入到STL中。
智能指针的使用其实并不难,如下所示:
为什么不建议使用auto_ptr
先来看看下面的语句:
auto_ptr<int> px(new int(8));
auto_ptr<int> py;
py = px;
上述赋值语句将完成什么工作呢?如果px和py是普通指针,则两个指针将指向同一个动态分配的int对象。这是不能接受的,因为程序可能将试图删除同一个对象两次——一次是px过期时,另一次是py过期时,我们知道,同一块内存是不能delete两次的。要避免这种问题,主要有以下两种方法:
-
建立所有权(ownership)概念。对于特定的对象,同一时刻只能有一个智能指针可拥有, 比如说当智能指针A指向对象x,当执行完B=A后,原来的指针A就失去了对x的所有权,这样只有拥有对象的智能指针的构造函数会删除该对象,unique_ptr和auto_ptr就是采用这种策略。
- 创建智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。例如,赋值时,计数将加1,而指针过期时,计数将减1,。当减为0时才调用delete。这是shared_ptr采用的策略。
当然,对于复制构造函数也应该采用同样的策略。
下面来看看在C++11中为什么建议摒弃auto_ptr,看下面的例子,
从程序的运行结果来看,当执行完赋值语句py = px后,再去访问px时程序崩溃了。原因就是因为赋值语句py = px使得对象的所有权从px转让给py了,px已经变为空指针了,再去访问px当然会出错了。
那么如果使用unique_ptr或者shared_ptr又会怎样了?还是测试下吧,先看unique_ptr情况:
看看,如果使用unique_ptr,在这种情况下编译会出错,也就是说尽管与auto_ptr一样,unique_ptr也采用所有权模型,但在使用unique_ptr时,程序不会等到运行阶段崩溃,在编译时就将可能潜在的错误暴露给你
好了,再看看shared_ptr情况:
使用shared_ptr时运行正常,因为shared_ptr采用引用计数,当执行完赋值语句py = px后,px和py都指向同一块内存,只不过在释放空间时因为事先要判断引用计数值的大小因此不会出现多次删除一个对象的错误。
如何选择哪种智能指针
在了解了这几种智能指针后,大家可能很自然的想到一个问题:在实际项目应用中,应使用哪种智能指针呢?根据这几种智能指针的特点,给出下面几点建议:
-
如果程序中要使用多个指向同一个对象的指针,那么应该使用shared_ptr;比如说现在有一个包含指针的STL容器,现在用某个支持复制和赋值操作STL算法去操作该容器的指针元素,那么就应该用shared_ptr。不能用unique_ptr(编译器报错)和auto_ptr(行为不确定)。
-
如果程序中不需要使用多个指向同一个对象的指针,则可使用unique_ptr;如果函数使用new分配内存,并返回指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。这样,所有权转让给接受返回值的unique_ptr,而该智能指针将负责调用delete。
- 在满足unique_ptr要求的条件时,也可使用auto_ptr,但unique_ptr是更好的选择。
总结
这篇文章只是从大体上总结了智能指针的基本用法,而在实际使用中还有许多值得注意的地方,后面会有文章继续总结这些细节,总之,学习C++的过程就是一个踩坑的过程。
推荐阅读:
【福利】自己搜集的网上精品课程视频分享(上)
【协议森林】邮差与邮局 (网络协议概观)
【数据结构与算法】 通俗易懂讲解 位排序
【C++札记】C++11并发编程(一)开启线程之旅
【C++札记】C/C++指针使用常见的坑
【C++札记】静态库和动态库详解(上)
码农有道 coding
码农有道,为您提供通俗易懂的技术文章,让技术变的更简单!