一、容器与继承

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

    唯一的可行的选择是容器中保存对象的指针。但是需要用户管理对象和指针。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. 关于MySQL中时间格式和取零点的问题

    select * from order where create_time>'2016-05-21 00:00:00'; 不包含2016-05-21 00:00:00时的订单 select * ...

  2. pm

    如何不被程序员(RD)们嫌弃--写给那些血气方刚的产品经理(PM)http://www.36kr.com/p/212020.html 最近有位刚做 PM(产品经理)的小伙跑来跟我控诉,说公司技术部的 ...

  3. iOS之tabbar图片去除渲染以及字体颜色统一配置

    转发:http://www.cnblogs.com/qianLL/p/5521228.html   方式一  代码实现 这种要写很多代码 ,每个控制器都要写   UIImage *image=[UII ...

  4. Delphi MaskEdit用法(转)

    源:http://www.cnblogs.com/zhangzhifeng/archive/2011/10/12/2208640.html MaskEdit是用来建立编辑框的,但它与Edit编辑框可以 ...

  5. 转: window中使用PSFTP/WinSCP实现SFTP上传下载

    sftp 服务器:  dbmonitor 1.sftp属于交互式的,所以你得缓存下命令#!/bin/shsftp -o Port=3322 root@172.16.1.21:/opt << ...

  6. OleDB连接字符串很基础却很重要

    --Oracle<add name="HISConnectionString" connectionString=" Server=127.0.0.1;Provid ...

  7. 使用bootstrap建立响应式网页——头部导航栏

    1.要建立响应式网站的布局,这个时候bootstrap的高级就能体现出来了. 2.先来了解一下bootstrap提供了哪些响应式工具供我们使用: (1)屏幕宽度尺寸的概念: <768px     ...

  8. LintCode 11 二叉查找树的搜索区间

    题目链接:http://www.lintcode.com/zh-cn/problem/search-range-in-binary-search-tree/ 1.描述 给定两个值 k1 和 k2(k1 ...

  9. 完美解决ie浏览器location.href不刷新页面的问题,进入页面只刷新一次

    /* ie不刷新列表bug */try{ var agent = navigator.userAgent.toLowerCase(); var ieflag = /(msie\s|trident.*r ...

  10. Ubuntu iptables 设置

    在ubuntu中由于不存在 /etc/init.d/iptales文件,所以无法使用service等命令来启动iptables,需要用modprobe命令. 启动iptables modprobe i ...