shared_ptr类模版

最近在看陈硕的《Linux多线程服务端编程:使用muduo C++网络库》,第一章中重点讲了多线程下指针的处理问题,其中重点是 shared_ptr 与 weak_ptr, 但书中对这部分的基础介绍比较简略,以前看过智能指针,但一直没有完整学习过,因而学习翻译了官方的文档进行学习。以下是 boost::shared_ptr 的中文翻译,其中一部分感觉不是很重要的这里暂时没有给出翻译。

Introduction

shared_ptr类模版存储了如用 C++ 的 new 表达式动态分配的对象的指针。当最后一个指向该对象的 share_ptr 被销毁或者重置时,该对象被保证一定会被删除。
例如:

1
2
shared_ptr< X > p1( new X );
shared_ptr< void > p2( new int(5) );

销毁时,不论模版的参数为何,shared_ptr 将删除在构造时所传递真实类型的指针。在第二个例子中,尽管 p2 类型是 shared_ptr< void >, 存储了一个类型为void*的指针, 当 p2 被销毁或者被重置,它将对传递到构造函数的 int* 调用delete函数。

基本成员函数

shared_ptr 拥有拷贝构造函数,Move构造函数,拷贝赋值函数,Move赋值函数,并且能用在标准库的容器里。对比操作符同样也提供,使得 shared_ptr 能用在标准库中的关联容器里。(关联容器?)

循环嵌套问题

因为使用了引用计数来作为其实现,因此循环嵌套 shared_ptr 的实体将不会被回收。 例如,如果 main() 有一个指向对象 A 的 shared_ptr,而 A 自身又直接或者间接地拥有一个指向 A 的 shared_ptr,那么 A 的引用计数将为 2。销毁 main() 中的 shared_ptr 将使得 A 的引用计数为 1。(使用 weak_ptr 打破循环?)

void 模版参数

shared_ptr 使用 T 来表明所指向的对象的类型,它及它的很多成员函数并不需要使用到 T;它被允许是一个不完整的类型,或者 void,需要使用到 T 的成员函数将在下面文档中介绍。

转换

shared_ptr< T > 能被隐式地转换到 shared_ptr< U >,T* 能被隐式转换到 U*。特别地, shared_ptr< T > 能被隐式转换为 shared_ptr< T const >;当 U 是 T 的基类时, 能被转换到 shared_ptr< U >;转换到 shared_ptr< void >。

std::shared_ptr

shared_ptr 是 C++11 一部分,为 std::shared_ptr。

数组指针

从 Boost 1.53 开始,shared_ptr 能够保存指向动态分配数组的指针,模版参数为 T[] 或者 T[N],两者间几乎没什么差别,后者能够检查进行下标的范围。
例如:

1
2
shared_ptr< double[ 1024 ] > p1( new double[ 1024 ])
shared_ptr< double[] > p2( new double[n] )

Best practices

去除内存泄漏的一个简单方法是:总是使用一个智能指针去存储 new 的结果。每个 new 关键字出现的地方应该有如下的形式:

1
shared_ptr< T > p( new Y );

这里 shared_ptr 可以用其他以外的智能指针代替; T 和 Y 可以是相同的类型,Y 的构造函数也可以作为传递的参数。

如果你仔细观察,你发现这里不需要使用 delete 语句,try/catch 也很少。

避免使用未命名的临时 shared_ptr 节省代码书写,为说明这个问题,考虑下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
void f( shared_ptr< int >, int);
int g();
void ok()
{

shared_ptr< int > p( new int(2) );
f( p, g() );
}
void bad()
{

f( shared_ptr< int >( new int(2)), g() );
}

ok函数使用了比较好的方式,而 bad 函数使用了临时的 shared_ptr,导致了内存泄漏的可能。因为函数参数的计算没有指定顺序,可能 new int(2) 会首先执行,然后 g(),但如果函数 g 在执行是抛出了异常,那么我们将无法调用 shared_ptr 的构造函数。 链接 Herb Sutter’s treatment (及here) 对这个问题有更多的说明。

上述描述的异常安全问题也能通过使用 make_shared 或者 allocate_shared 工厂函数被排除(定义在 boost/make_shared.hpp)。这些工厂函数通过整合内存加快了程序的效率。

Synopsis

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
namespace boost {

class bad_weak_ptr: public std::exception;

template<class T> class weak_ptr;

template<class T> class shared_ptr {

public:

typedef see below element_type;

shared_ptr(); // never throws
shared_ptr(std::nullptr_t); // never throws

template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);

~shared_ptr(); // never throws

shared_ptr(shared_ptr const & r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws

shared_ptr(shared_ptr && r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> && r); // never throws

template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p); // never throws

template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);

template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);

template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);

