智能指针详解

重点:学习智能指针,看这偏博客就够了,讲得很详细,特别用心去写的博客

原文出自:http://www.cnblogs.com/Lynn-Zhang/p/5699983.html

智能指针 auto_ptr、scoped_ptr、shared_ptr、weak_ptr

  什么是RAII?

RAII是Resource Acquisition Is Initialization的简称,是C++语言的一种管理资源、避免泄漏的惯用法。

RAII又叫做资源分配即初始化,即:定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

  为什么要使用RAII?

在计算机系统中,资源是数量有限且对系统正常运行具有一定作用的元素。比如:网络套接字、互斥锁、文件句柄和内存等等,它们属于系统资源。由于系统的资源是有限的,所以,我们在编程使用系统资源时,都必须遵循一个步骤:
  1. 申请资源;
  2. 使用资源;
  3. 释放资源。

第一步和第三步缺一不可,因为资源必须要申请才能使用的,使用完成以后,必须要释放,如果不释放的话,就会造成资源泄漏。

  什么是智能指针?

 所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放。它是一个类,有类似指针的功能。 

  智能指针的实现原理

  当类中有指针成员时,一般有两种方式来管理指针成员:
  • 一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝;
  • 另一种更优雅的方式是使用智能指针,从而实现指针指向的对象的共享。
  智能指针(smart pointer)的通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象。每次创建类的新对象时,初始化指针就将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,析构函数减少引用计数(如果引用计数减至0,则删除基础对象)。

  常见的智能指针

包括:std::auto_ptrboost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost::intrusive_ptr

Boost库的智能指针(ps:新的C++11标准中已经引入了unique_ptr/shared_ptr/weak_ptr):

 

  auto_ptr  独占所有权,转移所有权 

第一种实现:最开始auto_ptr的成员变量主要有T* _ptr 和 bool _owner,主要实现原理是在构造对象时赋予其管理空间的所有权,在拷贝或赋值中转移空间的所有权,在析构函数中当_owner为true(拥有所有权)时来释放所有权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
template < typename  T>
class  AutoPtr
{
public :
     //构造函数
     explicit  AutoPtr(T* ptr = NULL)
         :_ptr(ptr)
         , _owner( true )
     {}
     //拷贝构造
     AutoPtr(AutoPtr<T>& ap)  //参数不能写成const的,这里要修改ap对象的成员<br>       :_ptr(ap._ptr)
         , _owner( true )
     {
         ap._owner =  false ;   //转让权限
     }
     //赋值运算符重载
     AutoPtr& operator=(AutoPtr<T>& ap)
     {
         if  ( this ! = &ap)
         {
             delete  this ->_ptr;
             this ->_ptr = ap._ptr;
             // 转让权限
             this ->_owner =  true ;
             ap._owner =  false ;
         }
         return  * this ;
     }
     //析构函数
     ~AutoPtr()
     {
         // 只删除拥有权限的指针
         if  (_owner)
         {
             this ->_owner =  false ;
             delete  this ->_ptr;
         }
     }
 
     T& operator*()
     {
         return  *( this ->_ptr);
     }
 
     T* operator->()
     {
         return  this ->_ptr;
     }
 
     T* AutoPtr<T>::GetStr()
     {
         return  ( this ->_ptr);
     }
protected :
     T* _ptr;
     bool  _owner;  //权限拥有者
};

 出现的主要问题如果拷贝出来的对象比原来的对象先出作用域或先调用析构函数,则原来的对象的_owner虽然为false,但却在访问一块已经释放的空间,原因在于拷贝对象的释放会导致原对象的_ptr指向的内容跟着被释放!

问题体现如下

ap1将析构的权限给予了ap2,由于ap1._ptr和ap2._ptr指向同一份空间,ap2在出了if作用域之后自动被释放,进而导致ap1._ptr也被释放。

但是在if作用域之外,又对ap1._ptr进行访问,导致程序崩溃,这时候 ap1._ptr已经是一个野指针了,这就造成指针的悬挂的问题!

