大家好,我是小牛,今天跟聊一下 BAT 面试 C++ 开发工程师必问的一个考点:智能指针。
小艾:你昨晚面 C++ 去了?
小牛:对啊,不是这个厂主要技术栈都是 C++ 嘛,我就面去了。
小艾:问了点啥啊?
小牛:BAT 这 C++ 问的都差不多,又问智能指针了。
小艾:那来讲讲呗。
小牛:来。
智能指针的引入
大家都知道,指针是 C++ 中非常重要的一部分,大家在初期学习 C++ 的时候一定学过类似这样的指针方式。
int *ptr;
这种指针也被称为裸指针。但是使用裸指针会存在一些不足:
如果使用裸指针分配内存后,忘记手动释放资源,会出现内存泄漏。
如果使用多个裸指针指向同一资源,其中一个指针对资源进行释放,其它指针成为空悬指针,如果再次释放会存在不可预测的错误。上图中当 sp1 把资源释放后,sp2 成了空悬指针。空悬指针指的是指针所指向的对象已经释放的时候自身却没有被置为
nullptr
。sp1 通过free/delete
释放资源的内存时,内存不会立刻被系统回收,而是状态改变为可被其它地方申请的状态。这时当再次操作 sp2,这块内存可能被其它地方申请了,而具体被谁申请了是不确定的,因此可能导致的错误也是不可预测的。如果程序异常退出时,裸指针的释放资源的代码未能执行,也会造成内存泄漏。
为了改善裸指针的不足,确保资源的分配和释放是配对的,开发者提出了智能指针。智能指针主要是对裸指针进行了一次面向对象的封装,在构造函数中初始化资源地址,在析构函数中释放资源。 当资源应该被释放时,指向它的智能指针可以确保自动地释放它。
C++ 库中,为智能指针提供了不带引用计数和带引用计数的两种方案。
引用计数用于表示有多少智能指针引用同一资源。不带引用计数的智能指针采用独占资源的方式,而带引用计数的智能指针则可以同时多个指向同一资源。下面介绍一下它们的主要特点和区别。
不带引用计数的智能指针
不带引用计数的智能指针包括 auto_ptr
、scoped_ptr
和 unique_ptr
三种指针。
1. auto_ptr:
我们先来看个例子:
#include
int main(){
auto_ptr<int> ptr(new int(6));//定义auto_ptr指针ptr
auto_ptr<int> ptr1(ptr); //拷贝构造ptr定义ptr1
*ptr=8;//对空指针ptr赋值会产生不可预料的错误
return 0;
}
开始时 ptr 指向资源,一个整型数字6,当用 ptr1 拷贝构造 ptr 时,ptr1 指向资源,而 ptr 则指向 nullptr
。下一行程序中如果对空指针 ptr 赋值 8,将会产生不可预料的错误。
下图表示 auto_ptr
指针对资源的指向过程。
使用拷贝构造时,如果只有最后一个 auto_ptr
持有资源,其余 auto_ptr
持有的资源会被置为 nullptr
。
因此需要注意,不能在容器中使用 auto_ptr
,当容器发生拷贝时,原容器中 auto_ptr
持有的资源会置 nullptr
。
下面我们再来看一下 auto_ptr
的部分源码和部分解析:
template<class _Ty>class auto_ptr
{
public:
typedef _Ty element_type;
explicit auto_ptr(_Ty * _Ptr=nullptr) noexcept
: _Myptr(_Ptr)//初始化列表{ //构造函数
}
auto_ptr(auto_ptr& _Right) noexcept
: _Myptr(_Right.release())
{ //拷贝构造函数,会调用release()函数
}
_Ty * release() noexcept{
/*使用拷贝构造时,最后一个auto_ptr持有资源,
其余被置为nullptr*/
_Ty * _Tmp = _Myptr;
_Myptr = nullptr;
return (_Tmp);
}
private:
_Ty * _Myptr;//指向资源
};
当试图调用 auto_ptr
的拷贝构造函数时,在初始化列表中调用了 release()
函数,release()