实现智能指针

带引用计数的智能指针的实现

智能指针的原理

智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放。

常用的智能指针

unique_ptr,一个智能指针独占一块内存资源,其生命周期从 unique_ptr 指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁。不支持复制和赋值,直接赋值会编译出错。实在想赋值的话,需要使用:std::move。

1
2
3
4
//例如:
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // 编译会出错
std::unique_ptr<int> p3 = std::move(p1); // 转移所有权, 现在那块内存归p3所有, p1成为无效的指针.

shared_ptr,基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。

weak_ptr,弱引用。 引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。需要手动打破循环引用或使用weak_ptr。顾名思义,weak_ptr是一个弱引用,只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。

智能指针的实现

C++中智能指针的实现主要依赖于两个技术:

1、析构函数,对象被销毁时会被调用的一个函数,对于基于栈的对象而言,如果对象离开其作用域则对象会被自动销毁,而此时析构函数也自动会被调用。

2、引用计数技术,维护一个计数器用于追踪资源(如内存)的被引用数,当资源被引用时,计数器值加1,当资源被解引用时,计算器值减1。

3、操作符重载,智能指针的大致实现原理就是在析构函数中,检查所引用对象的引用计数,如果引用计数为0,则真正释放该对象内存。

实现带引用计数的智能指针

1、接收不同对象类型的构造函数

这个构造函数实现,比较简单,直接将引用计数+1即可。

2、析构函数

析构函数的实现,不能直接做 delete 操作,而是需要先对引用计数 -1,当引用计数为 0 时,才做 delete 操作。

3、拷贝构造函数

拷贝构造函数的实现,底层指针共享,然后将引用计数 +1 即可。

4、赋值操作符

赋值操作符的实现,稍微复杂一些,涉及到将新指向对象的引用计数 +1,将原指向对象的引用计数 -1,如果有需要还要销毁原指向对象。

为了能够使用引用计数技术,我们的智能指针不能再像原生指针那样能用可以指向任意资源对象,我们的智能指针只能指向实现了存在方法 incRefCount 和方法 decRefCount 的资源类了。

我们需要重载*运算符和->运算符:

我们使用指针最终还是需要通过指针去访问其所指向的资源对象的数据及方法,这个过程称为指针的解引用过程。指针的解引用可以使用*运算符和->运算符;

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
//带引用计数的智能指针的实现

template<typename T> //将智能指针定义成模板类
class SmartPointer {
public:
//默认构造函数
SmartPointer() :mPointer(NULL) {

}
//有参构造函数,接收不同指针类型的构造函数
SmartPointer(T *p) :mPointer(p); {

//智能指针指向类T,引用计数+1
if (mPointer) mPointer->incRefCount();
}
//拷贝构造函数
SmartPointer(const SmartPointer &tmp) :mPointer(tmp.mPointer) {

//引用计数+1
if (mPointer) mPointer->incRefCount();
}
//赋值操作符重载
SmartPointer &operator = (const SmartPointer &tmp) {

T *other(tmp.mPointer);
//新指向对象,引用计数值 +1;
if (other) other->incRefCount();
//原指向对象,引用计数值-1。如果 -1 之后引用计数为0,则销毁原资源对象
if (mPointer && mPointer->decRefCount() == 0) delete mPointer;

//智能指针指向新资源对象
mPointer = other;
return *this;
}

//析构函数
~SmartPointer() {
//实现内存资源自动销毁
if (mPointer && mPointer->decRefCount()==0) delete mPointer;
}

//重载*运算符
T& operator *(){
return *mPointer;
}

//重载->运算符
T* operator ->(){
return mPointer;
}

private:
T *mPointer; //指向智能指针实际对应的内存资源,根据参数自动推导规则,定义内部资源指针类型
};

//引用计数基类
class RefBase {
public:
//构造函数
RefBase() : mCount(0) {

}
void incRefCount() {
mCount++;
}
int decRefCount() {
return mCount--;
}
//调试接口,返回对象当前引用计数
int getRefCount() {
return mCount;
}

//析构函数
virtual ~RefBase() {

}

private:
int mCount;
};
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
// 智能指针测试代码 case 测试智能指针的引用计数功能

//继承于引用计数基类的SomeClass类
class SomeClass : public RefBase {
public:
SomeClass() { std::cout << "SomeClass default constructor !" << std::endl; }
~SomeClass() { std::cout << "SomeClass deconstructor !" << std::endl; }
};

void testcase(void)
{
SomeClass* pSomeClass = new SomeClass(); //1
SmartPointer<SomeClass> spOuter = pSomeClass;
std::cout << "SomeClass Ref Count (" << pSomeClass->getRefCount() << ") outer 1." << std::endl;
{ // inner 语句块
SmartPointer<SomeClass> spInner = spOuter;
std::cout << "SomeClass Ref Count (" << pSomeClass->getRefCount() << ") inner." << std::endl;
}
std::cout << "SomeClass Ref Count (" << pSomeClass->getRefCount() << ") outer 2." << std::endl;
// delete pSomeClass ; 不需要也不能执行delete操作!

std::cout << "new another SomeClass class for spOuter." << std::endl;
SmartPointer<SomeClass> spOuter2 = new SomeClass();
spOuter = spOuter2;// 1处new出来的SomeClass将会被自动释放
}

int main(void)
{
testcase();
return 0;
}
-------------本文结束 感谢阅读-------------
0%