1
2
3
4
5
6
7
8
9
10
11
void  Test()
{
         AutoPtr< int > ap1( new  int (1));
         if  ( true )
         {
             AutoPtr< int > ap2(ap1);
         }
 
         //这里的ap1._ptr已经是一个野指针了,这就造成指针的悬挂的问题
         *(ap1.GetStr() )= 10;
}

  auto_ptr的第二种实现方法:还是管理空间的所有权转移,但这种实现方法中没有_owner权限拥有者。构造和析构和上述实现方法类似,但拷贝和赋值后直接将_ptr赋为空,禁止其再次访问原来的内存空间,比较简单粗暴。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
template < typename  T>
class  AutoPtr
{
public :
     //构造函数
     explicit  AutoPtr(T* ptr = NULL)   //不能写成const T* ptr,否则出现const类型赋值给非const类型的问题
         :_ptr(ptr)
     {}
     //拷贝构造
     AutoPtr(AutoPtr<T>& ap)
         :_ptr(ap._ptr)
     {
         ap._ptr=NULL; 
     }
     //赋值运算符重载
     AutoPtr& operator=(AutoPtr<T>& ap)
     {
         if  ( this ! = &ap)
         {
             delete  this ->_ptr;
             this ->_ptr = ap._ptr;
             ap._ptr = NULL;
         }
         return  * this ;
     }
     //析构函数
     ~AutoPtr()
     {
         // 只删除拥有权限的指针
         if  (_ptr)
         {
             delete  _ptr;
         }
     }
 
     T& operator*()
     {
         return  *_ptr;
     }
 
     T* operator->()
     {
         return  _ptr;
     }
 
     T* GetStr()
     {
         return  _ptr;
     }
protected :
     T* _ptr;
};

  这种实现方式很好的解决了旧版本野指针问题,但是由于它实现了完全的权限转移,所以导致在拷贝构造和赋值之后只有一个指针可以使用,而其他指针都置为NULL,使用很不方便,而且还很容易对NULL指针进行解引用,导致程序崩溃,其危害也是比较大的。

  scoped_ptr  独占所有权,防拷贝

scoped_ptr的实现原理是防止对象间的拷贝与赋值。具体实现是将拷贝构造函数和赋值运算符重载函数设置为保护或私有,并且只声明不实现,并将标志设置为保护或私有,防止他人在类外拷贝,简单粗暴,但是也提高了代码的安全性。  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
template < typename  T>
class   ScopedPtr
{
public :
     ScopedPtr(T* ptr = NULL) 
         :_ptr(ptr)
     {}
 
     T& operator*()
     {
         return  *_ptr;
     }
 
     T* operator->()
     {
         return  _ptr;
     }
 
     T* GetStr()
     {
         return  _ptr;
     }
     //析构函数
     ~ScopedPtr()
     {
         if  (_ptr!=NULL)
         {
             delete  _ptr;
         }
     }
protected :
     //防拷贝
     ScopedPtr(ScopedPtr<T>& ap);
     ScopedPtr& operator=(ScopedPtr<T>& ap);
 
     T* _ptr;
};

 scoped_ptr的实现和auto_ptr非常类似,不同的是 scoped_ptr有着更严格的使用限制——不能拷贝,这就意味着scoped_ptr 是不能转换其所有权的。

在一般的情况下,如果不需要对于指针的内容进行拷贝,赋值操作,而只是为了防止内存泄漏的发生,scoped_ptr完全可以胜任。

   shared_ptr   共享所有权,引用计数

 shared_ptr的实现原理是通过引用计数来实现,拷贝或赋值时将引用计数加1,析构时只有当引用计数减到0才释放空间,否则只需将引用计数减1即可.

(ps:shared_ptr在新的C++11标准中叫做unique_ptr)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
template < typename  T>
class  SharedPtr
{
public :
     SharedPtr(T* ptr=NULL)
         :_ptr(ptr)
         , _refCount( new  long (1))
     {}
 
     ~SharedPtr()
     {
         _Release();
     }
 
     SharedPtr( const  SharedPtr<T>& sp)
         :_ptr(sp._ptr)
         , _refCount(sp._refCount)
     {
         ++(*_refCount);
     }
 
