一、容器与继承

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

    唯一的可行的选择是容器中保存对象的指针。但是需要用户管理对象和指针。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. 【转】使IFRAME在iOS设备上支持滚动

    原文链接: Scroll IFRAMEs on iOS原文日期: 2014年07月02日 翻译日期: 2014年07月10日翻译人员: 铁锚 很长时间以来, iOS设备上Safari中超出边界的元素将 ...

  2. NDK常用命令

    NDK Build 用法(NDK Build)   1.ndk-build的用法 Android NDKr4引入了一个新的.小巧的shell脚本ndk-build,来简化源码编译. 该文件位于NDK根 ...

  3. Sping中的IOC四种注解的简单记录

    @Component 通用注解,不推荐使用,要用的话,任何类的头上都能使用,但是这个类到底是干嘛用得就不知道了. @Controller 控制层使用,也就是MVC中的C,主要的时候使用的时候注意配合@ ...

  4. Total Highway Distance

    Total Highway Distance 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Little Hi and Little Ho are playing a ...

  5. Trim(',')的作用去除最有一个','

    public bool XMLDataImport()        { List<string> sqllist = new List<string>();          ...

  6. iframe载入等待

    <style> #pageloading{position:absolute; left:0px; top:0px;background:white url('../images/load ...

  7. Unity3d 开发之 ulua 坑的总结

    相同的 lua 代码在安卓上能正常运行,但在 IOS 上可能不会正常运行而导致报红,崩溃等,我在使用 lua 编程时遇到的一些坑总结如下: 1. File.ReadAllText, 诸如以下代码在 i ...

  8. codis 新版本 CodisLabs 编译安装

    codis 3.0 版本编译安装 # 首先安装 go 语言 wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz ...

  9. iOS开发——实时监控网速(仅作参考,发现一点问题)

    开发中用到获取网速的地方,应该就两种: 1.下载速度,这种可以直接在接受数据的地方统计计算.这个就不讲了. 2.获取手机网卡的数据,可以监控网卡的进出流量,下面就是. #import "Vi ...

  10. CodeForces 652A Gabriel and Caterpillar

    简单模拟. #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> ...