请参考:C++ Primer 第四版 13.5.1节:
当一个类具有指针型成员时,我们不能使用编译器提供的合成复制构造函数和赋值操作符进行复制和赋值,必须显式定义它们,这时也需要显式定义析构函数;
管理指针型成员有两种方法:
1. 定义智能指针类:即定义一个类,该类包含存储数据的指针成员,一个普通的整形计数变量,构造和析构函数,在析构函数中直接删除存储数据的指针成员,计数变量用来记录有多少个对象拥有该指针成员,计数变量的增减操作在包含该类指针的友元类中进行;
定义一个类,用来管理指针指针类,为该类定义一个指向智能指针类的指针型成员,当该类的对象赋值和复制时就会有多个对象同时拥有该指针,在自定义的复制构造函数和赋值操作符函数中复制智能指针型指针成员,对计数变量做加减操作,当计数变量值等于0时删除该智能指针成员;
通俗的说,包含智能指针的类在拷贝和赋值对象时,仅复制智能指针的值,多个对象共享同一个智能指针,即计数成员和数据指针成员 ;
(1)将真正存储数据的指针型成员定义在了智能指针类中,再定义一个计数变量,并将使用该类的类定义成友元类;
(2)然后在使用智能指针的类中定义一个智能指针成员,在赋值和复制时,直接对智能指针类的计数器进行加减操作,当计数器为0时删除智能指针成员;
本质上,智能指针类就是通过使用计数变量为数据指针计数,让多个对象能够安全共享同一个数据指针成员,当智能指针类的计数变量是一个普通的整形时,必须将计数变量定义在智能指针类中,然后通过其他类才能使用该类;
当将计数变量定义成一个指针时,我们可以直接使用智能指针类,而不用再通过其他类使用,这时就要将计数器增减和数据指针删除操作放在该智能指针类中;
考虑如下代码,如果将值型计数器定义在智能指针U_Ptr中:
int obj;
U_Ptr p1(&obj);
U_Ptr p2(p1); p1计数器加1,复制到p2,这时计数器一致
U_Ptr p3(p1); p1计数器加1,复制到p3,p2的计数器如何更新呢,毕竟现在有3个对象共享同一指针,除非定义另外一个类HasPtr包含智能指针类,然后使用U_Ptr;
如果将值型计数器改为指针型计数器,假如计数指针变量名为use,并且仍然定义在U_Ptr中,就不需要定义额外的类,而直接使用U_Ptr即可:
int obj;
U_Ptr p1(&obj);
U_Ptr p2(p1); p1计数器加1(*use++),复制到p2(p2.use = p1.use),这时计数器一致
U_Ptr p3(p1); p1计数器加1(*use++),复制到p3(p3.use = p1.use),p2的计数器此时也得到了更新,因为三个计数器指针都指向同一块内存,毕竟现在有3个对象共享同一指针;
相同点:他们都属于智能指针;
区别:
值型计数器:计数器和存储数据的指针被封装在一个智能指针类里,所有包含该智能指针类的类,都共享同一个智能指针类型的指针成员,该成员指向唯一一个包含数据指针和计数器的智能指针类;
指针型计数器:计数器和存储数据的指针不需要被封装在一个智能指针类里,而是直接使用,每个对象都含有各自的数据指针成员和计数器指针成员,但是所有的数据指针成员都指向同一块内存,共享同一块数据内容,所有计数器指针都指向同一块内存,共享同一个计数器值,所以需要通过显式定义拷贝构造函数和赋值操作符函数,在其中复制数据指针成员和计数器指针成员;
2. 定义值型类:就是当对象拷贝和赋值时,拷贝的不是数据指针成员的值,而是数据指针成员指向的地址空间的内容,这时不需要定义计数变量,也不需要定义额外的类;
在拷贝构造函数和赋值操作符函数中,赋值指针指向的地址空间的内容,这样,多个对象的指针成员是独立的,他们不共享一个数据指针成员;
#include <iostream>
using namespace std;
class Data;
class U_Ptr
{
private:
friend class Data; //声明为友元类,以便包含智能指针类的类能够对计数器操作;
int *ip;
size_t use; //值型计数器,如果定义为指针型计数器,那么我们就不用定义额外的类来使用该类,拷贝和赋值时,直接复制计数器指针即可,这样就可以把所有对象的计数器更新成一致的
U_Ptr() {};
~U_Ptr() { delete ip; cout << "-> U_Ptr::~U_Ptr();" << endl; }
};
class Data
{
public:
Data():ptr(NULL), value(0) {}
Data(int *p, int v);
Data(const Data &);
Data &operator=(const Data &);
~Data();
private:
int value;
U_Ptr *ptr;
};
Data::Data(int *p, int v):ptr(new U_Ptr), value(v)
{
cout << "Data::Data(int *p, int v);" << endl;
ptr->ip = p; //数据指针成员
ptr->use = 1;//值型计数器成员
}
Data::~Data()
{
if(!ptr)
{
cout << "-> Data::~Data(); ptr is NULL;" << endl;
return;
}
cout << "-> Data::~Data(); ptr->use:" << ptr->use << ";";
if(--ptr->use == 0) //友元类,所以可以直接操作智能指针类的成员
{
cout << "delete ptr;" << endl;;
delete ptr;
}
cout << endl;
}
Data &Data::operator=(const Data &obj)
{
cout << "-> Data &Data::operator=(const Data &obj);" << endl;
if(this == &obj)
{
cout << " :self assign; return; doesn't assign;" << endl;
return *this;
}
if(obj.ptr)
obj.ptr->use++;
if(ptr)
if(--ptr->use == 0)
delete ptr;
value = obj.value;
ptr = obj.ptr; //复制指针成员,该指针成员指向存储数据成员和值型计数器的智能指针类
return *this;
}
int main()
{
int *pd = new int(99);
cout << "*pd:" << *pd << endl;
// Data dt1(pd, 1);
// Data dt2(dt2);
// Data dt3 = dt1;
// Data dt4;
// dt4 = dt1;
// dt1=dt1;
Data dt5;
//dt5 = dt5;
Data dt6(dt5);
return 0;
}
运行结果:
*pd:99
-> Data::Data(const Data &obj);
:source object obj.ptr is NULL; return; doesn't copy;
-> Data::~Data(); ptr is NULL;
-> Data::~Data(); ptr is NULL;