onesimple

智能指针的实现机理

  智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数(reference count),实现指针指向的对象的共享的。

  • 介绍

其实现的基本思想:

  1.每次创建类的新对象时,初始化指针并将引用计数置为1;
  2.当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
  3.对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数减至0,则删除对象),并增加右操作数所指对象的引用计数;
  4.调用析构函数时,减少引用计数(如果引用计数减至0,则删除基础对象);
  4.重载“->”以及“*”操作符,使得智能指针有类似于普通指针的操作。
  根据以上分析,首先可以得出下面的类模板原型。

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
template <class T>
class SmartPointer
{
public:
SmartPointer(T *p = 0);//构造函数
SmartPointer(const SmartPointer& src);//拷贝构造函数
SmartPointer& operator = (const SmartPointer& rhs);//赋值函数
T* operator -> ();//重载->
T& operator * ();//重载*
~SmartPointer();//析构函数
private:
void MinusRef()
{
//被其他成员函数所调用
if (--*m_pRef == 0)//自身的引用计数减1
{
//如果计数为0,则释放内存
delete m_ptr;
delete m_pRef;
}
}
T *m_ptr; //保存对象指针
size_t *m_pRef; //保存引用计数
};

  上面的私有成员函数MinusRef将引用计数减1。如果引用计数减至0,则删除m_ptr所指对象。根据前面的分析,MinusRef只被赋值函数以及析构函数使用。
下面说明各个成员的具体定义。
  首先是构造函数与析构函数的定义。普通构造函数中,m_ptr与p指向同一块内存,并初始化引用计数为1。拷贝构造函数中与普通构造函数的不同之处为引用计数需要加1。析构函数调用私有成员MinusRef对引用计数递减,并且判断是否需要释放对象。代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<class T>
SmartPointer<T>::SmartPointer(T *p) //普通构造函数
{
m_ptr = p; //m_ptr与p指向同一内存
m_pRef = new size_t(1); //m_pRef初值为1
}
template<class T>
SmartPointer<T>::SmartPointer(const SmartPointer<T>& src)//拷贝构造函数
{
m_ptr = src.m_ptr; //m_ptr与src.m_ptr指向同一内存
m_pRef++;
m_pRef = src.m_pRef; //拷贝引用计数
}
template<class T>
SmartPointer<T>::~SmartPointer() //析构函数
{
MinusRef(); //引用减1,如果减后的引用为0,则释放内存
std::cout<<"SmartPointer: Destructor"<<std::endl;
}

  接下来是“->”和“*”的重载。这两个函数很简单,只需要分别返回m_ptr以及m_ptr所指的内容即可。注意,如果m_ptr此时为空,则应该抛出异常。代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template<class T>
T* SmartPointer<T>::operator -> () //重载 ->
{
if (m_ptr)
return m_ptr;
//m_ptr为NULL时,抛出异常
throw std::runtime_error("access through NULL pointer");
}
template<class T>
T& SmartPointer<T>::operator * () //重载 *
{
if (m_ptr)
return *m_ptr;
//m_ptr为NULL时,抛出异常
throw std::runtime_error("dereference of NULL pointer");
}

  最后是赋值函数的实现:

1
2
3
4
5
6
7
8
9
10
template<class T>
SmartPointer<T>& SmartPointer<T>::operator = (const SmartPointer<T>& rhs)//赋值函数
{
++*rhs.m_pRef; //rhs的引用加1
MinusRef(); //自身指向的原指针的引用减1
m_ptr = rhs.m_ptr; //m_ptr合rhs.m_ptr指向同一个对象
m_pRef = rhs.m_pRef; //复制引用
return *this;
}

  这样,就可以像 std::auto_ptr那样来使用SmartPointer。以下先定义一个测试类,测试程序如下。

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
class Test
{
public:
Test() {name = NULL;}
Test(const char* strname)//构造函数
{
name = new char[strlen(strname)+1];//分配内存
strcpy(name, strname);//拷贝字符串
}
Test& operator = (const char *namestr)//赋值函数
{
if (name != NULL)
{
delete name;//释放原来的内存
}
name = new char[strlen(namestr)+1];//分配新内存
strcpy(name, namestr);//拷贝字符串
return *this;
}
void ShowName() {cout << name << endl;}
~Test()
{
if (name != NULL)
{
delete name;
}
name = NULL;
cout << "delete name" << endl;
}
public:
char *name;
};
int _tmain(int argc, _TCHAR* argv[])
{
SmartPointer<Test> t1;//空指针
SmartPointer<Test> t2(new Test("fefd"));
SmartPointer<Test> t3(new Test("wewew"));
try
{
t1->ShowName();//空指针调用抛出异常
}
catch (const exception& err)
{
cout << err.what() << endl;
}
t2->ShowName(); //使用t2调用showName()
*t2 = "David"; //使用t2给对象赋值
t2->ShowName(); //使用t2调用showName()
t2 = t3; //赋值,原来t2的对象引用为0,发生析构
//而t3的对象引用加1
cout << "End of main..." << endl;
getchar();
return 0;
}

  main函数代码第41行,t1指向一个NULL指针,因此调用ShowName时会出现异常(异常在重载的“->”函数中被抛出)。
  main函数代码第48~50行,使用SmartPtr对象对Test对象进行操作,其方法与使用Test对象指针的操作方法相同。
  main函数代码第51行,对t2进行赋值操作,操作完成后,t2引用的原对象发生析构(此对象没有SmartPtr对象引用了),t2和t3引用同一个对象,于是这个对象的引用计数加1。注意,这里我们并没有显示地对t2所引用的原对象进行释放操作,这就是智能指针的精髓所在。

🐶 五百年雨打的石桥,有你走过 🐶