shared_ptr & operator=(shared_ptr const & r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws

shared_ptr & operator=(shared_ptr const && r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> const && r); // never throws

template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);

template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);

shared_ptr & operator=(std::nullptr_t); // never throws

void reset(); // never throws

template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);

template<class Y> void reset(shared_ptr<Y> const & r, element_type * p); // never throws

T & operator*() const; // never throws; only valid when T is not an array type
T * operator->() const; // never throws; only valid when T is not an array type

element_type & operator[](std::ptrdiff_t i) const; // never throws; only valid when T is an array type

element_type * get() const; // never throws

bool unique() const; // never throws
long use_count() const; // never throws

explicit operator bool() const; // never throws

void swap(shared_ptr & b); // never throws

template<class Y> bool owner_before(shared_ptr<Y> const & rhs) const; // never throws
template<class Y> bool owner_before(weak_ptr<Y> const & rhs) const; // never throws
};

template<class T, class U>
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b); // never throws

template<class T>
bool operator==(shared_ptr<T> const & p, std::nullptr_t); // never throws

template<class T>
bool operator==(std::nullptr_t, shared_ptr<T> const & p); // never throws

template<class T>
bool operator!=(shared_ptr<T> const & p, std::nullptr_t); // never throws

template<class T>
bool operator!=(std::nullptr_t, shared_ptr<T> const & p); // never throws

template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b); // never throws

template<class T> typename shared_ptr<T>::element_type * get_pointer(shared_ptr<T> const & p); // never throws

template<class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r); // never throws

template<class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r); // never throws

template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r); // never throws

template<class T, class U>
shared_ptr<T> reinterpet_pointer_cast(shared_ptr<U> const & r); // never throws

template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);

template<class D, class T>
D * get_deleter(shared_ptr<T> const & p);
}

Members

element_type

1
typedef ... element_type;

当 T 不是数组类型时,element_type 是 T;当 T 是 U[] 或者 U[N] 时,element_type 是 U

默认构造函数

1
2
shared_ptr(); // never throws
shared_ptr(std::nullptr_t); // never throws

效果:创建一个空的 shared_ptr。
后置条件:use_count() == 0 && get() == 0。
抛出:无。

保证 nothrows 是重要的,因为 reset() 通过默认构造函数来指定,这也表明了默认构造函数不能分配内存空间。

指针构造函数

1
template<class Y> explicit shared_ptr(Y *p);

前置条件:Y 必须是完整类型。当 T 是数组类型时,语句 delete[] p;当 T 非数组类型时,delete p 必须可以执行,不能抛出异常。当 T 是 U[N],Y()[N] 必须可以转化到 T*;当 T 是 U[],Y()[] 必须可以转化到 T*;否则, Y* 必须可以转化到 T*。
效果:当 T 非数组类型时,构造一个拥有指针 p 的shared_ptr。否则,创建一个拥有指针 p 和一个可以调用 delete[] p 的 deleter。
后置条件:use_count() == 1 && get() == p。如果 T 非数组类型并且 p 可以转化到 enable_shared_from_this< V >*,p->shared_from_this()返回 *this 的一个拷贝。
抛出:当资源不能被满足时,std::bad_alloc 或者自定义的异常。
异常安全:如果异常被抛出,T 是数组类型,构造函数调用 delete[] p;T 非数组类型则调用 delete p。
注意:p 必须是指向通过 C++ 的 new 表达式来创建的对象的指针,或者为 0。即使 p 为 0, 后置条件中 use_count() 也为 1;delete 作用于值为 0 的指针无害。

该构造函数是模版函数,是为记忆传进来的指针的真实类型。析构函数将会调用 delete 作用于原始类型的指针,即使当 T 没有一个虚析构函数或者为 void。

带 deleter 的构造函数

1
2
3
4
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);

拷贝构造函数

1
2
shared_ptr(shared_ptr const & r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws

前置条件:Y* 可以转化到 T*。
效果:如果 r 是空的,构造一个空的shared_ptr;否则,构造一个分享 r 的指针所有权的shared_ptr。
后置条件:get() == r.get() && use_count() == r.use_count()。
抛出:无。

Move 构造函数

1
2
shared_ptr(shared_ptr && r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> && r); // never throws

前置条件:Y* 可以转化到 T*。
效果:move 构造一个从 r 的 shared_ptr。
后置条件:*this 包含了 r 旧的值。r 为空并且 r.get() == 0。
抛出:无。

aliasing 构造函数

1
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p); // never throws

weak_ptr 构造函数

1
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);

前置条件:Y* 可以转化到 T*。
效果:构造一个分享 r 所有权的 shared_ptr 并且复制 r 中的指针。
后置条件:use_count() == r.use_count()。
抛出:当 r.use_count() == 0, bad_weak_ptr。
异常安全:如果异常抛出,构造函数将无效果。

