c++智能指针:auto_ptr shared_ptr

1. 简介

C++程序员最痛苦的莫过于对内存的管理,由于没有像C# 和 java的内存回收机制,C++程序员在复杂的程序中容易造成内存的泄露。即使程序员十分小心,异常的产生也有可能会造成部分内存的泄露(由于异常导致delete语句没有机会执行)。为了避免该问题,出现了各种类型的智能指针。只能指针实质就是重载了-> 和 * 操作符的类,由类来实现对内存的管理,确保即使有异常产生,也可以通过智能指针类的析构函数完成内存的释放。
旧版本C++为用户提供了 auto_ptr 智能指针,但由于其功能的限制以及自身的缺陷,完全没有发挥智能指针的强大功能,各种第三方的类库提供了远比C++标准库丰富的智能指针类。例如boost库,提供了 scoped_ptr, shared_ptr, weak_ptr等。 C++11 借鉴boost库,新引入了几个十分重要智能指针,shared_ptr, unique_ptr, weak_ptr。 丰富了标准库在智能指针方面的缺陷。

使用智能指针需要包含头文件

2. auto_ptr

2.1 定义

MSDN对 auto_ptr 的定义为:

The template class describes an object that stores a pointer to anallocated object of type Type * that ensures the object to which itpoints gets deleted when its enclosing auto_ptr gets destroyed.

2.2 一般用法

auto_ptr 对象将会绑定一个指定类型的指针,并且负责该指针资源的释放。auto_ptr 的基本使用方法如下所示:

[cpp]  view plain copy
  1. struct MyStruct  
  2. {  
  3.     MyStruct(){cout<<"MyStruct()\n";}  
  4.     ~MyStruct(){cout<<"~MyStruct()\n";}  
  5.     int i;  
  6.     int j;  
  7. };  
  8. auto_ptr pStruct(new MyStruct);  
  9. pStruct->i = 1;  
  10. pStruct->j = 2;  
  11. (*pStruct).i = 3;  
  12. (*pStruct).j = 4;  

如上所示 

auto_ptrpStruct(new MyStruct);

定义了一个智能指针对象 pStruct, 该对象用于管理一个类型为 MyStruct 的指针。在auto_ptr类里,有一个指针成员变量,由该成员变量保存指针的地址。使用智能指针和使用一般的指针相同,因为其重载了 -> 操作符,例如

[cpp]  view plain copy
  1. pStruct->i = 1;  
  2. pStruct->j = 2;  

-> 操作符用于返回指针。智能指针类还重载了 * 操作符,用于返回对象的引用,例如

[cpp]  view plain copy
  1. (*pStruct).i = 3;  
  2. (*pStruct).j = 4;  

(*pStruct) 返回的是对象的引用,因此可以使用 (*pStruct).j 来访问该对象的成员。

2.3 资源管理

auto_ptr 智能管理从new 申请的一个对象资源,而不能管理动态分配的数组,例如:

[cpp]  view plain copy
  1. struct MyStruct  
  2. {  
  3.     MyStruct(){cout<<"MyStruct()\n";}  
  4.     ~MyStruct(){cout<<"~MyStruct()\n";}  
  5.     int i;  
  6.     int j;  
  7. };  
  8.       
  9. {  
  10.     //compile OK, but we cannot use auto_ptr this way!  
  11.     auto_ptr pStructArr(new MyStruct[5]);  
  12. }  

如上例所示了,为pStructArr绑定了一个动态分配的数组,编译器在编译阶段不会报错,但是由于auto_ptr内部维护的指针资源只能管理一个对象,在释放资源时使用的是delete而不是 delete[], 因此这种使用方式会导致资源的泄露。

切记: auto_ptr只能用来管理一个对象的指针。

2.4 赋值和复制

使用auto_ptr 的复制和赋值要格外小心,对于普通的指针,进行复制(或赋值)时,实际是复制(或赋值)指针的地址,通过复制(或赋值),使得两个指针指向同一个地址。但是auto_ptr 却不同,在复制(或赋值)auto_ptr 对象之后,原来的auto_ptr 对象(右侧的对象)放弃资源的使用权,而新的auto_ptr 对象(左侧的)则拥有该基础对象的使用权。例如:

[cpp]  view plain copy
  1. auto_ptr<int> pInt(new int(3));  
  2. auto_ptr<int> pInt2 = pInt;  

