本文将结合C++11标准中的智能指针std::unique_ptr类的简单使用实例,讨论其基本原理,以期快速了解该智能指针类的使用。
std::unique_ptrlt;gt;是什么?std::unique_ptr是什么?
std::unique_ptr是C++语言中提供的一种智能指针类,使用它可以方便的管理指针,尽可能的避免内存泄漏。unique_ptr对象可以用于维护普通(常常用于索引一块内存)的指针,在其生命周期结束后,自动地删除它,并释放相关的内存。unique_ptr重载了-和*运算符,因此可以像使用普通指针那样使用unique_ptr智能指针。下面是一段简单的C++语言代码示例,请看:
C++代码示例这段C++语言代码很简单,main()函数首先创建了一个unique_ptr智能指针对象,newTask(23)本来需要一个Task*指针索引,现在直接使用unique_ptr对象taskPtr管理它,所以代码中没有使用delete删除相应的指针,因为“智能”指针对象taskPtr会在相关指针生命周期结束后自动地删除它。编译这段C++语言代码时,记得指定-std=c++11选项,最终得到的输出如下:
#g++t1.cpp-std=c++11
#./a.out
Task::Constructor
23
Task::Destructor
事实上,不管函数是正常还是不正常(程序抛出异常等)的退出,taskPtr的析构函数总是会被调用的,因此,taskPtr管理的raw指针会被自动删除,避免内存泄漏。
unique_ptr的“专享所有权”
unique_ptr中的“unique”一词有着“唯一的,独一无二”的意思,这主要体现在所有权上,某个raw指针同时只能被一个unique_ptr指针绑定,我们不能拷贝unique_ptr对象,只能转移。事实上,鉴于unique_ptr对象相对于其管理的raw指针的独一无二特性,其内部不需要像shared_ptr智能指针类那样需要“引用计数”机制,一旦unique_ptr对象的析构函数被调用,它就会删除掉绑定的raw指针。
使用unique_ptrlt;gt;智能指针类创建一个空unique_ptr对象
std::unique_ptr本质上是一个在标准命名空间std中的模板类,使用它需要包含头文件memory,例如定义一个可以绑定int指针的对象的C++语言代码可以如下写:
#includememorystd::unique_ptrintptr;
还可以在定义unique_ptr对象的时候传入需要与之绑定的“raw指针”,这一点在前面的C++语言代码实例中已经见过:
std::unique_ptrTasktaskPtr(newTask(23));
需要注意,不能直接把raw指针直接赋值给unique_ptr对象,也就是说下面这行C++语言代码是非法的:
taskPtr=newTask(23);//非法检查unique_ptr对象是否为空
上面定义的unique_ptr对象ptr没有与任何raw指针绑定,因此它是空的。检查unique_ptr对象是否为空,一般有两种方法,相关的C++语言代码示例如下,请看:
方法1:if(!ptr)
std::coutptrisemptystd::endl;方法2:if(ptr==nullptr)
std::coutptrisemptystd::endl;
重置(reset)unique_ptr
调用unique_ptr的reset()方法将删除与之绑定的raw指针,并且将unique_ptr对象置空:
taskPtr.reset();
//taskPtr==nullptr
为真上面这行C++语言代码执行后,与taskPtr绑定的raw指针将会被删除,并且taskPtr被置空,也即taskPtr==nullptr为真。
unique_ptr对象不可拷贝
鉴于unique_ptr不可拷贝,只能移动,所以我们不能通过拷贝构造函数活着赋值操作拷贝unique_ptr对象,下面这两行C++语言代码都是非法的:
std::unique_ptrTasktaskPtr2=taskPtr;//非法
taskPtr=taskPtr2;//非法转移
unique_ptr对象的所有权
如前文所述,我们不能拷贝unique_ptr对象,却可以移动它,所谓“移动”,其实就是转移所有权,请看下面这个示例,首先创建一个unique_ptr对象:
std::unique_ptrTasktaskPtr2(newTask(55));
此时taskPtr2显然不为空。现在将其对绑定raw指针的所有权转移到一个新的unique_ptr对象,相关的C++语言代码可以如下写:
std::unique_ptrTasktaskPtr4=std::move(taskPtr2);
if(taskPtr2==nullptr)
std::couttaskPtr2isemptystd::endl;//转移给taskPtr4
if(taskPtr4!=nullptr)
std::couttaskPtr4isnotemptystd::endl;
std::move()函数将taskPtr2转换为右值(rvalue)引用,所以unique_ptr的移动构造函数可以将与taskPtr2绑定的raw指针转移给taskPtr4,在这之后,taskPtr2变为空。
释放对绑定raw指针的所有权
unique_ptr的release()函数可以直接将对绑定raw指针的所有权释放,该函数会将绑定的raw指针返回,请看下面的C++语言代码示例:
std::unique_ptrTasktaskPtr5(newTask(55));
if(taskPtr5!=nullptr)
std::couttaskPtr5isnotemptystd::endl;//释放所有权
Task*ptr=taskPtr5.release();
if(taskPtr5==nullptr)
std::couttaskPtr5isemptystd::endl;
执行完上面的代码后,taskPtr5变为空,并且其绑定的raw指针被赋值给ptr。
完整示例
下面以一段完整的C++语言代码示例结束本文:
完整示例
同样的,编译时需要指定-std=c++11,最终输出如下,请看:
#g++t2.cpp-std=c++11
#./a.out
ptr1isempty
ptr1isempty
Task::Constructor
taskPtrisnotempty
23
Task::Destructor
ResetthetaskPtr
taskPtrisempty
Task::Constructor
taskPtr2isempty
taskPtr4isnotempty
Task::Destructor
Task::Constructor
taskPtr5isempty
55
Task::Destructor
小结
本文主要讨论了C++11标准中的智能指针unique_ptr类的基本使用和一些相