auto_ptr 构造函数

1
2
template<class Y> shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);

前置条件:Y* 可以转化到 T*。
效果:构造一个 shared_ptr,存储 r.release() 的拷贝
后置条件:use_count() == 1。
抛出:当资源不能被满足时,std::bad_alloc 或者自定义的异常。
异常安全:如果异常抛出,构造函数将无效果。

unique_ptr 构造函数

1
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);

析构函数

1
~shared_ptr(); // never throws

效果:
如果 *this 为空,或者与另外一个 shared_ptr 共享所有权 (use_count()>1),那么没有副作用。
否则,如果 *this 拥有一个指针 p 和一个 deleter d,d(p) 被调用。
否则,*this 拥有一个指针 p, delete p 被调用。
抛出: 无。

赋值

1
2
3
shared_ptr & operator=(shared_ptr const & r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);

效果:等同于 shared_ptr(r).swap(*this)。
返回:*this。
注意:因为临时对象的构造会使得 use_count() 更新

1
2
3
4
shared_ptr & operator=(shared_ptr && r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> && r); // never throws
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r

效果:等同于 shared_ptr( std::move(r) ).swap(*this)。
返回:*this。

1
shared_ptr & operator=(std::nullptr_t); // never throws

效果:等同于 shared_ptr().swap(*this)。
返回:*this。

reset

1
void reset(); // never throws

效果:等同于 shared_ptr().swap(*this)。

1
template<class Y> void reset(Y * p);

效果:等同于 shared_ptr(p).swap(*this)。

1
template<class Y, class D> void reset(Y * p, D d);

效果:等同于 shared_ptr(p,d).swap(*this)。

1
template<class Y, class D, class A> void reset(Y * p, D d, A a);

效果:等同于 shared_ptr(p,d,s).swap(*this)。

1
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p); // never throws

效果:等同于 shared_ptr(r,p).swap(*this)。

indirection

get

1
element_type * get() const; // never throws

返回:存储的指针。
抛出:无。

unique

1
bool unique() const; // never throws

返回:use_count() == 1。
抛出:无。
注意:unique() 比 use_count() 更快。如果使用 unique() 来实现写拷贝,当存储的指针为 0 时不要依赖特定的值。(?)

use_count

1
long use_count() const; // never throws

返回:共享所有权的 shared_ptr 的数量,若 *this 为空则为 0。
抛出:无。
注意:use_count() 不是高效的,只是用来调试和测试。

conversions

swap

1
void swap(shared_ptr & b); // never throws

效果:交换两个智能指针的内容
抛出:无。

Free Functions

Example

shared_ptr_example.cpp 提供了一个完整的例子,该程序包含了 shared_ptr 对象的 std::vector 和 std::set。
注意容器生成后,shared_ptr 的 use_count() 为 2.

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

// Boost shared_ptr_example.cpp --------------------------------------------//

// Copyright Beman Dawes 2001. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


// See http://www.boost.org/libs/smart_ptr for documentation.

// Revision History
// 21 May 01 Initial complete version (Beman Dawes)

// The original code for this example appeared in the shared_ptr documentation.
// Ray Gallimore pointed out that foo_set was missing a Compare template
// argument, so would not work as intended. At that point the code was
// turned into an actual .cpp file so it could be compiled and tested.

#include <vector>
#include <set>
#include <iostream>
#include <algorithm>
#include <boost/shared_ptr.hpp>

// The application will produce a series of
// objects of type Foo which later must be
// accessed both by occurrence (std::vector)
// and by ordering relationship (std::set).

struct Foo
{
Foo( int _x ) : x(_x) {}
~Foo() { std::cout << "Destructing a Foo with x=" << x << "\n"; }
int x;
/* ... */
};

typedef boost::shared_ptr<Foo> FooPtr;

struct FooPtrOps
{
bool operator()( const FooPtr & a, const FooPtr & b )
{ return a->x > b->x; }
void operator()( const FooPtr & a )
{ std::cout << a->x << " " << a.use_count() << "\n"; }
};

int main()
{
std::vector<FooPtr> foo_vector;
std::set<FooPtr,FooPtrOps> foo_set; // NOT multiset!

FooPtr foo_ptr( new Foo( 2 ) );
foo_vector.push_back( foo_ptr );
foo_set.insert( foo_ptr );

foo_ptr.reset( new Foo( 1 ) );
foo_vector.push_back( foo_ptr );
foo_set.insert( foo_ptr );

foo_ptr.reset( new Foo( 3 ) );
foo_vector.push_back( foo_ptr );
foo_set.insert( foo_ptr );

foo_ptr.reset ( new Foo( 2 ) );
foo_vector.push_back( foo_ptr );
foo_set.insert( foo_ptr );

std::cout << "foo_vector:\n";
std::for_each( foo_vector.begin(), foo_vector.end(), FooPtrOps() );

std::cout << "\nfoo_set:\n";
std::for_each( foo_set.begin(), foo_set.end(), FooPtrOps() );
std::cout << "\n";

return 0;
}

