智能指针的核心思想是将资源(内存)与指针对象的生命周期绑定起来,创建对象的同时立即为其分配资源,对象销毁时立即释放资源。
1、智能指针和资源的绑定
将裸指针和它相关的操作封装成一个类,并在创建对象时必定要调用的构造函数中,分别进行资源的分配和释操作。当然,为了保证对象的操作方式看起来与裸指针相同,这个封装类起码还要重载operator->和operator *
一个简单的,在其生命周期中绑定了一个资源的智能指针类模板My_ptr代码如下:
#include<iostream>
using namespace std;
template<class T>
class My_ptr
{
private:
T* _ptr; //裸指针
public:
My_ptr(T* p):_ptr(p)
{
cout << "生成获得资源" << endl;
}
T& operator*() //重载*运算符
{
return *_ptr;
}
T* operator->() //重载->运算符
{
return _ptr;
}
~My_ptr()
{
cout << "死前释放资源" << endl;
//delete _ptr;
}
};
//测试代码
int main()
{
int a = 100;
{
My_ptr<int> p(&a); //指针对象p生命期开始
cout << "打印资源数据" << *p << endl;
} //指针对象p生命周期结束
cout << "指向对象p生命周期已结束" << endl;
return 0;
}
//输出结果如下:
生成获得资源
打印资源数据100
死前释放资源
指向对象p生命
在程序设计实践中常常需要多个指针指向(共享)同一个资源,这时,资源的释放就不能单纯地取决于某一个指针对象的生命期。既不能在还有指针使用资源的时候其他指针擅自释放资源,也不能在都不需要资源时没有即时释放资源。
目前,采用指针计数器作为资源管理者,是实现这种智能指针的一种通用实现技术。资源使用者以令计数值加1的方式向管理者登记,以减1的方式进行注销,当计数器值为0时,由管理者进行释放资源
template<typename T>
class Res_Ptr
{
private:
T* res_p; //指向资源的指针
int use_num; //计数器
//构造函数
Res_Ptr(int *p):res_p(p),use_num(1){}
~Res_Ptr
{
delete res_p;
}
friend class SharedPtr;
};
类中以友元类的方式指定了资源使用者的所属类SharePtr。由于这个类的析构函数仅完成了资源释放工作,所以使用者类除了在构造函数中为计数器加1之外,还必须在其析构函数中为计数器减1,并判断是否应该释放资源
template<typename T>
class SharePtr
{
public:
//构造函数
SharePtr(T* p, T i): ptr(new Res_Ptr<T>(p)), val(i){}
//复制构造函数
SharePtr(const SharePtr& orig) :ptr(orig.ptr), val(orig.val)
{
++ptr->use_num; //指向同一资源指针对象数目加1
}
~SharePtr()
{
if (--ptr->use_num == 0) //析构一个指针对象,计数器减去1
delete ptr; //如果计数器为0,则删除资源对象
}
private:
Res_Ptr<T>* ptr; //指向资源对象Res_Ptr的指针
T val; //可以修改资源数据的变量
};
将上面两个类连起来编写一个程序
#include<iostream>
using namespace std;
template<typename T> class SharePtr;
template<typename T>
//定义计数类模板
class Res_Ptr
{
friend class SharePtr<T>;
private:
T* res_p; //指向资源的指针
int use_num; //计数器
//构造函数
Res_Ptr(int *p):res_p(p),use_num(1)
{
cout << "Res_Ptr构造函数" << endl;
}
~Res_Ptr()
{
delete res_p;
cout << "Res_Ptr析构函数" << endl;
}
};
//SharePtr模板声明
template<typename T>
class SharePtr
{
public:
//构造函数
SharePtr(T* p, T i): ptr(new Res_Ptr<T>(p)), val(i)
{
cout << "SharePtr构造函数" << "use_num = " << ptr->use_num << endl;
}
//复制构造函数
SharePtr(const SharePtr& orig) :ptr(orig.ptr), val(orig.val)
{
++ptr->use_num; //指向同一资源指针对象数目加1
cout << "SharePtr复制构造函数" << "use_num = " << ptr->use_num << endl;
}
~SharePtr()
{
cout << "SharePtr析构函数" << "use_num = " << ptr->use_num << endl;
if (--ptr->use_num == 0) //析构一个指针对象,计数器减去1
delete ptr; //如果计数器为0,则删除资源对象
}
private:
Res_Ptr<T>* ptr; //指向资源对象Res_Ptr的指针
T val; //可以修改资源数据的变量
};
//测试代码
int main()
{
{
SharePtr<int>hpa = SharePtr<int>(new int(42), 100); //构造第一个指针对象,指向资源为整型数据42
{
SharePtr<int>hpb(hpa); //复制构造指针对象
SharePtr<int>hpc(hpb);
SharePtr<int>hpd = hpa;
};
cout << "内层括号结束" << endl;
}
cout << "中层括号结束" << endl;
return 0;
}
//输出结果
Res_Ptr构造函数
SharePtr构造函数use_num = 1
SharePtr复制构造函数use_num = 2
SharePtr复制构造函数use_num = 3
SharePtr复制构造函数use_num = 4
SharePtr析构函数use_num = 4
SharePtr析构函数use_num = 3
SharePtr析构函数use_num = 2
内层括号结束
SharePtr析构函数use_num = 1
Res_Ptr析构函数
中层括号结束
在C++11的标准中也定义了三大智能指针,uniquer_ptr、shared_ptr和weak_ptr,具体使用方式可以参考C++ Primer 5th