一直都知道智能指针是依靠引用计数的原理实现的,但是,自己一直都没有尝试自己实现一个简单的指针指针,最近尝试自己写了一个智能指针的类,进行了简单的测试,不知道会不会有bug,代码如下:
SmartPointer.h
#include <iostream>
template <typename T>
class SmartPointer
{
public:
//构造函数
SmartPointer( T *p = 0 ):_ptr( p ), _reference_count ( new size_t ){
if( p )
*_reference_count = 1;
else
*_reference_count = 0;
}
//拷贝构造函数(浅拷贝)
SmartPointer( const SmartPointer& other )
{
if( this != &other )
{
_ptr = other._ptr;
_reference_count = other._reference_count;
(* _reference_count )++;
}
}
//重载赋值操作符
SmartPointer& operator= ( const SmartPointer& other )
{
if( _ptr == other._ptr )
return *this;
//先释放原有所指向的对象
releaseCount();
_ptr = other._ptr;
_reference_count = other._reference_count;
//增加引用计数
( * _reference_count )++;
return *this;
}
//重载运算符
T& operator*()
{
if( _ptr )
return *_ptr;
}
//重载运算符
T* operator->()
{
if( _ptr )
return _ptr;
}
//析构函数
~SmartPointer()
{
if( -- ( *_reference_count ) == 0 )
{
delete _ptr;
delete _reference_count;
}
}
private:
T *_ptr;
size_t *_reference_count;//指针
void releaseCount(){
if( _ptr )
{
(* _reference_count )--;
if( (*_reference_count) == 0 )
{
delete _ptr;
delete _reference_count;
}
}
}
};
再介绍一下常用的智能指针:
1. std::auto_ptr,有很多问题。 不支持复制(拷贝构造函数)和赋值(operator =),但复制或赋值的时候不会提示出错。因为不能被复制,所以不能被放入容器中。
2. C++11或boost的shared_ptr,基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。
3. C++11或boost的weak_ptr,弱引用。 引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。需要手动打破循环引用或使用weak_ptr。顾名思义,weak_ptr是一个弱引用,只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。
4. C++11引入的unique_ptr, 也不支持复制和赋值,但比auto_ptr好,直接赋值会编译出错。实在想赋值的话,需要使用std::move。
例如:
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // 编译会出错
std::unique_ptr<int> p3 = std::move(p1); // 转移所有权, 现在那块内存归p3所有, p1成为无效的指针.
谈到智能指针互相引用成环的问题,下面举个例子:
class B;
class A
{
public:
shared_ptr<B>
m_b;
};
class B
{
public:
shared_ptr<A>
m_a;
};
int main()
{
while (true)
{
shared_ptr<A>
a(new A);
//new出来的A的引用计数此时为1
shared_ptr<B>
b(new B);
//new出来的B的引用计数此时为1
a->m_b
= b; //B的引用计数增加为2
b->m_a
= a; //A的引用计数增加为2
}
//b先出作用域,B的引用计数减少为1,不为0,所以堆上的B空间没有被释放,且B持有的A也没有机会被析构,A的引用计数也完全没减少
//a后出作用域,同理A的引用计数减少为1,不为0,所以堆上A的空间也没有被释放
}
所以在使用基于引用计数的智能指针时,要特别小心循环引用带来的内存泄漏,循环引用不只是两方的情况,只要引用链成环都会出现问题。当然循环引用本身就说明设计上可能存在一些问题,如果特殊原因不得不使用循环引用,那可以让引用链上的一方持用普通指针(或弱智能指针weak_ptr)即可