编译运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
g++ -g shared_ptr_example.cpp -o shared_ptr_example
./shared_ptr_example

foo_vector:
2 2
1 2
3 2
2 2

foo_set:
3 2
2 2
1 2

Destructing a Foo with x=2
Destructing a Foo with x=3
Destructing a Foo with x=1
Destructing a Foo with x=2

Handle/Body Idiom

shared_ptr 的一个常用地方是实现一个 handle/body (pimpl) idiom, 它可以避免头文件中类的具体实现。
]shared_ptr_example2_test.cpp 例子包含了一个头文件 shared_ptr_example2.hpp,该头文件利用 shared_ptr 去隐藏了不完整类型的具体实现。 shared_ptr_example2.cpp 则包含了具体实现。注意这里不需要一个 explicit 的析构函数。与 ~scoped_ptr 不同,~shared_ptr 不需要 T 是完整类型。(?)

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
//  Boost shared_ptr_example2 header file  -----------------------------------//

// Copyright Beman Dawes 2001. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


// See http://www.boost.org/libs/smart_ptr for documentation.

#include <boost/shared_ptr.hpp>

// This example demonstrates the handle/body idiom (also called pimpl and
// several other names). It separates the interface (in this header file)
// from the implementation (in shared_ptr_example2.cpp).

// Note that even though example::implementation is an incomplete type in
// some translation units using this header, shared_ptr< implementation >
// is still valid because the type is complete where it counts - in the
// shared_ptr_example2.cpp translation unit where functions requiring a
// complete type are actually instantiated.

class example
{
public:
example();
void do_something();
private:
class implementation;
boost::shared_ptr< implementation > _imp; // hide implementation details
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Boost shared_ptr_example2 implementation file  -----------------------------//

// Copyright Beman Dawes 2001. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


// See http://www.boost.org/libs/smart_ptr for documentation.

#include "shared_ptr_example2.hpp"
#include <iostream>

class example::implementation
{
public:
~implementation() { std::cout << "destroying implementation\n"; }
};

example::example() : _imp( new implementation ) {}

void example::do_something()
{ std::cout << "use_count() is " << _imp.use_count() << "\n"; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Boost shared_ptr_example2_test main program  ------------------------------//

// Copyright Beman Dawes 2001. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


// See http://www.boost.org/libs/smart_ptr for documentation.

#include "shared_ptr_example2.hpp"

int main()
{

example a;
a.do_something();
example b(a);
b.do_something();
example c;
c = a;
c.do_something();
return 0;
}

编译运行

1
2
3
4
5
6
7
8
g++ -g shared_ptr_example2.cpp shared_ptr_example2_test.cpp -o shared_ptr_example2_test
./shared_ptr_example2_test

use_count() is 1
use_count() is 2
destroying implementation
use_count() is 3
destroying implementation

这里第一个 destroying implementation 的输出是由于 c = a 语句导致 c 中的 _imp 被析构。

Thread Safety

shared_ptr 对象提供了 C++ 内建类型的线程安全级别。 shared_ptr 对象能被多个线程同时读,不同的 shared_ptr 对象能够被多个线程同时写(即使这些对象是共享同一个引用)。

Example:

1
shared_ptr<int> p(new int(42));

1
2
3
4
5
6
7
//--- Example 1 ---

// thread A
shared_ptr<int> p2(p); // reads p

// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
1
2
3
4
5
6
7
//--- Example 2 ---

// thread A
p.reset(new int(1912)); // writes p

// thread B
p2.reset(); // OK, writes p2
1
2
3
4
5
6
7
//--- Example 3 ---

// thread A
p = p3; // reads p3, writes p

// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
1
2
3
4
5
6
7
//--- Example 4 ---

// thread A
p3 = p2; // reads p2, writes p3

// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"
1
2
3
4
5
6
7
//--- Example 5 ---

// thread A
p3.reset(new int(1));

// thread B
p3.reset(new int(2)); // undefined, multiple writes

从 Boost release 1.33.0 开始,shared_ptr 在大多平台上使用了通用锁的实现。
如果你的程序是单线程的,并且不需要链接使用了 shared_ptr 的库,你可以使用宏定义 #define BOOST_SP_DISABLE_THREADS 来切换到普通非原子的引用计数更新。
你能定义宏 BOOST_SP_USE_PTHREADS 来关闭通用锁,切换回使用普通的 pthread_mutex_t 的代码。