C++11的智能指针(2) shared_ptr

C++11新引入了几种智能指针:unique_ptrshared_ptrweak_ptr,而原来的auto_ptr被弃用。

我会写几篇文章分别来介绍这几种智能指针的用法,本篇主要介绍shared_ptr

shared_ptr可以说是我们最常规意义上理解的智能指针了,区别于unique_ptrshare_ptr有拷贝构造函数和赋值操作符,每当shared_ptr多出一个拷贝,所有拷贝的引用计数都会增加。

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
// example1.cpp

#include <iostream>
#include <memory>

class Test {
public:
Test(int tag) : _tag(tag) {
std::cout << "Test::Test() " << _tag << std::endl;
}

~Test() {
std::cout << "Test::~Test() " << _tag << std::endl;
}

void test() {
std::cout << "Test::test() " << _tag << std::endl;
}
private:
int _tag;
};

int main() {
std::shared_ptr<Test> p1(new Test(1));
p1->test();
std::cout << "p1:" << p1.use_count() << std::endl;
std::cout << "----------------" << std::endl;

std::shared_ptr<Test> p2 = std::make_shared<Test>(2);
p2->test();
std::cout << "p2:" << p2.use_count() << std::endl;
std::cout << "----------------" << std::endl;

std::shared_ptr<Test> p3 = p1;
p3->test();
std::cout << "p1:" << p1.use_count() << std::endl;
std::cout << "p3:" << p3.use_count() << std::endl;
std::cout << "----------------" << std::endl;

p1.reset();
std::cout << "p1:" << p1.use_count() << std::endl;
std::cout << "p3:" << p3.use_count() << std::endl;
std::cout << "----------------" << std::endl;

p2 = p3;
p2->test();
std::cout << "p2:" << p2.use_count() << std::endl;
std::cout << "p3:" << p3.use_count() << std::endl;
std::cout << "----------------" << std::endl;

return 0;
}

编译

1
g++ -o example1 -std=c++11 example1.cpp

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Test::Test() 1			
Test::test() 1
p1:1 (1)
----------------
Test::Test() 2
Test::test() 2
p2:1 (2)
----------------
Test::test() 1
p1:2
p3:2 (3)
----------------
p1:0
p3:1 (4)
----------------
Test::~Test() 2
Test::test() 1
p2:2
p3:2 (5)
----------------
Test::~Test() 1 (6)

(1) 展示了用shared_ptr的构造函数来生成一个shared_ptr对象,并且我们看到他的引用计数现在是1

(2) 展示了用make_shared来生成一个shared_ptr,可以看到,这里我们终于彻底告别了new,是不是感觉有点暗爽。

(3) 展示了shared_ptr的赋值操作,我们看到,赋值之后,p1p3的引用计数都增加到了2

(4) reset操作使得p1变为空的,所以它的引用计数为0,而p3的引用计数则减少到了1,此时的p1p3已经完全不是一回事了。

(5) p2 = p3的操作使得p2原来指向的对象被释放,所以我们首先看到一条析构函数的输出。然后我们看到p2 p3的的引用计数都变成了2

(6) 程序退出的时候,很自然的,所有的智能指针都出了作用域,所以最后一条析构调用被输出。

类型转换

假设我们两个类

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
#include <iostream>
#include <memory>

class Base {
public:
Base() {
std::cout << "Base::Base()" << std::endl;
}
~Base() {
std::cout << "Base::~Base()" << std::endl;
}
virtual void test() {
std::cout << "Base::test()" << std::endl;
}
};

class Derived : public Base {
public:
Derived() {
std::cout << "Derived::Derived()" << std::endl;
}
~Derived() {
std::cout << "Derived::~Derived()" << std::endl;
}

virtual void test() {
std::cout << "Derived::test()" << std::endl;
}
};

int main() {
std::shared_ptr<Base> pb = std::make_shared<Base>();
std::shared_ptr<Derived> pd = std::make_shared<Derived>();
std::cout << "pb.use_count() " << pb.use_count() << std::endl;
std::cout << "pd.use_count() " << pd.use_count() << std::endl;
pb = static_cast<Base>(pd);
std::cout << "pb.use_count() " << pb.use_count() << std::endl;
std::cout << "pd.use_count() " << pd.use_count() << std::endl;
pb->test();
return 0;
}

用static_cast, dynamic_cast, const_cast是无法用在不同的shared_ptr之上的。

自己实现一个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
//shared_ptr.h
namespace up4dev {

template<typename T>
class shared_ptr {
public:
shared_ptr() : _p(nullptr), _c(nullptr){
}

~shared_ptr() {
reset();
}

shared_ptr(T* p) : _p(p), _c(new int(1)) {
}

shared_ptr(const shared_ptr& sp) {
reset();
_p = sp._p;
_c = sp._c;
*_c += 1;
}

shared_ptr& operator=(const shared_ptr& sp) {
reset();
_p = sp._p;
_c = sp._c;
*_c += 1;
return *this;
}

T* get() {
return _p;
}

T* operator->() {
return _p;
}

void reset() {
if (_c) {
*_c -= 1;
if (*_c == 0) {
delete _p;
delete _c;
}
_p = nullptr;
_c = nullptr;
}
}

int use_count() {
return _c ? *_c : 0;
}

private:
T* _p;
int* _c;
};

}

对前文的测试用例稍加修改,用新写的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
//example3.cpp

#include <iostream>
#include "shared_ptr.h"

class Test {
public:
Test(int tag) : _tag(tag) {
std::cout << "Test::Test() " << _tag << std::endl;
}

~Test() {
std::cout << "Test::~Test() " << _tag << std::endl;
}

void test() {
std::cout << "Test::test() " << _tag << std::endl;
}
private:
int _tag;
};

int main() {
up4dev::shared_ptr<Test> p1(new Test(1));
p1->test();
std::cout << "p1:" << p1.use_count() << std::endl;
std::cout << "----------------" << std::endl;

up4dev::shared_ptr<Test> p2 = up4dev::shared_ptr<Test>(new Test(2));
p2->test();
std::cout << "p2:" << p2.use_count() << std::endl;
std::cout << "----------------" << std::endl;

up4dev::shared_ptr<Test> p3 = p1;
p3->test();
std::cout << "p1:" << p1.use_count() << std::endl;
std::cout << "p3:" << p3.use_count() << std::endl;
std::cout << "----------------" << std::endl;

p1.reset();
std::cout << "p1:" << p1.use_count() << std::endl;
std::cout << "p3:" << p3.use_count() << std::endl;
std::cout << "----------------" << std::endl;

p2 = p3;
p2->test();
std::cout << "p2:" << p2.use_count() << std::endl;
std::cout << "p3:" << p3.use_count() << std::endl;
std::cout << "----------------" << std::endl;

return 0;
}

编译

1
g++ -o example3 -std=c++11 example3.cpp

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Test::Test() 1
Test::test() 1
p1:1
----------------
Test::Test() 2
Test::test() 2
p2:1
----------------
Test::test() 1
p1:2
p3:2
----------------
p1:0
p3:1
----------------
Test::~Test() 2
Test::test() 1
p2:2
p3:2
----------------
Test::~Test() 1

C++11的智能指针系列文章