zoukankan      html  css  js  c++  java
  • 句柄类(写成泛型)

    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下载地址:下载  (更新了添加模板实现和测试)

     

    点击这里给我发消息
  • 相关阅读:
    1.border-image
    CSS3 3D transform
    js表单的focus()与blur()方法
    jquery背景backgroundPosition插件
    数字反转
    js的字符串charAt()方法
    FormData使用方法详解
    封装自己的jquery插件
    webpack打包vue项目之后怎么启动&注意事项
    JavaScript中的async/await
  • 原文地址:https://www.cnblogs.com/charm/p/1790393.html
Copyright © 2011-2022 走看看