智能指针专题
一、智能指针
内存泄漏是软件工程中常见的bug,在C++语言中,由于经常需要动态的申请堆空间,经常会忘记归还堆空间,C++语言没有垃圾回收机制(JAVA、Pathon有),所以指针无法控制所指向堆空间的生命周期。
普通软件 -->太慢、重启
服务器 -->越来越慢,无法使用
智能指针的概念:指针生命周期结束时,主动释放堆空间;一片堆空间最多只能由一个指针标识;杜绝指针运算和指针比较。
智能指针的设计方案:
通过类模板描述指针的行为,这样就可以定义不同类型的指针和对象。
重载指针特征操作符(--> 和 *),利用对象模拟原生指针的行为。
智能指针——SmartPointer
编程实现:
template <typename T >
class SmartPointer : public Object
{
protected:
T* m_pointer; //将会指向T类型对象的地址
public:
SmartPointer(T* p = NULL)
{
m_pointer = p;
}
SmartPointer( const SmartPointer<T>& obj) //拷贝构造函数 形参为引用。可直接修改原对象
{
m_pointer = obj.m_pointer; //仅仅这一句话,会有两个指针指向同一块空间
const_cast< SmartPointer<T>& >(obj).m_pointer = NULL; //取出CONST类型的只读属性,同一个堆空间只能有一个指针
}
SmartPointer<T>& operator= (const SmartPointer<T>& obj) //重载赋值操作符
{
if( this != &obj) //避免自赋值
{
delete m_pointer;
m_pointer = obj.m_pointer;
const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
}
return *this; //返回自身,支持连续赋值
}
T* operator-> ()
{
return m_pointer;
}
T* operator* ()
{
return *m_pointer;
}
bool isNULL()
{
return (m_pointer == NULL);
}
T* get()
{
return m_pointer;
}
~SmartPointer() //智能指针的析构函数中直接归还堆空间
{
delete m_pointer;
}
智能指针类编写完成后,在主函数中添加如下代码:
class Test
{
public:
Test()
{
cout << "Test" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
};
int main()
{
SmartPointer<Test> sp = new Test(); //指向一个test对象的地址
SmartPointer<Test> nsp;
nsp = sp;
cout << sp.isNULL() << endl;
cout << nsp.isNULL() <<endl;
return 0;
}
运行结果如下:
使用规则:创建的智能指针只能指向堆空间中的单个对象或者变量,不能指向数组和局部变量或者局部对象。
智能指针的意义在于最大程度地避免内存泄露。(通过对象来模拟指针的行为,用析构函数来释放申请的堆空间的内存)
二、再论智能指针
在单链表(LinkList)中,使用智能指针(SmartPointer)代替大量使用的原生指针可以吗?
不能。因为在智能指针使用规则中,一片堆空间最多只能由一个指针标识,而在单链表的类模板实现时,在遍历的时候,同时需要多个指针指向同一个数据元素!
新的设计方案——设计一个新的智能指针(SharedPointer)
设计Pointer类作为智能指针的抽象父类(模板):
1、纯虚析构函数 virtual ~Pointer() = 0;
2、重载operator ->();
3、重载operator *();
新的设计方案:
template <typename T>
class Pointer : public Object
{
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的顶层父类是純虚的的,所以Pointer得析构函数在不自己实现的情况下,也是纯虚的。
实现Pointer()后,将SmartPointer进行部分修改。
SharedPointer设计要点——类模板
通过计数机制(ref)标识堆内存
堆空间被指向时,ref++;
指针被置空时,ref--;
ref == 0时,释放堆内存。
注意:使用指针指向计算变量,即每一个堆空间的变量都对应着一个计数变量,且计数变量也是在堆空间创建的,生命周期一致,需要手动释放空间(free)。
由于SharedPointer支持多个对象同时指向一片堆空间,因此必须支持比较操作。
实现代码如下:
#include "Pointer.h"
#include <cstdlib>
#include "Exception.h"
namespace DTLib
{
template <typename T>
class SharedPointer : public Pointer<T>
{
protected:
int* m_ref; //指针指向计数变量 即每一个堆空间的变量都对应一个计数变量 且计数变量也是在堆上创建的,生命周期也是一致的
//通过计数机制标识堆内存,堆内存被指向时,ref++,指针被置空时,ref--,释放堆内存时,ref == 0;
void assign( const SharedPointer<T>& obj ) //函数封装赋值操作
{
this->m_ref = obj.m_ref;
this->m_pointer = obj.m_pointer;
if( this->m_ref ) //指向的计数变量合法
{
(*this->m_ref)++;
}
}
public:
SharedPointer(T* p= NULL) : m_ref(NULL) //成员变量指向计数变量的指针并初始化
{
if( p )
{
this->m_ref = static_cast<int*>(std::malloc(sizeof(int)));
if( this->m_ref)
{
*(this->m_ref) = 1; //意味着参数指针已经有一个计数变量来标志了
this->m_pointer = p;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"nO memory to create SharedPointer object ...");
}
}
}
SharedPointer(const SharedPointer<T>& obj) : Pointer<T>(NULL)
{
assign(obj); //拷贝构造函数中,要对计数变量继续自增!
}
SharedPointer<T>& operator =(const SharedPointer<T>& obj)
{
if( this != &obj) //避免自赋值
{
clear();//将当前的智能指针对象置空,不让它指向任何堆空间
assign(obj);
}
return *this;
}
void clear()
{
T* toDel = this->m_pointer;
int* ref = this->m_ref;
this->m_pointer = NULL;
this->m_ref = NULL;
if( ref ) //计算变量是否合法
{
(*ref)--; //clear后减一
if( *ref == 0)
{
free(ref);
delete toDel;
}
}
}
~SharedPointer()
{
clear();
}
};
template < typename T>
bool operator == ( const SharedPointer<T>& l,const SharedPointer<T>& r)
{
return (l.get() == r.get());
}
template < typename T>
bool operator != ( const SharedPointer<T>& l,const SharedPointer<T>& r)
{
return !(l == r); //使用相等比较来实现不相等
}
}
主函数代码:
class Test : public Object
{
public:
int value;
Test() : value(0)
{
cout<< "Test" <<endl;
}
~Test()
{
cout<< "~Test" <<endl;
}
};
int main()
{
const SharedPointer<Test> sp0 = new Test(); //只读的
//sp0->value = 100; const变量不能赋值
SharedPointer<Test> sp1 = sp0;
SharedPointer<Test> sp2 = NULL;
sp2 = sp1;
sp2->value = 100;
cout<< sp0->value << endl; //多个指针指向同一片内存空间
cout<< sp1->value << endl;
cout<< sp2->value << endl;
sp2.clear();
cout << ( sp0 == sp1) <<endl;
cout << ( sp0 == sp2) <<endl;
sp1.clear();
cout << ( sp0 == sp1) <<endl;
cout << ( sp0 == sp2) <<endl;
cout<< sp0->value << endl;
//sp0.clear(); const变量
//delete sp0; 不能使用delete释放智能指针指向的堆空间,不能释放两次。
//cout<< "sp0->value" << endl;
//cout<< sp0 << endl; //内存已经释放 指针被删除
return 0;
}
智能指针的使用军规:
只能用来指向堆空间中的单个变量(对象);
不同类型的智能指针对象不能混合使用;
不要使用delete释放智能指针指向的堆空间