1.有些时候我们需要用容器来保存因继承而相关的对象(摘自C++Primer)也就是一个容器里面既有基类对象和派生类对象,但是对象不是多态的,这就出现一些问题:
a.比如我们把这个容器设置成派生类类型B,则当我们保存基类对象时候派生类部分的成员是没有初始化的,后果就是当这个对象调用派生类B的某些成员时候不知道会调用了哪些内存数据;
b.假如都统一设成基类对象A,则所有的派生类对象都截断了派生部分的数据。
句柄类的就是为了解决上述问题:
根据对象不同而调用不同的构造函数,前面我们说过动态绑定。
句柄类就是基于这样一种思想而构思出来的:下面以书上例子来简要说一下
句柄类保存的是目标对象的指针,并且会根据对象类型来初始化不同的指针(这用了动态绑定):
//具有继承关系的两个对象 只列出关键部分成员
class Item_base{
public:
virtual Item_base* clone() const //关键的虚函数哦
{
return new Item_base(*this);
}
};
class Bulk_base:public Item_base{
public:
Bulk_base* clone() const
{
return new Bulk_base(*this);
}
};
句柄类就保存Item_base的指针,该指针会在运行时根据动态类型来构造相应对象:
class Sales_item{
public:
Sales_item(const Item_base& item):p(item.clone())
,use(new int(1)){} //这个构造函数是关键 这里会根据item的类型(可能是基类可能是派生类)来正确构造
private:
Item_base *p;
int *use;
};
2.好了,能做到了根据指针的动态类型而正确地构造对象,这已经达到目的了,剩下的工作就是:
a.确保保存的指针指向的对象在Sale_item生命周期内不被删除,这个就是运用引用计数来实现了;
(插曲:为什么需要引用计数?因为保存的是指针而不是副本,句柄类中的析构函数要在适当情况下才能删除这个指针,而不是析构就一定要删除,因为当句柄类发生赋值,复制时候,就是有2个或以上的句柄类对象拥有同一个指针成员p,假如s1,s2的指针同样指向同一块内存区域,但s1生命周期结束而s2没结束,s1却把这个指针删除了,那s2中的p就成了野指针了。)
b.编写适当的复制构造函数(句柄类发生了动态分配内存),复制操作符,虚析构函数(继承层次中必须要自定义且为虚函数),就是我们之前所讲的
书上这个例子我简单地完善和写了个demo,有需要的可以下载附件参考下。
demo大概能完成如下功能:
vector<Sales_item> si; //注意这个不能初始化哦
Item_base i1(7,2.2),i2(2,1.5),i3(5,4.5),i4(6,4.6),i5(3,3.4);
Bulk_base b1(7,2.2),b2(2,1.5),b3(5,4.5),b4(6,4.6),b5(3,3.4);
Item_base i[]= {i1,i2,i3,i4,i5};
Bulk_base b[]= {b1,b2,b3,b4,b5};
for(int k=0;k<5;k++)
si.push_back(Sales_item(i[k])); //Sales_item(i[k])调用了 Sales_item(const Item_base& item):p(item.clone())这个构造函数
for(int k=0;k<5;k++) //而item.clone又是虚函数,会根据指针的动态类型来调用,从而实现了正确的构造对象
si.push_back(Sales_item(b[k]));
sort(si.begin(),si.end(),compare);
vector<Sales_item>::iterator it;
for ( it = si.begin();it!=si.end();it++)
{
std::cout<<it->total_price()<<std::endl;//这里就可以根据不同的对象类型来计算了
}
3.用模板来实现这个句柄:
template <class T>
class Handle{
public:
Handle():p(0),use(new int(1)){}
Handle(const T& item):p(item.clone()),use(new int(1)){}
Handle(const Handle& i):p(i.p),use(i.use){AddRef();} //被复制副本当然要+1了
~Handle(){Release();}
Handle& operator=(const Handle& rhs)
{
AddRef(); //防止自身赋值,假如左右对象一样,即自身赋值时
Release(); //该对象计数use至少为2了吧,那再自减1的时候也不会
p = rhs.p; //误删除p指针了;而非自身赋值情况下,左操作数就先自加1
use = rhs.use; //然后右操作数必须自减1并检测是否为0(需要被覆盖了嘛)
return *this; //然后剩下就是跟复制构造函数的成员赋值了
}
const T* operator->() const
{
if(p)
return p;
else
throw std::logic_error("Unbound Handle");
}
const T& operator*() const
{
if (p)
return *p;
else
throw std::logic_error("Unbound Handle");
}
double total_price() const
{
return p->net_price();
}
private:
T *p;
int *use;
void AddRef()
{
++*use;
}
void Release()
{
if (--(*use)==0)
{
delete p;
delete use;
}
}
};
demo下载地址:下载 (更新了添加模板实现和测试)