     //传统写法
     /*SharedPtr<T>& operator=(const SharedPtr<T>& sp)
     {
         if (this != &sp)
         {
             this->_Release();       
             _refCount = sp._refCount;
             _ptr = sp._ptr;
             ++(*_refCount);
         }
         return *this;
     }*/
 
     //现代写法
     SharedPtr<T>& operator=(SharedPtr<T> sp)
     {
         swap(_ptr, sp._ptr);
         swap(_refCount, sp._refCount);
 
         return  * this ;
     }
 
     T& operator*()
     {
         return  *_ptr;
     }
 
     T* operator->()
     {
         return  _ptr;
     }
 
     T* GetPtr()
     {
         return  _ptr;
     }
 
     long  GetCount()
     {
         return  *_refCount;
     }
 
protected :
     void  _Release()
     {
         if  (--(*_refCount) == 0)
         {
             //delete _ptr;
             _del(_ptr);
             delete  _refCount;
         }
     }
 
protected :
     T* _ptr;
     long * _refCount;     //引用计数
};

  为什么引用计数要设置为指针类型,而不设置成整型或静态变量?  

  请看这篇文章:http://www.cnblogs.com/Lynn-Zhang/p/5400714.html

有时候看似完美的东西,往往都存在着隐含的缺陷,上面的实现仍旧存在问题!

问题如下:

  1. 在多线程环境下,引用计数的更新存在安全隐患
  2. 循环引用问题
  3. 定制删除器

第一个比较容易理解,我们可以在改变引用计数的时候加上一把互斥锁,防止多线程带来的隐患。

第二个,循环引用问题。我们先来看一段利用标准库的shared_ptr 实现的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>
using   namespace  std;
#include<memory>
 
struct  Node
{
     shared_ptr<Node> _prev;
     shared_ptr<Node> _next;
 
     ~Node()
     {
         cout <<  "delete :"  << this << endl;
     }
};
void  TestSharedPtr()
{
     shared_ptr<Node> cur( new (Node));
     shared_ptr<Node> next( new (Node));
}

  

cur和next出了作用域,都调用析构函数被释放了。看似正确,但是如果我们加两句代码,问题就出现了:

1
2
3
4
5
6
7
void  TestSharedPtr()
{
     shared_ptr<Node> cur( new (Node));
     shared_ptr<Node> next( new (Node));
     cur->_next = next;     // 1
     next->_prev = cur;     // 2
}

  

我们发现两个节点都没有被释放!我们来分析一下原因:加了这两句代码后,这两个节点的引用计数都增加了1。出了作用域进行析构时,两个对象均不能释放,因为prev的要释放的前提是next释放,而next的释放又依赖于prev的释放。最后就形成了循环引用,谁都释放不了。

解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
template < class  T>
struct  Node
{
public :
     ~Node()
     {
         cout <<  "delete:"  <<  this  << endl;
     }
public :
     weak_ptr<Node> _prev;
     weak_ptr<Node> _next;
};
  
void  TestWeakPtr()
{
     shared_ptr<Node> cur( new  Node());
     shared_ptr<Node> next( new  Node());
  
     cout <<  "连接前:"  << endl;
     cout <<  "cur:"  << cur.use_count() << endl;
     cout <<  "next:"  << next.use_count() << endl;
  
     cur->_next = next;
     next->_prev = cur;
  
     cout <<  "连接后:"  << endl;
     cout <<  "cur:"  << cur.use_count() << endl;
     cout <<  "next:"  << next.use_count() << endl;
  
}

 

因为weak_ptr(弱引用智能指针)会对引用计数会做特殊处理(上述情况不加1)。

第三个,定制删除器。在shared_ptr中只能处理释放new开辟的空间,而对于malloc,以及fopen打开的文件指针不能处理,所以提出了定制删除器,方便释放其他类型的指针,而其实现则是通过仿函数(通过重载operator())来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
template  < typename  T,  class  Deleter = Del<T>>
class  SharedPtr
{
public :
     SharedPtr(T* ptr);
     SharedPtr(T* ptr, Deleter del);
     SharedPtr( const  SharedPtr<T, Deleter>& ap);
     ~SharedPtr();
     //SharedPtr<T, Deleter>& operator=(const SharedPtr<T, Deleter>& ptr);//传统写法
     //现代写法
     SharedPtr<T, Deleter>& operator=(SharedPtr<T, Deleter> ap);
     T& operator*() const ;
     T* operator->() const ;
     long  GetCount() const ;
     T* GetPtr() const ;
protected :
     void  _Realease();
protected :
     T* _ptr;
     long * _pCount;
     Deleter _del;
};
 
