本节将实现一个名为SmartPointer的简化版本的shared_ptr模板类,相关C++代码如下。

版权声明

本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。

本文不可以以纸质出版为目的进行改编、摘抄。

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
//Project - SmartPointer
#include <iostream>
using namespace std;

class Fish {
public:
string sName;
Fish(const string& name){
sName = name;
cout << "Fish Constructor called: " << sName << endl;
}

void sayHello(){
cout << "Aloha: " << sName << endl;
}

~Fish(){
cout << "Fish Destructor called: " << sName << endl;
}
};

template <typename T>
class SmartPointer {
private:
T* ptr; //原始指针
int* refCount; //指向引用计数的指针
void releaseReference(){ //释放对对象的引用
if (!ptr)
return;
(*refCount)--;
if (*refCount==0){
delete ptr; ptr = nullptr;
delete refCount; refCount = nullptr;
}
}

public:
SmartPointer(T* p=nullptr){
ptr = p;
refCount = new int;
*refCount = 1;
}

~SmartPointer(){
releaseReference();
}

//拷贝构造函数
SmartPointer(const SmartPointer& r) {
cout << "Copy constructor of SmartPointer." << endl;
ptr = r.ptr;
refCount = r.refCount;
(*refCount)++;
}

//重载=操作符
SmartPointer& operator=(const SmartPointer& r){
cout << "operator=(const SmartPointer&)." << endl;
if (&r == this)
return *this;
releaseReference();
ptr = r.ptr;
refCount = r.refCount;
(*refCount)++;
return *this;
}

//重载*操作符
T& operator*(){
cout << "operator*() of SmartPointer." << endl;
if (ptr)
return *ptr;
throw exception();
}

//重载->操作符
T* operator->(){
cout << "operator->() of SmartPointer." << endl;
if (ptr)
return ptr;
throw exception();
}

//查询引用计数
int referenceCount(){
return *refCount;
}
};

int main() {
SmartPointer<Fish> f1(new Fish("Dora"));
SmartPointer<Fish> f2(new Fish("Tom"));
auto f3 = f1; //调用f3的拷贝构造函数,以f1为实参
f2 = f1; //调用f2的operator=()函数,以f1为实参,间接导致Tom鱼被释放
(*f2).sayHello(); //调用f2的operator*()函数
f2->sayHello(); //调用f2的operator->()函数
cout << "Refernce count of Dora fish: " << f2.referenceCount() << endl;
return 0;
}

上述代码的执行结果为:

1
2
3
4
5
6
7
8
9
10
11
Fish Constructor called: Dora
Fish Constructor called: Tom
Copy constructor of SmartPointer.
operator=(const SmartPointer&).
Fish Destructor called: Tom
operator*() of SmartPointer.
Aloha: Dora
operator->() of SmartPointer.
Aloha: Dora
Refernce count of Dora fish: 3
Fish Destructor called: Dora

  类似于第7章中介绍的模板函数,SmartPointer被设计为一个模板类,其类型参数T需要在使用该类时于<>内提供。

🚩第22 ~ 26行:SmartPointer对象包含两个私有属性,其中,ptr为指向动态对象的原始指针;refCount则为指向引用计数的指针。如前所述,该引用计数用于记录指向动态对象的存活智能指针对象的个数。

🚩第27 ~ 35行:下述情况之一,releaseReference()成员函数将被调用,以释放该指针对动态对象的引用。

  • 智能指针对象析构时;

  • 智能指针准备绑定其它对象前。

🚩第28~ 29行:如果智能指针是空指针,直接返回。

🚩第30行:引用计数递减。

🚩第31 ~ 34行:如果引用计数归零,说明该指针所指向的动态对象已经不被需要,将其释放,同时释放引用计数。

🚩第38 ~ 42行:构造函数接受一个动态对象的指针作为参数。函数体内,原始指针得到保存,引用计数被创建并置1。

🚩第44 ~ 46行:析构函数内调用releaseReference()函数释放指针对动态对象的引用。如果该智能指针是指向该动态对象的最后一个存活的智能指针,该函数会释放动态对象。

🚩第49 ~ 54行:拷贝构造函数。该函数通过复制来初始化一个新的智能指针对象。函数体完成了下述工作:

  • 从源对象复制原始指针及引用计数指针。

  • 引用计数递增,因为又产生了一个新的指向该动态对象的智能指针。

🚩第57 ~ 66行:operator=()操作符函数用于对一个已经存在的智能指针对象进行赋值。

🚩第59 ~ 60行:如果智能指针对象自己赋值给自己,什么也不做,直接返回*this做为表达式的值。请读者注意,a = a这种表达式在语法上是合法的,虽然多数编译器会给出警告信息。

🚩第61行:在绑定到新的动态对象之前,先释放对原动态对象的引用。

🚩第62 ~ 64行:绑定至新的动态对象,递增引用计数。

🚩第65行:返回*this作为表达式的值。

🚩第69 ~ 74行:间接操作符*的重载函数。当对一个智能指针使用间接操作符时,该函数会被调用,以获得其所指向的动态对象的引用。

🚩第71 ~ 72行: 如果原始指针不为空,返回所指向的动态对象的引用。

🚩第73行:对一个空指针使用间接操作符是非法的,抛出异常(exception)▲。

🚩第77 ~ 82行:指向操作符->的重载函数。当对一个智能指针使用指向操作符时,该函数会被调用。该函数预期返回原始指针。

🚩第79 ~ 80行:如果原始指针非空,返回原始指针。

🚩第81行:对一个空指针应用指向操作符是非法的,抛出异常。

🚩第85 ~ 87行:查询智能指针所指向的动态对象的引用计数,该引用计数表明当前指向该动态对象的智能指针的数量。

接下来,main()函数演示了上述SmartPointer类型的使用方法。

🚩第91 ~ 92行:创建Dora鱼、Tom鱼及相关智能指针。执行结果的第1 ~ 2行对应两条鱼的构造输出。

🚩第93行:f3的拷贝构造函数被执行,以f1为实参。执行结果的第3行对应拷贝构造函数的输出。

🚩第94行:该行代码执行前f2已存在,故f2的operator=()函数被执行,以f1为实参。执行结果的第4行对应operator=()函数的输出。当前,f2是指向Tom鱼的唯一智能指针,f2的被赋值将间接导致Tom鱼被释放,执行结果的第5行对应Tom鱼的析构输出。

🚩第95行:对智能指针f2应用间接操作符以获取f2所指向的动态对象的引用,本行事实上导致了f2的operator*()函数的执行,该函数出的输出见执行结果的第6行。以返回的Dora鱼对象的引用为基础,sayHello()成员函数被执行,执行结果的第7行可以看到来自Dora鱼的问候。

🚩第96行:对智能指针f2应用指向操作符,本行事实上导致了f2的operator->()函数的执行,其输出对应执行结果的第8行。以该函数返回的Dora鱼的原始指针为基础,sayHello()成员函数被执行,执行结果的第9行可再次看到来自Dora鱼的问候。

🚩第97行:目前为止,共有f1、f2和f3共三个智能指针指向Dora鱼,referenceCount()返回Dora鱼的引用计数,其值应为3,见执行结果的第10行。

  到了main()函数的结尾,智能指针f1、f2和f3作为栈对象,相继被析构并释放对Dora鱼的引用。其中,最后一个被析构的智能指针发现Dora鱼的引用计数归零,将其释放。执行结果的第11行,对应Dora鱼的析构输出。