思考一个问题,能否使用智能指针(SmartPointer)替换单链表(LinkList)中的原生指针?经过尝试发现替换后的单链表在遍历数据时程序直接爆掉,那么问题出在哪里?
首先回忆一下SmartPointer的设计实现:
指针生命周期结束时主动释放堆空间。
一片堆空间最多只能由一个指针标识。
杜绝指针运算和指针比较。
问题就出在智能指针只能由一个指针标识,当对链表进行遍历时就需要多个指针同时指向一片堆空间这就是导致程序爆掉的本质原因。
但是我们还是想将智能指针使用起来,因为能够给我们避免很多的内存管理问题,所以就有新的需求了,我们想创建两类智能指针,一种是原始的智能指针,另一种是能够多个指针同时指向的智能指针,他们都有自动管理内存的功能。
既然有新的需求我们就得有新的设计方案:
还是使用类模板完成设计。
将Pointer类作为两类智能指针的抽象父类。
Pointer继承自Object顶层父类。
Pointer类中需要重载指向操作符(->)和取值操作符(*)。
含有纯析构函数virtual ~Pointer() = 0;构建抽象类。
下面是抽象类的声明及实现:
template<typename T>
class Pointer : public Object//Pointer为抽象类
{
protected:
T* m_pointer;
public:
Pointer(T* p = NULL)
{
m_pointer = p;
}
T* operator ->()
{
return m_pointer;
}
T& operator *()
{
return *m_pointer;
}
bool isNull()
{
return (m_pointer == NULL);
}
T* get()
{
return m_pointer;
}
//要求析构函数为纯虚函数,但是可以省略,因为Object类的析构函数为纯虚函数
};
由于Object顶层父类的析构函数是纯虚函数,Pointer类继承自Object类,只要在Pointer中不含有~Pointer()函数的具体实现那么Pointer类就是抽象类。
实现了抽象父类Pointer后我们就有必要重新将SmartPointer类实现了:
首先使用类模板实现。
SmartPointer类继承自Pointer类。
带有构造函数和拷贝构造函数。
为了实现赋值及连续赋值等操作,重载赋值操作符。
最后有一个析构函数释放指针。
代码如下:
template <typename T>
class SmartPointer : public Pointer<T>
{
public:
SmartPointer(T* p = NULL) : Pointer<T>(p)
{
}
SmartPointer(const SmartPointer<T>& obj)
{
this->m_pointer = obj.m_pointer;
const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;//曾经出过错,将(obj).m_pointer写成(obj.m_pointer)
}
SmartPointer<T>& operator =(const SmartPointer<T>& obj)
{
if(this != &obj)
{
T* p = this->m_pointer;//使用this指针限定作用域
this->m_pointer = obj.m_pointer;
const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
delete p;//考虑到异常安全所以将p作为中间变量最后再销毁。
}
return *this;//返回的意义在于可以进行连续赋值
}
~SmartPointer()
{
delete this->m_pointer;
}
};
智能指针的实现本质上和以前的没什么区别,只是为了实现另一种智能指针SharedPointer,所以将他们都设计成作为子类实现。
测试代码:
class Test : public Object
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
};
int main()
{
SmartPointer<Test> sp = new Test();
SmartPointer<Test> nsp;
nsp = sp;
return 0;
}
输出结果:
Test()
~Test()
因为智能指针只能由最多一个指针标识,并且会自动内存管理,所以分别打印了一次构造函数语句和析构函数语句。
下一篇就是实现SharedPointer类,它
使得多个智能指针对象可以指向同一片堆空间,而且同时支持堆内存的自动释放。