boost 库 内存管理 shared_ptr shared_array 3

shared_ptr

shared_ptr包装new操作符从堆上分配的动态对象,实现引用计数型的智能指针,可以被自由地拷贝和赋值,在任意地方共享它,当引用计数未0时,删除被包装的动态分配对象,可以被安全地放在标准容器中。

1. 类摘要

shared_ptr类摘要

2. 操作函数

shared_ptr用来管理new动态分配对象,重载了*和->操作符模拟原始指针行为,提供隐式bool类型转化判断指针有效性,get()获得原始指针,未提供指针算数操作。
shared_ptr有各种形式的构造函数

  • 无参的shared_ptr()创建持有空指针的shared_ptr。
  • shared_ptr(Y *)获得指向类型T的指针p的管理权,同时引用计数置为1。要求Y类型必须能够转化为T类型。
  • shared_ptr(shared_ptr const &r)从另一个shared_ptr获得指针的管理权,同时引用计数加1,两个shared_ptr共享一个指针的管理权。
  • shared_ptr(std::auto_ptr<Y> &r)从一个auto_ptr获得指针管理权,引用计数置为1,auto_ptr自动失去管理权。
  • operator=赋值操作符从另一个shared_ptr或者auto_ptr获得指针管理权。
  • shared_ptr(Y *p, D d),参数d指定析构时的定制删除器,而不是简单的delete。

shared_ptr的reset()函数将引用计数减1,停止对指针的共享,除非引用计数为0,否则不会发生删除操作。带参数的reset()对原指针引用计数减1,同时改为管理另一指针。
shared_ptr中unique()use_count()检查引用计数,unique()在shared_ptr是指针的唯一拥有者时返回true,use_count()返回当前指针的引用计数。use_count()不提供高效率的操作,有时候不可用(极少数情况)。unique()总是可用的,而且速度比use_count()==1速度更快。
shared_ptr支持比较运算,等于、不等于、小于,基于内部保存的指针,shared_ptr可以被用于标准关联容器(set和map)。
shared_ptr指针转化static_pointer_cast<T>()const_pointer_cast<T>()dynamic_pointer_cast<T>(),和标准的转型操作static_cast<T>const_cast<T>dynamic_cast<T>类似,但返回的是转型后的shared_ptr。
shared_ptr支持流输出操作符operator<<,输出内部的指针值,方便调试。
shared_ptr提供基本的线程安全保证。

3. 用法

4. 工厂函数

shared_ptr在<boost/make_shared.hpp>中提供了自由工厂函数make_shared<T>(),消除显示new的调用。

1
2
template<class T, class... Argss>
shared_ptr<T> make_shared(Args && .. args);

make_shared()函数最多可以接受10个参数,传递给类型T的构造函数,创建shared_ptr<T>的对象并返回。make_shared()函数比直接创建shared_ptr对象的方式快且高效,因为它内部仅分配一次内存。
allocate_shared()make_shared()多接受一个定制的内存分配器类型参数,其它方面相同。

5. 应用于标准容器

  • 将容器作为shared_ptr管理的对象。
  • 将shared_ptr作为容器的元素。

6. 应用于桥接模式

桥接模式(brideg)是一种结构型设计模式,把类的具体实现细节对用于隐藏起来,达到类之间的最小耦合关系。可以将头文件的依赖关系降到最小,减少编译事件,而且可以不使用虚函数实现多态。

7. 应用于工厂模式

工厂模式是一种创建性设计模式,包装new操作符的使用,让对象的创建集中在工程类或者工厂函数中,更容易的适应变化。

8. 定制删除器

shared_ptr(Y *p, D d)第一个参数是被管理的指针,第二个删除器参数d则告诉shared_ptr在析构时不是使用delete操作指针p,而是要用d来操作,即把delte p换成d(p)。删除器d可以是函数对象、函数指针,只要能够像函数一样调用使得d(p)成立即可。对删除器的要求是必须是可拷贝的,不能抛出异常。shared_ptr提供自由函数get_deleter(shared_ptr<T> const &p),返回删除器指针。有了删除器可以实现管理任意资源。只要该资源提供它的释放操作,shared_ptr保证自动释放。

9. 高级议题

