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.

时间: 2024-10-12 19:36:53

C++ 句柄类的相关文章

句柄类与继承

前一小节<容器与继承>http://blog.csdn.net/thefutureisour/article/details/7744790提到过: 对于容器,假设定义为基类类型,那么则不能通过容器訪问派生类新增的成员:假设定义为派生类类型,一般不能用它承载基类的对象,即使利用类型转化强行承载,则基类对象能够訪问没有意义的派生类成员,这样做是非常危急的.对这个问题的解决的方法,是使用容器保存基类的指针. 在C++中,这类问题有一种通用的解决的方法,称为句柄类.它大体上完毕双方面的工作: 1.管

C++ Primer 学习笔记_72_面向对象编程 --句柄类与继承[续]

面向对象编程 --句柄类与继承[续] 三.句柄的使用 使用Sales_item对象能够更easy地编写书店应用程序.代码将不必管理Item_base对象的指针,但仍然能够获得通过Sales_item对象进行的调用的虚行为. 1.比較两个Sales_item对象 在编写函数计算销售总数之前,须要定义比較Sales_item对象的方法.要用Sales_item作为关联容器的keyword,必须能够比較它们.关联容器默认使用keyword类型的小于操作符,可是假设给Sales_item定义小于操作符,

C++ Primer 学习笔记_83_模板与泛型编程 --一个泛型句柄类

模板与泛型编程 --一个泛型句柄类 引言: [小心地雷] 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板.在熟悉了这些特性之后再研究这个例子也许会帮助.另一方面,这个例子还能很好地测试你对这些特性的理解程度. 前面示例的Sales_item和Query两个类的使用计数的实现是相同的.这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数.原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化.至于是公开还是隐藏下

智能指针与句柄类(四)

当我们希望使用容器来保存继承体系中的对象时,容器用于继承体系中的类型会有影响:派生类对象复制到基类对象时,派生类对象将被切掉.那么解决这一问题的方法通常是使用容器保存基类对象的指针,这些指针实际指向的是程序运行时动态分配的派生类对象,用户必须保证在容器析构前调用delete来释放动态分配的对象,如下例: 1 class Base 2 { 3 public: 4 virtual void action() = 0; 5 }; 6 class Derived1 : public Base 7 {..

【ThinkingInC++】32、一个句柄类

头文件 /** * 功能:句柄类,头文件只包含公共的接口和单一的指针,该指针指向一个没有 完全定义的类 * 时间:2014年8月25日07:52:42 * 作者:cutter_point */ #ifndef HANDLE_H_INCLUDED #define HANDLE_H_INCLUDED class Handle { struct Cheshire; //这个只是声明,如果要改变Cheshire的组成,唯一要重新编译的是 //Handle因为头文件并没有动 Cheshire* smile

0722-----C++Primer听课笔记----------句柄类和智能指针

1.再说智能指针 1.1  为什么要用智能指针?对于一个指针,它指向一个动态分配内存的对象,若同时有多个指针指向该对象,那么当我们delete的时候,就会出现delete 一个无效内存的错误,因为该对象已经被delete过了,所以这就造成了错误.针对这一情况,我们想到,new 和 delete 必须是成对出现的,那么联想到类里面,很容易想到这个构造函数和析构函数也是成对出现的,对于每一个对象,初始化的时候会调用构造函数,而销毁的时候必然要调用析构函数.因此我们就可以对 指针 进行封装,将该指针的

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

0  C++中多态的概念 多态是指通过基类的指针或者引用,利用虚函数机制,在运行时确定对象的类型,并且确定程序的编程策略,这是OOP思想的核心之一.多态使得一个对象具有多个对象的属性.class Core作为就算成绩的基类.class Grad为Core的子类,添加了论文(thesis)成绩,Grad最终成绩为论文成绩和基类计算方法得到的成绩中的较小值.这是一个知识点:继承的适用场合就是,子类和基类的功能是相同或者相似,但是子类多了一些扩展. //filename:Core.h #ifndef

智能指针与句柄类(二)

之前文章提到写时复制(copy-on-write)技术,要实现这种功能,针对上文中Handle代码,需要将size_t * use这个抽象出来,封装成一个引用计数类,提供写时复制功能.CUseCount类实现如下: 1 class CUseCount 2 { 3 public: 4 CUseCount(); 5 CUseCount(const CUseCount&); 6 ~CUseCount(); 7 8 bool only()const; //判断引用计数是否为0, 句柄类无法访问priva

智能指针与句柄类(三)

之前文章中实现的写时复制,句柄类中引用计数和T类型指针是分开的,这里换一种方式来处理,将引用计数和T类型指针视为一个整体,当做句柄类模板参数.先对上节中的引用计数进行改造: 1 class CUseCount 2 { 3 public: 4 CUseCount(); 5 CUseCount(const CUseCount&); 6 CUseCount& operator=(const CUseCount&); 7 ~CUseCount(); 8 9 void markUnshare