C++ 句柄类

Posted ZHOU YANG

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 句柄类相关的知识,希望对你有一定的参考价值。

一、容器与继承

    在容器中保存有继承关系的对象时,如果定义成保存基类对象,则派生类将被切割,如果定义成保存派生类对象,则保存基类对象又成问题(基类对象将被强制转换成派生类对象,而派生类中定义的成员未被初始化)。

    唯一的可行的选择是容器中保存对象的指针。但是需要用户管理对象和指针。C++中一个通用的技术是包装类(cover)或句柄类(handle)。用句柄类存储和管理类指针。

     句柄类大体上完成两方面的工作:

  1. 管理指针,这与智能指针的功能类似。

  2. 实现多态,利用动态绑定,是得指针既可以指向基类,也可以指向派生类。

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

  1. 像对任何保存指针的类一样,必须确定对复制控件做些什么。包装了继承层次的句柄通常表现得像一个智能指针或者像一个值。

  2. 名柄类决定句柄接口屏蔽还是不屏蔽继承层次,如果不屏蔽继承层次,用户必须了解和使用基本层次中的对象(objects in theunderlying hierarchy)。

下面通过一个我自己写的一个简单的例子来说明这个问题:

    这个例子程序包括一个基类,一个派生类,还有一个句柄类。

    其中,基类有2个私有成员,数值m_base和程序名字name。派生类有一个新的私有成员,m_der。

    派生类和基类有虚函数compute。基类的compute它计算基类成员m_base平方。派生类的compute计算m_base平方和m_der之和。

    句柄类有两个数据成员,分别是指向引用计数的指针( 这里必须是指针,复制时引用计数复制指针的值,保证一个实例化对象只有一个引用计数)和指向基类或者是其派生类的指针。

#include<iostream>
#include<string>
#include<exception>
using namespace std;
// base class
class Base {
public:
    //basic constructor
    Base(int m_base = 1, string name = "Base")
            : m_base(m_base), name(name) {
        cout << "Base constructor called!" << endl;
    }
    //copy constructor
    Base(const Base &base) : Base(base.m_base, base.name) {
        cout << "Base copy called" << endl;
    }
    virtual Base *clone() const {
        return new Base(*this);
    }
    const string getName() {
        return name;
    }
    virtual int compute() const {
        return m_base * m_base;
    }
    virtual ~Base(){
        cout<<"Base deleted"<<endl;
    }
protected:
    int m_base;
    string name;
};
class Derived : public Base {
public:
    //basic constructor
    Derived(int m_base, string name, int m_der)
            : Base(m_base, name), m_der(m_der) {
        cout << "Derived constructor called" << endl;
    }
    //copy constructor
    Derived(const Derived &derived) : Derived(derived.m_base, derived.name, derived.m_der) {
        cout << "Derived copy called" << endl;
    }
    virtual Derived *clone() const {
        return new Derived(*this);
    }
    virtual int compute() const {
        //调用父类中定义的方法
        return Base::compute() + m_der;
    }
    virtual ~Derived(){
        cout<<"Derived deleted"<<endl;
    }
private:
    int m_der;
};
class Handler {
public:
    //默认构造函数
    Handler() : pBase(NULL), use(new int(1)) { }
    //一般构造函数
    Handler(const Base &item) : pBase(item.clone()), use(new int(1)) { }
    //复制构造函数
    //每复制一次,引用计数就加1
    Handler(const Handler &ref) : pBase(ref.pBase), use(ref.use) {
        ++*use;
    }
    //重载赋值操作符
    Handler &operator=(const Handler &right) {
        ++*(right.use);
        decrese_use();
        pBase = right.pBase;
        use = right.use;
        return *this;
    }
    //重载箭头操作符
    const Base *operator->() const {
        if (pBase)
            return pBase;
        else
            throw logic_error("unbound Handler!");
    }
    //重载解引用操作符
    const Base &operator* () const{
        if(pBase)
            return *pBase;
        else
            throw logic_error("unbound Handler");
    }
    void print_use() {
        cout << pBase->getName() << " use: " << *use << endl;
    }
    //析构函数
    ~Handler() {
        decrese_use();
    }
private:
    //此处必须使用指针,保证一个Base实例只对应一个引用计数
    int *use;
    Base *pBase;
    void decrese_use() {
        if (--*use == 0) {
            cout << pBase->getName() << " is going to be deleted!" << endl;
            delete pBase;
        }
    }
};
int main() {
    Handler h1(Base(2,"Base"));
    h1.print_use();
    cout<<"Base compute:"<<(*h1).compute()<<endl;
    Handler h2(h1);
    h2.print_use();
    cout<<"Base compute:"<<(*h2).compute()<<endl;
    cout<<"-------------------------------------"<<endl;
    Handler h3(Derived(3,"derived",3));
    h1=h3;
    h1.print_use();
    cout<<"Derived compute:"<<(*h1).compute()<<endl;
    cout<<"system automatic delete begin"<<endl;
    return 0;
}

二、句柄类

    句柄类Handle 有3个构造函数:默认构造函数,复制构造函数,和接收基类Base对象的构造函数。为了保证 在接收基类Base对象的构造函数中 复制具体对象的时候实现动态调用,得到正确类别的实例,我们在类中定义了虚函数clone

Base

virtual Base *clone() const {
        return new Base(*this);
}

Derived

virtual Derived *clone() const {
        return new Derived(*this);
}

三、运行结果   

主函数调用:

int main() {
    Handler h1(Base(2,"Base"));
    h1.print_use();
    cout<<"Base compute:"<<(*h1).compute()<<endl;
    Handler h2(h1);
    h2.print_use();
    cout<<"Base compute:"<<(*h2).compute()<<endl;
    cout<<"-------------------------------------"<<endl;
    Handler h3(Derived(3,"derived",3));
    h1=h3;
    h1.print_use();
    cout<<"Derived compute:"<<(*h1).compute()<<endl;
    cout<<"system automatic delete begin"<<endl;
    return 0;
}

输出:

Base constructor called!
Base constructor called!
Base copy called
Base deleted
Base use: 1
Base compute:4
Base use: 2
Base use: 2
Base compute:4
-------------------------------------
Base constructor called!
Derived constructor called
Base constructor called!
Derived constructor called
Derived copy called
Derived deleted
Base deleted
derived use: 2
derived use: 2
Derived compute:12
system automatic delete begin
Base is going to be deleted!
Base deleted
derived is going to be deleted!
Derived deleted
Base deleted

  主函数中使用Base对象创建了Handler对象h1,并由h1构造Handler对象h2,通过输出可以发现Handler对象的引用计数由1变为2。然后使用Derived对象创建Handler对象h3,并将其赋值给h1,对h1,h3 输出其引用计数,可知引用计数均为2.

以上是关于C++ 句柄类的主要内容,如果未能解决你的问题,请参考以下文章

《C++沉思录》——类设计核查表代理类句柄类

从 《Accelerated C++》源码学习句柄类

片段句柄 onactivityresult 内结果的 StartActivity

[引用区别] c++中引用与java中引用区别

在 C++ 中打开一个 dll(获取句柄)

终于懂了:Delphi重定义消息结构随心所欲,只需要前4个字节是消息编号就行了,跟Windows消息虽然尽量保持一致,但其实相互没有特别大的关系。有了这个,就有了主动,带不带句柄完全看需要。(代码片段