首先pInt用于管理动态分配的整型指针,pInt2 = pInt进行复制后,pInt 被重置为未绑定的状态,而pInt2 此时拥有了对整型指针的管理权。需要注意的是,在复制或赋值时,如果pInt2 已经绑定了一个指针,那么会首先释放该指针的资源,然后再绑定新的资源。

因此,auto_ptr的赋值或复制要格外小心。

2.5 测试和reset

当需要判断auto_ptr 对象是否绑定一个指针时,使用 get() 函数进行判断

[cpp]  view plain copy
  1. auto_ptr<int> pInt(new int(3));  
  2. if( pInt.get() )  
  3.     cout<< *pInt<< endl;  
  4.   
  5. //error C2451: conditional expression of type 'std::auto_ptr<_Ty>' is illegal  
  6. if( pInt )  
  7.     cout<< *pInt << endl;  

get() 用于返回 auto_ptr 管理的指针对象,因此判断是否绑定了指针要使用 obj.get() 的方式,auto_ptr 对象不能直接判断,如上例的 if( pInt ) 所示,编译器提示的错误如注释所示。

如果想要改变auto_ptr 管理的指针,使用 reset 函数,例如:

[cpp]  view plain copy
  1. #include"stdafx.h"  
  2. #include  
  3. #include  
  4. #include  
  5. using namespace std;  
  6.   
  7. int main()  
  8. {  
  9.     struct MyStruct  
  10.     {  
  11.         MyStruct(int ii) : i(ii) {cout<<"MyStruct() i="<< i << endl;}  
  12.         ~MyStruct(){cout<<"~MyStruct()i="<< i << endl;;}  
  13.         int i;  
  14.     };  
  15.   
  16.     auto_ptr pMyStruct(new MyStruct(5));  
  17.     pMyStruct.reset(new MyStruct(10));  
  18.       
  19.     system("pause");  
  20.     return 0;  
  21. }  

输出结果如下图所示:



由输出结果可以看出,

pMyStruct.reset(newMyStruct(10));

将auto_ptr对象重新绑定了新的指针。需要注意的,原绑定的资源在auto_ptr重新绑定资源后得到了释放!这就是 auto_ptr 的特性,一旦改变资源,原资源就会被释放。

 

根据auto_ptr的特点,在使用auto_ptr 时遵循如下的原则:

(1)不要使用auto_ptr 绑定指向静态分配对象的指针。

(2)不要使用两个auto_ptr 对象指向同一个对象。

(3)不要使用auto_ptr 对象保存指向动态分配数组的指针。

(4)不要将auto_ptr 对象存储在容器中,因为它不满足容易对存储对象赋值和复制的条件要求。

 

建议 auto_ptr 在函数体内动态申请局部变量的指针并且不需要进行指针的复制、赋值时使用。


3. shared_ptr

3.1 定义

CSDN 对 shared_ptr 的说明如下:

The template class describes an object that uses reference counting to manage resources. A shared_ptrobject effectively holds a pointer to the resource that it owns or holds a null pointer. A resource can be owned by more than one shared_ptr object; when the last shared_ptr object that owns a particular resource is destroyed, the resource is freed.

由定义可以看出,shared_ptr 使用引用计数的方式来实现对指针资源的管理。同一个指针资源,可以被多个 shared_ptr 对象所拥有,直到最后一个 shared_ptr 对象析构时才释放所管理的对象资源。

可以说,shared_ptr 是最智能的智能指针,因为其特点最接近原始的指针。不仅能够自由的赋值和拷贝,而且可以安全的用在标准容器中。


3.2 一般用法

shared_ptr 和 auto_ptr 一样,也是重载了 -> 和 * 操作符,使用 get() 函数可以得到原始的指针,auto_ptr 提供了隐式的bool类型转换,可以直接用于判断auto_ptr对象是否绑定了指针资源。 use_cout() 用于返回当前资源的引用计数。

