一,智能指针
智能指针是行为类似于指针的类对象,使用智能指针可帮助管理动态分配的内存与使用new创建的对象。
二,智能指针模板类
C++中提供的智能指针模板有auto_ptr、unique_ptr、shared_ptr和weak_ptr。可以将new获得的地址赋给智能指针对象。当智能指针过期时,其析构函数将使用delete来释放内存。因此,如果将new返回的地址赋给智能指针对象,将无需记住稍后释放这些内存,当智能指针过期时,这些内存会自动被释放。智能指针对象的很多方面都类似于常规指针,例如,可以对它执行解除引用操作(*p)、访问结构成员(p->data)、将它赋给相同类型的智能指针。但是将智能指针对象赋值个另一个智能指针对象,将引发一些问题。
三,智能指针的应用
要创建智能指针对象,必须包含头文件memory。例如,模板auto_ptr包含如下构造函数
template<class X>
class auto_ptr{
public:
explicit auto_ptr(X *p = 0) throw();
...
};
1,使用智能指针来管理动态分配的内存
下面是使用智能指针的例子
auto_ptr<int> pInt(new int);
下面是不使用智能指针的例子
int *p = new int;
...
delete p;
上面的指针变量p是一个普通的指针,当p的生命周期结束时,指针p分配的内存会被释放,但是p指向的内存空间不会被释放,因此,要手动调用delete,释放掉动态分配的内存空间。但pInt是一个智能指针,它是一个对象,当它的生命周期结束时,会调用它的析构函数,在析构函数中会释放掉动态分配的内存。
2,使用智能指针来管理用new创建的对象
class Resource{
public:
Resource(){
cout<<"object created."<<endl;
}
~Resource(){
cout<<"object delete."<<endl;
}
};
int main(){
auto_ptr<Resource> p1(new Resource);
shared_ptr<Resource> p2(new Resource);
unique_ptr<Resource> p3(new Resource);
}
输出结果
object created.
object delete.
object created.
object delete.
object created.
object delete.
Process returned 0 (0x0) execution time : 0.039 sPress any key to continue.
3,使用智能指针应注意的问题
Resource res;
auto_ptr<Resource> p1(res);
p1过期时,程序将把delete运算符用于非堆内存,这是错误的。
1,智能指针赋值带来的问题
auto_ptr<Resource> p1(new Resource);
auto_ptr<Resource> p2;
p2 = p1;
如果p1与p2是常规指针,则两个指针将指向同一个Resource对象。但是对于智能指针来说这是不能接受的,因为程序将试图删除同一个对象两次,一次是p1过期时,另一次是p2过期时。要避免这种问题,方法有多种:a,建立所有权概念:对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后,让赋值操作转让所有权。这就是用于auto_ptr与unique_ptr的策略,但unique_ptr的策略更加严格。
b,创建更高智能的指针,跟踪引用特定对象的智能指针数,这称为引用计数。例如,赋值时,引用计数加1,而指针过期时,计数将减1。仅当最后一个指针过期时,才调用delete,这是shared_ptr采用的策略。
2,错误使用auto_ptr带来的问题
auto_ptr<string> p1(new string("hello world."));
auto_ptr<string> p2;
p2 = p1;
cout<<*p2<<endl;
cout<<*p1<<endl;
上面的程序在运行时会出错,出错的原因是,赋值语句p2 = p1将所有权从p1转到p2,这导致p1不再引用该字符串。在auto_ptr类型的指针放弃对象的所有权之后,便可能使用它来访问该对象,却发现这个智能指针是空的,所以会导致上面的问题。unique_ptr与auto_ptr都是采用所有权模型,如果用unique_ptr代替auto_ptr也会出错。但使用unique_ptr时,程序不会等到运行期崩溃,而在编译器发现赋值操作p2 = p1,将报错。3,使用shared_ptr代替auto_ptr解决上面的问题
shared_ptr<string> p1(new string("hello world."));
shared_ptr<string> p2;
p2 = p1;
cout<<*p2<<endl;
cout<<*p1<<endl;
上面的程序正常运行,这次p1与p2指向同一个对象,对象的引用计数由1增加到2。在程序末尾,p2的生命周期结束时,会调用它的析构函数,此时对象的引用计数降低为1。然后p1的生命周期结束,会调用它的析构函数,此时对象的引用计数降低为0,释放掉为对象分配的内存空间。
五,unique_ptr为何优于auto_ptr?
1,unique_ptr比auto_ptr更安全
a,使用auto_ptr赋值
auto_ptr<string> p1(new string("hello world."));
auto_ptr<string> p2;
p2 = p1;
在上面的赋值语句中,p2接管string对象的所有权,p1的所有权将被剥夺。这样做可以防止p1与p2的析构函数试图删除同一个对象两次。但如果程序随后使用p1,这将出错。b,使用unique_ptr赋值
unique_ptr<string> p1(new string("hello world."));
unique_ptr<string> p2;
p2 = p1;
编译器认为赋值语句p2 = p1非法,避免了p1不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全,编译阶段出错误比潜在的程序崩溃更安全。2,unique_ptr可用于数组
模板auto_ptr使用delete而不是delete [],因此只能与new一起使用,而不能与new []一起使用。但unique_ptr有使用new []与 delete []版本。
unique_ptr<double []> p(new double(2));
六,选择智能指针的指导原则
1,如果程序需要使用多个指向同一个对象的指针,应选择使用shared_ptr。
2,如果程序不需要使用多个指向同一个对象的指针,则可使用unique_ptr。
七,weak_ptr
a,weak_ptr的作用
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->。它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
b,weak_ptr的用法
1,weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
2,使用weak_ptr的成员函数use_count(),可以观测资源的引用计数。
3,使用weak_ptr的成员函数expired(),可以判断引用计数是否为0。如果引用计数为0,返回true;否则返回false。
4,使用weak_ptr一个非常重要的成员函数lock(),从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。
c,weak_ptr的使用
shared_ptr<int> sp(new int);
weak_ptr<int> wp(sp);
if (!wp.expired()){
shared_ptr<int> sp2 = wp.lock();
*sp2 = 100;
}
cout<<*sp2<<endl;