句柄类(写成泛型)

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

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午12:42
下一篇 2023年2月7日 下午12:42

相关推荐