智能指针与句柄详解(一)

前言:
智能指针与引用计数详解(一)中提到实现智能指针有两种方法,一种是引用计数,另一种就是句柄类实现。

什么是句柄类?
句柄类是用来存储和管理基类指针。指针所指对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类型对象。用户通过句柄类访问继承层次的操作。因为句柄类使用指针执行操作,虚成员的行为将在运行时根据句柄实际绑定的对象的类型而变化。因此,句柄的用户可以获得动态行为但无须操心指针的管理。句柄本身也可以是某个C++对象,可以用来提供和指针一致的功能。

句柄类带来的好处:

  • 可以在实现中用运行时绑定而不是编译时绑定的方式来处理对象;
  • 可以对他人隐藏对象的实现。

包装了继承层次的句柄类有两个重要的设计因素:

  • 像对任何保存指针的类一样,必须确定对复制控制做些什么。包装了继承层次的句柄通常表现得像一个智能指针或者像一个值。
  • 句柄类决定句柄接口屏蔽还是不屏蔽继承层次,如果不屏蔽继承层次,用户必须了解和使用基本层次中的对象。

句柄分类:

  • 指针型句柄
  • 值类型句柄

指针型句柄
在句柄类中复制的是基类指针而不是对象,即多个指针共享一个对象。
实例:
定义一个名为Sales_item的指针型句柄类,表示Item_base层次。Sales_item的用户将像使用指针一样使用它:用户将Sales_item绑定到Item_base类型的对象并使用*和->操作符执行Item_base的操作:

定义句柄(Sales_item):
Sales_item类有三个构造函数:默认构造函数、复制构造函数和接受Item_base对象的构造函数。第三个构造函数将复制Item_base对象,并保证:只要Sales_item对象存在副本就存在。当复制Sales_item对象或给Sales_item对象赋值时,将复制指针而不是复制对象。像对其它指针型句柄类一样,将用引用计数来管理副本。
sales_item.h

#ifndef _Sales_item_h
#define _Sales_item_h

#include "item_base.h"

class Sales_item 
{
public:
    Sales_item();
    Sales_item(const Sales_item& sales_item);
    Sales_item(const Item_base& item_base);
    ~Sales_item();

    Sales_item& operator=(const Sales_item& sitem);
    const Item_base* operator->() const;
    const  Item_base& operator*() const; 

private:
    Item_base *m_item_base;
    int *m_useCount;

    void decr_use();
};
#endif

sales_item.cpp

#include "sales_item.h"
#include <iostream>
#include <exception>
using namespace std;

Sales_item::Sales_item()
    :m_item_base(NULL)
    ,m_useCount(new int(1))
{
    cout<<"Sales_item constructor"<<endl;
}

Sales_item::Sales_item(const Sales_item& sales_item)
    :m_item_base(sales_item.m_item_base)
    ,m_useCount(sales_item.m_useCount)
{
    cout<<"Sales_item copy constructor"<<endl;
    ++*m_useCount;
}

Sales_item::Sales_item(const Item_base& item_base)
    :m_item_base(item_base.clone())
    ,m_useCount(new int(1))
{
    cout<<"Sales_item item_base constructor m_useCount = "<<*m_useCount<<endl;
}

Sales_item& Sales_item::operator=(const Sales_item& sitem)
{
    bool isSame = this->m_item_base == sitem.m_item_base;
    cout<<"Sales_item assignment isSame = "<<isSame<<endl;
    if (!isSame) {
        ++*sitem.m_useCount;
        decr_use();
        m_item_base = sitem.m_item_base;
        m_useCount = sitem.m_useCount;
    }
    return *this;
}

Sales_item::~Sales_item()
{
    cout<<"Sales_item destruct"<<endl;
    decr_use();
}

const Item_base* Sales_item::operator->() const
{
    cout<<"Sales_item operator->"<<endl;
    if (m_item_base) {
        return m_item_base;
    }
    else throw ("unbound Sales_item");
}

const Item_base& Sales_item::operator*() const
{
    cout<<"Sales_item operator*"<<endl;
    if (m_item_base) {
        return *m_item_base;
    }
    else throw ("unbound Sales_item");
}

void Sales_item::decr_use()
{
    cout<<"Sales_item decr_use m_useCount = "<<*m_useCount<<endl;
    if (-- *m_useCount == 0) {
        delete m_item_base;
        delete m_useCount;
    }
}

