一、容器与继承

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

    唯一的可行的选择是容器中保存对象的指针。但是需要用户管理对象和指针。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 = , 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()) { }
//一般构造函数
Handler(const Base &item) : pBase(item.clone()), use(new int()) { }
//复制构造函数
//每复制一次,引用计数就加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 == ) {
cout << pBase->getName() << " is going to be deleted!" << endl;
delete pBase;
}
}
};
int main() {
Handler h1(Base(,"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(,"derived",));
h1=h3;
h1.print_use();
cout<<"Derived compute:"<<(*h1).compute()<<endl;
cout<<"system automatic delete begin"<<endl;
return ;
}

二、句柄类

句柄类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(,"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(,"derived",));
h1=h3;
h1.print_use();
cout<<"Derived compute:"<<(*h1).compute()<<endl;
cout<<"system automatic delete begin"<<endl;
return ;
}

输出:

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++ 句柄类的更多相关文章

  1. code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类

    0  C++中多态的概念 多态是指通过基类的指针或者引用,利用虚函数机制,在运行时确定对象的类型,并且确定程序的编程策略,这是OOP思想的核心之一.多态使得一个对象具有多个对象的属性.class Co ...

  2. C++中的句柄类

    初次在<C++ Primer>看到句柄,不是特别理解.在搜索相关资料后,终于有了点头绪. 首先明白句柄要解决什么问题.参考文章<C++ 沉思录>阅读笔记——代理类 场景: 我们 ...

  3. c++ 容器、继承层次、句柄类

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

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

    面向对象编程 --句柄类与继承[续] 三.句柄的使用 使用Sales_item对象能够更easy地编写书店应用程序.代码将不必管理Item_base对象的指针,但仍然能够获得通过Sales_item对 ...

  5. C++的句柄类

    上一篇文件介绍了关于C++代理类的使用场景和实现方法,但是代理类存在一定的缺陷,就是每个代理类会创建一个新的对象,无法避免一些不必要的内存拷贝,本篇文章引入句柄类,在保持代理类多态性的同时,还可以避免 ...

  6. C++ 句柄类的原理以及设计

    句柄类存在的意义是为了弥补将派生类对象赋给基类对象时发生的切片效应.比如以下的程序: multimap<Base> basket; Base base; Derived derive; b ...

  7. C++中代理类和句柄类

    指针是 C 与其他语言区别的重要特征之一,在 C++ 中,指针也被广泛运用,我们通过指针实现多态.然而,众所周知,指针的使用必须小心,否则很容易造成内存泄漏 Memory Leak.当我们有几个指针指 ...

  8. c++句柄设计

    句柄,也称为智能指针. 我计算了一下我的时间,以后每14天得读完一本书,才不愧对我买的这么多书.然而我还要抽出时间来谢谢博文.最近读的是c++沉思录,开篇就用了3章来讲述句柄.好了,废话少说,接下来谈 ...

  9. sp<> 强指针类的用法

    在android 中可以广泛看到的template<typename T>,  class Sp 句柄类实际上是android 为实现垃圾回收机制的智能指针.智能指针是c++ 中的一个概念 ...

随机推荐

  1. Django: 配置和静态文件

    运行django-admin.py startproject [project-name] 命令会生成一系列文件,在django 1.6版本以后的settings.py文件中有以下语句: # Buil ...

  2. jq中的ajax

    jq对ajax进行了封装,在jq中$.ajax()方法是最底层的方法,第二层是load() , get() , post()方法,第三层是$.getScript()和$.getJSON().基本第二种 ...

  3. Entity Framework 学习初级篇6--EntityClient

    System.Data.EntityClient 命名空间是 实体框架的 .NET Framework 数据提供程序.EntityClient 提供程序使用存储特定的 ADO.NET 数据提供程序类和 ...

  4. [转] Linux下移动virtualbox虚拟硬盘丢失eth0

    1.遇到什么的问题(What)      在新的virtualbox虚拟机上挂上曾使用过的虚拟硬盘,在启动的时候,发现找不到网卡eth0, 在输入ifconfig –a的时候,也没有任何Ethnet的 ...

  5. ms10_046_shortcut_icon_dllloader漏洞利用和ettercap dns欺骗

    ms10_046_shortcut_icon_dllloader漏洞利用过程 msf > use exploit/windows/browser/ms10_046_shortcut_icon_d ...

  6. ajax编程**

    ajax 编程 *step1获得 XmlHttpRequest 对象.该对象由浏览器提供,但是该类型并没有标准化.ie 和其它浏览器不同,其它浏览器都支持该类型,而 ie 不支持. function ...

  7. Qt5:Qt中图片的翻转,旋转,缩放,扭曲操作

    具体用到了  mirror()  shear()  scaled() translate() rotate() 等函数函数等会儿再写 (其中  translate() 和 rotate() 俩函数组合 ...

  8. IWorkSpace接口介绍 1.打开各种数据库

    IWorkspace接口提供访问工作空间的通用属性和方法,如它的连接属性,以及包含的数据集的方法. IWorkspace的成员字段: Members   Description ConnectionP ...

  9. ural1439 Battle with You-Know-Who

    Battle with You-Know-Who Time limit: 2.0 secondMemory limit: 64 MB Rooms of the Ministry of Magic ar ...

  10. POJ3352 Road Construction 双连通分量+缩点

    Road Construction Description It's almost summer time, and that means that it's almost summer constr ...