template  < typename  T,  class  Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(T* ptr) :_ptr(ptr), _pCount( new  long (1))
{}
 
template  < typename  T,  class  Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(T* ptr, Deleter del) : _ptr(ptr), _pCount( new  long (1)), _del(del)
{}
 
template  < typename  T,  class  Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr( const  SharedPtr<T, Deleter>& ap) : _ptr(ap._ptr), _pCount(ap._pCount), _del(ap._del)
{
     ++(* this ->_pCount);
}
 
template  < typename  T,  class  Deleter = Del<T>>
SharedPtr<T, Deleter>::~SharedPtr()
{
     this ->_Realease();
}
 
//template <typename T, class Deleter = Del<T>>//传统写法
//SharedPtr<T, Deleter>& SharedPtr<T, Deleter>::operator=(SharedPtr<T, Deleter> ap)
//{
//  if (this->_ptr != ap._ptr)
//  {
//      this->_Realease();
//      this->_ptr = ap._ptr;
//      this->_pCount = ap._pCount;
//      ++(*this->_pCount);
//  }
//  return *this;
//}
 
template  < typename  T,  class  Deleter = Del<T>> //现代写法
SharedPtr<T, Deleter>& SharedPtr<T, Deleter>::operator=(SharedPtr<T, Deleter> ap)
{
     swap( this ->_ptr, ap._ptr);
     swap( this ->_pCount, ap._pCount);
     swap( this ->_del, ap._del);
     return  * this ;
}
 
template  < typename  T,  class  Deleter = Del<T>>
T& SharedPtr<T, Deleter>::operator*() const
{
     return  *( this ->_ptr);
}
 
template  < typename  T,  class  Deleter = Del<T>>
T* SharedPtr<T, Deleter>::operator->() const
{
     return  this ->_ptr;
}
 
template  < typename  T,  class  Deleter = Del<T>>
long  SharedPtr<T, Deleter>::GetCount() const
{
     return  *( this ->_pCount);
}
 
template  < typename  T,  class  Deleter = Del<T>>
T* SharedPtr<T, Deleter>::GetPtr() const
{
     return  this ->_ptr;
}
 
template  < typename  T,  class  Deleter = Del<T>>
void  SharedPtr<T, Deleter>::_Realease()
{
     if  (--(* this ->_pCount) == 0)
     {
         _del(_ptr);
         delete  this ->_pCount;
     }
}
template < typename  T>
struct  Free
{
     void  operator()(T* ptr)
     {  
         cout <<  "Free:" <<ptr<< endl;
         free (ptr);
     }
};
 
template < typename  T>
struct  Fclose
{
     void  operator()(T* ptr)
     {
         cout <<  "Fclose:"  << ptr << endl;
         fclose (ptr);
     }
};
 
template < typename  T>
struct  Del
{
     void  operator()(T* ptr)
     {
         cout <<  "Delete:"  << ptr << endl;
         delete (ptr);
     }
};
 
//测试代码
void  TestDelete()
{
     int  *p1 = ( int *) malloc ( sizeof ( int ));
     SharedPtr< int , Free< int >> sp1(p1);
 
     FILE * p2 =  fopen ( "test.txt" "r" );
     SharedPtr< FILE , Fclose< FILE >> sp2(p2);
     
     SharedPtr< int > sp3( new  int (1));
 
}

  

 总结:

auto_ptr   管理权的转移 ->不建议使用

scopd_ptr ( unique_ptr )  防拷贝 ->简单粗暴

shared_ptr  引用计数 ->增减引用计数,直到对象的引用计数为0时再释放