[cpp]  view plain copy
  1. #include"stdafx.h"  
  2. #include  
  3. #include  
  4. #include  
  5. using namespace std;  
  6.   
  7. int main()  
  8. {  
  9.     struct MyStruct  
  10.     {  
  11.         MyStruct(int ii) : i(ii) {cout<<"MyStruct() i="<< i << endl;}  
  12.         ~MyStruct(){cout<<"~MyStruct()i="<< i << endl;;}  
  13.         int i;  
  14.     };  
  15.   
  16.     {  
  17.   
  18.         //spMyStruct 维护一个MyStruct 类型的指针  
  19.         shared_ptr spMyStruct(new MyStruct(10));   
  20.   
  21.         //与 auto_ptr 不同的是 shared_ptr 提供了隐式的bool类型转换,用于判断是否已绑定了资源  
  22.         if(spMyStruct)  
  23.         {  
  24.             cout << "1. Use count = " << spMyStruct.use_count() << endl;  
  25.           
  26.             shared_ptr spMyStruct2 = spMyStruct;  //赋值  
  27.             cout<< "2. Use count = " << spMyStruct2.use_count() << endl;  
  28.   
  29.             shared_ptr spMyStruct3(spMyStruct2);  //拷贝  
  30.             cout<< "3. Use count = " << spMyStruct3.use_count() << endl;  
  31.   
  32.             spMyStruct2.reset();  
  33.             cout<< "4. After reset, use count = " << spMyStruct.use_count() << endl;  
  34.   
  35.   
  36.         }  
  37.         cout << "5. spMyStruct2 and spMystruct3 out of scope:" << endl;  
  38.         cout << "6. use count = " << spMyStruct.use_count() << endl;  
  39.   
  40.         cout <<"7. spMyStruct out of scope:" << endl;  
  41.     }  
  42.   
  43.     system("pause");  
  44.     return 0;  
  45. }  

输出结果如下图所示:

c++智能指针:auto_ptr shared_ptr_第1张图片


由输出结果可以看出,对auto_ptr进行复制或者拷贝,实质是增添了引用计数, 和 auto_ptr 不同的是, reset() 函数在 shared_ptr 中的作用是对引用计数减1,并不会立即释放资源。知道引用计数减为0时,资源才得到真正的释放。


use_count()在实际的应用中很少使用,一般是用于调试,shared_ptr提供了一个高效的函数 unique(),同

[cpp]  view plain copy
  1. use_count==1  
等效,即,当维护的指针资源引用计数为1时,返回true.


3.3 类型转换


shared_ptr 的类型转换不能使用一般的static_cast,这种方式进行的转换会导致转换后的指针无法再被shared_ptr对象正确的管理。应该使用专门用于shared_ptr类型转换的 static_pointer_cast() , const_pointer_cast() 和 dynamic_pointer_cast().例如

[cpp]  view plain copy
  1. #include"stdafx.h"  
  2. #include  
  3. #include  
  4. #include  
  5. using namespace std;  
  6.   
  7. class BaseClass  
  8. {  
  9. public:  
  10.     BaseClass(){}  
  11.     ~BaseClass(){}  
  12. public:  
  13.     virtual void Func() { cout<<"BaseClass::Func()\n";}  
  14. };  
  15.   
  16. class DeriveClass : public BaseClass  
  17. {  
  18. public:  
  19.     DeriveClass(){}  
  20.     ~DeriveClass(){}  
  21. public:  
  22.     virtual void Func() override { cout << "DeriveClass::Func()\n";}  
  23. };  
  24.   
  25. int main()  
  26. {  
  27.     shared_ptr spBase( new BaseClass() );  
  28.     spBase->Func(); // 输出 BaseClass::Func()  
  29.   
  30.     shared_ptr spDerive( new DeriveClass() );  
  31.     spDerive->Func();// 输出 DeriveClass::Func()  
  32.   
  33.     shared_ptr spBase2 = dynamic_pointer_cast(spDerive);  
  34.     spBase2->Func();// 输出 DeriveClass::Func()  
  35.   
  36.     system("pause");  
  37.     return 0;  
  38. }  

3.4 make_shared

使用shared_ptr 避免了手动使用delete来释放由new 申请的资源,标准库也引入了make_shared函数来创建一个shared_ptr对象,使用shared_ptr 和 make_shared ,你的代码里就可以使 new 和 delete 消失,同时又不必担心内存的泄露。例如:

[cpp]  view plain copy
  1. shared_ptr<int> spInt = make_shared<int>(10);  

通过make_shared创建了一个整型变量的shared_ptr对象。至于资源的管理,就交给shared_ptr了!


3.5 总结

shared_ptr 是最为智能的智能指针,使用shared_ptr 和 make_shared,可以使你的C++代码几乎永远的和new, delete说再见! shared_ptr可以用在各种场合,局部变量,类的成员变量,参数,容器的元素!你不必担心资源的释放,不必担心内存的泄露!学会并熟练使用shared_ptr,提高开发效率吧。



你可能感兴趣的:(C++专栏)