shared_ptr

shared_ptr<void>可以存储void*的指针,而void*型指针可以指向任意类型,因此shared_ptr<void>拥有了容纳任意类型的能力。但将指针存储为void*会丧失原来的类型信息,在使用时使用转型函数转为原来的指针,但这设计运行时动态类型转换,使代码不够安全,建议不要这样使用。

删除器的高级用法

基于shared_ptr<void>和定制删除器,shared_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
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
141
142
143
144
145
146
147
148

class C //让C代理B,对B的方法做一次封装
{
private:
class B;
boost::shared_ptr<B> p;
public:
C();
void print();
};

class C::B
{
public:
void print()
{
cout<<"B ctor :"<<this<<endl;
}
};

C::C():p(new B){}
void C::print()
{
p->print();
}


class E
{
public:
E(){cout<<"E ctor"<<endl;}
virtual void f() = 0;
virtual void g() = 0;
protected:
virtual ~E()
{
cout<<"E dtor"<<endl;
}
};
class F : public E
{
public:
virtual void f()
{
cout<<"class F f"<<endl;
}
virtual void g()
{
cout<<"class F g"<<endl;
}
};

E* create1()
{
return (E*)(new F);
}

boost::shared_ptr<E> create2()
{
return boost::shared_ptr<E>(new F);
}

class G
{
public:
G()
{
cout<<"ctor G :"<<this<<endl;
}
~G()
{
cout<<"dtor G :"<<this<<endl;
}

};

void DeleteG(G *g)
{
if(g != NULL)
{
cout<<"delete G :"<<g<<endl;
delete g;
}
}

void AryF(void * p)
{
cout<<"AryF"<<endl;
}

void TestSharedPtr()
{
boost::shared_ptr<int> sp1(new int(10));
cout<<"sp1 unique :"<<sp1.unique()<<endl;
cout<<"sp1 use_count :"<<sp1.use_count()<<endl;

boost::shared_ptr<int> sp2(sp1);
cout<<"sp1 unique :"<<sp1.unique()<<endl;
cout<<"sp1 use_count :"<<sp1.use_count()<<endl;
cout<<"sp2 unique :"<<sp2.unique()<<endl;
cout<<"sp2 use_count :"<<sp2.use_count()<<endl;

sp2.reset();
cout<<"sp2 reset after"<<endl;
cout<<"sp1 unique :"<<sp1.unique()<<endl;
cout<<"sp1 use_count :"<<sp1.use_count()<<endl;
cout<<"sp2 unique :"<<sp2.unique()<<endl;
cout<<"sp2 use_count :"<<sp2.use_count()<<endl;

boost::shared_ptr<A> spA1(new A());
cout<<"spA :"<<spA1.get()<<endl;

//工厂函数
boost::shared_ptr<A> spA2 = boost::make_shared<A>();

boost::shared_ptr<A> spA3 = boost::make_shared<A, int, int>(1, 2);

//应用于标准容器
vector<boost::shared_ptr<int>> v(3);
int i = 0;
for(auto& it : v)
{
it = boost::make_shared<int>(++i);
cout<<*(it)<<endl;
}

boost::shared_ptr<int> sp3 = v[2];
*sp3 = 100;
cout<<*v[2]<<endl;

//应用于桥接模式
C c;
c.print();

{
//应用于工厂模式
E * E1 = create1();
boost::shared_ptr<E> E2 = create2();

delete (F*)E1; //E1必须手动调用delete 而E2在出作用域后自动调用 E的析构函数是protected的,所需需要转型为F再delete
}
cout<<"桥接模式结束"<<endl;

//定制删除器
boost::shared_ptr<G> pg(new G(), DeleteG);

boost::shared_ptr<void> pv(NULL, AryF);
}

shared_array

包装new[]操作符在堆上分配的动态数组,使用引用计数机制为动态数组提供代理,可在程序的生命周期里长期存在,直到没有任何引用后才释放内存。

类摘要

  • shared_array构造函数接受的指针必须是new[]的结果
  • 提供operator[]操作符重载
  • 析构函数使用delete[],释放资源

用法

最好使用shared_ptr<std::vector> 或者 std::vector<shared_ptr>来替代