scoped_array 和 shared_array 管理对象数组, 由于scopd_ptr 和 scoped_array、shared_ptr 和 shared_array 的实现都比较类似,这里不再赘述。需要注意的是这两种机制都重载了operator[] .

week_ptr 弱指针 ->辅助shared_ptr解决循环引用的问题。

 

转载必须注明作者和出处:

ProLyn http://www.cnblogs.com/Lynn-Zhang/p/5699983.html

分类:  C/C++
标签:  智能指针
1
0
« 上一篇: 设计模式——单例模式
» 下一篇: Unix下 五种 I/O模型
posted @  2016-07-26 01:40  ProLyn 阅读( 164) 评论( 0编辑  收藏

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++智能指针详解 智能指针详解 智能指针内容很多,重点是基本⽤法。 #include <boost/shared_ptr.hpp> class CBase: public boost::enable_shared_from_this<CBase> { public: virtual void f(){}//必须有个虚函数才能向上向下转换。 } typedef boost::shared_ptr<CBase> CBasePtr; class CChild: public CBase {} typedef boost::shared_ptr<CChild> CChildPtr; void main() { CBasePtr ptrBase = boost::make_shared<CBase>(); //CBasePtr ptrBase = CBasePtr(new CBase()); // 向下转换 CChildPtr ptrChild = boost::dynamic_pointer_cast<CChild>(ptrBase); // 向上转换 CBasePtr ptrXXX = ptrChild; // 普通转换 CChildPtr ptrXX = CChildPtr(dynamic_cast<CChild*>(ptrXXX.get())); } 暂时学会这些⽤法即可。 url: C++ 智能指针详解 ⼀、简介 由于 C++ 语⾔没有⾃动内存回收机制,程序员每次 new 出来的内存都要⼿动 delete。程序员忘记 delete,流程太复杂,最终导致没 有 delete,异常导致程序过早退出,没有执⾏ delete 的情况并不罕见。 ⽤智能指针便可以有效缓解这类问题,本⽂主要讲解参见的智能指针的⽤法。包 括:std::auto_ptrboost::scoped_ptr、boost::shared_ptr、boost::scoped_array、、boost::weak_ptr、boost::intrusive_ptr。你可能会想,如 此多的智能指针就为了解决new、delete匹配问题,真的有必要吗?看完这篇⽂章后,我想你⼼⾥⾃然会有答案。 下⾯就按照顺序讲解如上 7 种智能指针(smart_ptr)。 ⼆、具体使⽤ 1、总括 对于编译器来说,智能指针实际上是⼀个栈对象,并⾮指针类型,在栈对象⽣命期即将结束时,智能指针通过析构函数释放有它管理的 堆内存。所有智能指针都重载了"operator->"操作符,直接返回对象的引⽤,⽤以操作对象。访问智能指针原来的⽅法则使⽤"."操作符。 访问智能指针包含的裸指针则可以⽤ get() 函数。由于智能指针是⼀个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指 针是否为空,需要这样判断:if (my_smart_object.get())。 智能指针包含了 reset() ⽅法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递⼀个对象,则智能指 针会释放当前对象,来管理新传⼊的对象。 我们编写⼀个测试类来辅助分析: class Simple { public: Simple(int param = 0) { number = param; std::cout << "Simple: " << number << std::endl; } ~Simple() { std::cout << "~Simple: " << number << std::endl; } void PrintSomething() { std::cout << "PrintSomething: " << info_extend.c_str() << std::endl; } std::string info_extend; int number; }; 2、std::auto_ptr std::auto_ptr 属于 STL,当然在 namespace std 中,包含头⽂件 #include<memory> 便可以使⽤。std::auto_ptr 能够⽅便的管理单个堆 内存对象。 我们从代码开始分析: void TestAutoPtr() { std::auto_ptr<Simple> my_memory(new Simple(1)); // 创建对象,输出:Simple:1 if (my_memory.get()) { // 判断智能指针是否为空 my_memory->PrintSomething(); // 使⽤ operator-> 调⽤智能指针对象中的函数 my_memory.get()->info_extend = "Addition";
C++智能指针 智能指针_unique_ptr智能指针详解 智能指针详解 作为智能指针的⼀种,unique_ptr 指针⾃然也具备"在适当时机⾃动释放堆内存空间"的能⼒。和 shared_ptr 指针最⼤的不同之处在 于,unique_ptr 指针指向的堆内存⽆法同其它 unique_ptr 共享,也就是说,每个 unique_ptr 指针都独⾃拥有对其所指堆内存空间的所有 权。 这也就意味着,每个 unique_ptr 指针指向的堆内存空间的引⽤计数,都只能为 1,⼀旦该 unique_ptr 指针放弃对所指堆内存空 间的所有权,则该空间会被⽴即释放回收。 unique_ptr 智能指针是以模板类的形式提供的,unique_ptr<T>(T 为指针所指数据的类型)定义在<memory>头⽂件,并位于 std 命名空间 中。因此,要想使⽤ unique_ptr 类型指针,程序中应⾸先包含如下 2 条语句: 1. #include <memory> 2. using namespace std; 第 2 句并不是必须的,可以不添加,则后续在使⽤ unique_ptr 指针时,必须标注std:: 。 unique_ptr智能指针的创建 智能指针的创建 考虑到不同实际场景的需要,unique_ptr<T> 模板类提供了多个实⽤的构造函数,这⾥给读者列举了⼏种常⽤的构造 unique_ptr 智能指针的⽅式。 1) 通过以下 2 种⽅式,可以创建出空的 unique_ptr 指针: 1. std::unique_ptr<int> p1(); 2. std::unique_ptr<int> p2(nullptr); 2) 创建 unique_ptr 指针的同时,也可以明确其指向。例如: 1. std::unique_ptr<int> p3(new int); 由此就创建出了⼀个 p3 智能指针,其指向的是可容纳 1 个整数的堆存储空间。 和可以⽤ make_shared<T>() 模板函数初始化 shared_ptr 指针不同,C++11 标准中并没有为 unique_ptr 类型指针添 加类似的模板函数。 3) 基于 unique_ptr 类型指针不共享各⾃拥有的堆内存,因此 C++11 标准中的 unique_ptr 模板类没有提供拷贝构造函数,只提供 了移动构造函数。例如: 1. std::unique_ptr<int> p4(new int); 2. std::unique_ptr<int> p5(p4);//错误,堆内存不共享 3. std::unique_ptr<int> p5(std::move(p4));//正确,调⽤移动构造函数 值得⼀提的是,对于调⽤移动构造函数的 p4 和 p5 来说,p5 将获取 p4 所指堆空间的所有权,⽽ p4 将变成空指针(nullptr)。 4) 默认情况下,unique_ptr 指针采⽤ std::default_delete<T> ⽅法释放堆内存。当然,我们也可以⾃定义符合实际场景的释放规 则。值得⼀提的是,和 shared_ptr 指针不同,为 unique_ptr ⾃定义释放规则,只能采⽤函数对象的⽅式。例如: 1. //⾃定义的释放规则 2. struct myDel 3. { 4. void operator()(int *p) { 5. delete p; 6. } 7. }; 8. std::unique_ptr<int, myDel> p6(new int); 9. //std::unique_ptr<int, myDel> p6(new int, myDel()); unique_ptr<T>模板类提供的成员⽅法 模板类提供的成员⽅法 为了⽅便⽤户使⽤ unique_ptr 智能指针,unique_ptr<T> 模板类还提供有⼀些实⽤的成员⽅法,它们各⾃的功能如表 1 所⽰。 表 1 unique_ptr指针可调⽤的成员函数 成员函数名 成员函数名 功 功 能 能 operator*() 获取当前 unique_ptr 指针指向的数据。 operator->() 重载 -> 号,当智能指针指向的数据类型为⾃定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 operator =() 重载了 = 赋值号,从⽽可以将 nullptr 或者⼀个右值 unique_ptr 指针直接赋值给当前同类型的 unique_ptr 指针。 operator []() 重载了 [] 运算符,当 unique_ptr 指针指向⼀个数组时,可以直接通过 [] 获取指定下标位置处的数据。 get() 获取当前 unique_ptr 指针内部包含的普通指针。 get_deleter()

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值