最近在自己写智能指针的时候,遇到了一些小的疑惑,也纠正了之前的一些理解上的偏差。
首先是最简单的智能指针的一个实现:
template<class T>
class SmartPointer{
private:
T* ptr;
public:
SmartPointer(T* p) :ptr(p){} //构造函数
T& operator*(){ //重载*操作符
return *ptr;
}
T* operator->(){ //重载->操作符
return ptr;
}
~SmartPointer(){ //析构函数
delete ptr;
}
};
这与auto_ptr类似,可以完成自动释放内存的功能,但一旦程序员使用了如下的方式:
class A{};
SmartPointer<A> ptr(new A);
SmartPointer<A> ptr1=ptr;
会导致程序崩溃,因为ptr和ptr1两个智能指针会重复delete源指针,所以stl里有了shared_ptr。
下面看一下加入了引用计数的智能指针的实现:
template<class T>
class SharedPointer{
public:
explicit SharedPointer(T* p) :ptr(p),count(NULL){ //构造函数,不允许隐式转换
count = new size_t(1);
}
SharedPointer(const SharedPointer& orig):ptr(orig.ptr),count(orig.count){ //复制构造函数
++*count;
}
SharedPointer& operator=(const SharedPointer& rhs){ //赋值构造函数
++*(rhs.count);
if (--*count == 0){ //防止自我赋值时误删
delete ptr;
}
ptr = rhs.ptr;
count = rhs.count;
return *this;
}
~SharedPointer(){ //析构函数
if (--*count == 0)
delete ptr;
}
T& operator*(){
return *ptr;
}
T* operator->(){
return ptr;
}
T* getOriPtr(){
return ptr;
}
private:
T *ptr;
size_t* count;
};
在SharedPointer里添加了一个指向计数的指针count,每当智能指针被复制构造或者赋值构造的时候,count指向的值都会自增,当count指向的值为0时,智能指针的析构函数才会调用delete释放原始指针的内存。
这里发现了两种可能导致误用的例子,是我在实际实用中发生的。
1.让智能指针指向栈区:
class A{};
A a;
SharedPointer<A> ptr(&a);
这样的使用,编译器将不会报错,但是由于a是存储在栈区的对象,在进程结束前将自动调用析构函数,而我们的智能指针ptr会对栈区使用delete,导致程序崩溃。
2.用同一个源指针构造两个不同的智能指针:
class A{};
A* p=new A;
SharedPointer<A> ptr(p);
SharedPointer<A> ptr1(p);
这样的使用,将导致指针p被两次调用delete;原因是count指向的内存是在构造函数中分配的,ptr和ptr1都是通过构造函数构造出来的,其count分别指向两个不同地址,其值都为1,在ptr析构时,ptr的count指向的值变为0,发生delete p;接下来在ptr1析构时,ptr1的count指向的值变为0,再发生delete p;这样就导致了程序崩溃。
所以在使用智能指针时,应该统一采用如下使用方式:
class A{};
SharedPointer<A> ptr(new A);
SharedPointer<A> ptr1(new A);
这样就可以保证不会出现如上两种错误。