智能指针是行为类似于指针的类对象,但这种对象还有其他功能。使用指针指向一块新申请的内存的过程中,有时忘记释放新申请的内存,导致内存泄漏。为了防止该问题的发生,C++提供了智能指针模板类。其思想就是将常规的指针变成一个类对象,该对象主要实现常规指针的功能,当该对象过期的时候,会自动调用其析构函数,在析构函数中完成内存释放的操作。
智能指针有auto_ptr
、unique_ptr
、shared_ptr
和weak_ptr
(本章暂时不做详细介绍),其中auto_ptr
在C++11中摒弃。
class A{...};
void fun()
{
A* pa = new A;
std::auto_ptr<A> p1 = new A;
std::unique_ptr<A> p2 = new A;
std::shared_ptr<A> p3 = new A;
...
delete pa; //必须在pa过期之前释放分配的内存
//delete p1;//不需要手动释放内存,在p1过期的时候,会自动调用其析构函数释放对应的内存块
//delete p2;//不需要手动释放内存,在p2过期的时候,会自动调用其析构函数释放对应的内存块
//delete p3;//不需要手动释放内存,在p3过期的时候,会自动调用其析构函数释放对应的内存块
}
常规的指针在使用的过程中两个指针可能会指向同一个对象,这是不能接受的,因为程序在结束的时候会存在删除同一对象两次的问题,如下例:
std::string* ps (new std::string("abc"));
std::string* p1;
p1 = pa;
...
delete ps;
delete pa;
针对上述问题,auto_ptr
和unique_ptr
在常规指针的基础上增加了所有权的概念,对于特定对象,只有一个指针可以拥有它,这样只有拥有对象的智能指针才能删除该对象。
而shared_ptr
采用跟踪引用特定对象的智能计数(即引用计数)策略。例如,赋值的时候,计数加1,而指针过期的时候,计数减1,仅当最后一个指针过期的时候才调用delete
。
下面我们举个例子,看一下他们之间的区别。
auto_ptr<std::string> p1(new std::string("abc"));
auto_ptr<std::string> p2;
p2 = p1; //p2接管string对象的所有权后,p1的所有权被剥夺,其不再指向有效数据,后面如果尝试调用p1,程序运行的时候则会导致程序core dumped
unique_ptr<std::string> p1(new std::string("abc"));
unique_ptr<std::string> p2;
p2 = p1; //p2接管string对象的所有权后,p1的所有权被剥夺,其不再指向有效数据,后面如果尝试调用p1,则编译失败
shared_ptr<std::string> p1(new std::string("abc"));//引用计数器为1
shared_ptr<std::string> p2;
p2 = p1; //引用计数器为2
//在p1、p2过期的过程中,计数器相应减1,当最后一个智能指针过期的时候,调用修购函数时,将引用计数器降低到0,并释放对应的空间
从上面的例子可以看出unique_ptr
与auto_ptr
最大的区别就在于当一个智能指针的所有权被剥夺后,若后面的程序要调用它的时候,unique_ptr
直接在编译阶段就失败,将问题暴露出来,而auto_ptr
编译阶段不会报错,在程序运行的时候出现异常,因此unique_ptr
的安全性更高(编译阶段错误比程序崩溃更安全)。
相比auto_ptr
,unique_ptr
另外一个优点就是它有一个可以用于数组的变体。我们知道delete
与new
配对,delete[]
与new[]
配对,而auto_ptr
中使用的是delete
,而不是delete[]
,因此只能与new
一起使用,不能与new[]
一起使用。但unique_ptr
有delete
和delete[]
两个版本,它可以与new
一起使用,也可以与new[]
一起使用。同样的,shared_ptr
也支持与new
一起使用,不支持new[]
的使用。
注:使用new
分配内存时,可以使用auto_ptr
、unique_ptr
、shared_ptr
;使用new[]
分配内存时,不能使用auto_ptr
和shared_ptr
,可以使用unique_ptr
;不使用new
和new[]
分配内存时,不能使用unique_ptr
。
shared_ptr
。unique_ptr
。