相对于智能指针与引用计数详解(一)中的智能指针,主要有以下区别:
这里的句柄类中已经自己完成引用计数功能,不需要计数类;
接受Item_base对象的构造函数中运用了原型模式,主要原因是管理的基础指针涉及继承层次问题。

item_base.h

#ifndef _Item_base_h
#define _Item_base_h

class Item_base 
{
public:
    Item_base();
    virtual Item_base* clone() const;
    virtual ~Item_base();
    virtual  int net_price() const;
};

#endif

item_base.cpp

#include "item_base.h"
#include <iostream>

const int PRICE = 20;
using namespace std;


Item_base::Item_base()
{
    cout<<"Item_base constructor"<<endl;
}

Item_base* Item_base::clone() const
{
    cout<<"Item_base clone"<<endl;
    return new Item_base(*this);
}

Item_base::~Item_base()
{
    cout<<"Item_base destruct"<<endl;
}

int Item_base::net_price() const
{
    return PRICE;
}

bulk_item.h

#ifndef _Bulk_item_h
#define _Bulk_item_h

#include "item_base.h"

class Bulk_item : public Item_base
{
public:
    Bulk_item();
    virtual Bulk_item* clone() const;
    ~Bulk_item();
    virtual int net_price() const;
};
#endif

bulk_item.cpp

#include "bulk_item.h"
#include <iostream>

const int BULKPRICE = 30;
using namespace std;

Bulk_item::Bulk_item()
{
    cout<<"Bulk_item constructor"<<endl;
}

Bulk_item* Bulk_item::clone() const
{
    cout<<"Bulk_item clone"<<endl;
    return new Bulk_item(*this);
}

Bulk_item::~Bulk_item()
{
    cout<<"Bulk_item destruct"<<endl;
}

int Bulk_item::net_price() const
{
    return BULKPRICE;
}

main.cpp

    Item_base ib;
    Sales_item si(ib);
    Bulk_item bulki;
    Sales_item sbi(bulki);
    int siprice = si->net_price();       //正常输出20
    int price = sbi->net_price();       //正常输出30
    cout<<"siprice = "<<siprice<<endl;
    cout<<"price = "<<price<<endl;

运行结果:

// Item_base 类构造函数执行(对应代码:Item_base ib;)
Item_base constructor
// Sales_item 类接受Item_base 类构造函数执行(Sales_item si(ib);)
Item_base clone
Sales_item item_base constructor m_useCount = 1
// Bulk_item构造函数执行,根据继承关系,默认构造函数执行顺序是基类——>派生类
Item_base constructor
Bulk_item constructor
Bulk_item clone
Sales_item item_base constructor m_useCount = 1
// 句柄类重载“->”运算符,所以对应代码(si->),根据重载运算符逻辑,返回的是被管理的基础指针(Item_base ),所以能直接调用对应的成员函数。对于用户来说是透明的,感觉net_price函数是Sales_item 类的一样。
Sales_item operator->
Item_base net_price
// 对应代码(sbi->),由于多态原理,所以能够输出各自正确的值。
Sales_item operator->
Bulk_item net_price
siprice = 20
price = 30
//程序执行完毕,会回收对象(sbi),对应的是decr_use函数(delete m_item_base;)
Sales_item destruct
Sales_item decr_use m_useCount = 1
Bulk_item destruct        // 由于delete m_item_base对应的m_item_base指向的对象是Bulk_item
Item_base destruct
//回收bulki,析构函数是虚函数,所以执行顺序是派生类——>基类
Bulk_item destruct
Item_base destruct
//回收si
Sales_item destruct
Sales_item decr_use m_useCount = 1
Item_base destruct
//回收ib
Item_base destruct

小结:
句柄是C++中的一种普遍使用的实现技巧。它们赋予了类设计者足够的弹性来处理不定大小的实现,减少编译期的依赖,阻止他人获得类的实现细节,以及为同一个抽象数据类型提供不同的实现。在C++中,动态绑定仅在通过引用或指针调用时才能应用于声明为虚的函数。C++程序定义继承层次接口的句柄类很常见,这些类分配并管理指向继承层次中对象的指针,因此能够使用户代码在无须处理指针的情况下获得动态行为。

扩展:
上述的例子介绍了指针型句柄,另外还有值型句柄,同时为了适配可以用模板来实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值