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下载地址:下载 (更新了添加模板实现和测试)
原文链接: https://www.cnblogs.com/charm/archive/2010/08/02/1790393.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/13341
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!