C++ 句柄类

时间:2022-09-07 11:48:33

一、容器与继承

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

    唯一的可行的选择是容器中保存对象的指针。但是需要用户管理对象和指针。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&sol;C&plus;&plus;&lpar;3&rpar; - 从 《Accelerated C&plus;&plus;》源码学习句柄类

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

  2. C&plus;&plus;中的句柄类

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

  3. c&plus;&plus; 容器、继承层次、句柄类

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

  4. C&plus;&plus; Primer 学习笔记&lowbar;72&lowbar;面向对象编程 --句柄类与继承&lbrack;续&rsqb;

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

  5. C&plus;&plus;的句柄类

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

  6. C&plus;&plus; 句柄类的原理以及设计

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

  7. C&plus;&plus;中代理类和句柄类

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

  8. c&plus;&plus;句柄设计

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

  9. sp&lt&semi;&gt&semi; 强指针类的用法

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

随机推荐

  1. iOS支付宝支付总结

    1.按照http://doc.open.alipay.com/doc2/detail?spm=0.0.0.0.SWdJgo&treeId=59&articleId=103676&amp ...

  2. event事件对象

    事件对象event: 在触发DOM事件的时候都会产生一个对象 1.type:获取事件类型 2.target:获取事件目标 3.stopPropagation():组织事件冒泡 4.preventDef ...

  3. MFC----任务管理器的制作

    首先建立一个MFC项目,因为进程有多个并且是动态的,所以可以看做链表,获得头结点&&依次向下遍历: 首先 我们使用CreateToolhelp32Snapshot提取出进程表,之后

  4. jquery 打印宽高

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. LeetCode Shortest Palindrome

    原题链接在这里:https://leetcode.com/problems/shortest-palindrome/ 题目: Given a string S, you are allowed to ...

  6. python中函数总结之装饰器闭包

    1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器 ...

  7. mysql查询差集

    select A.* from A left join B using(name,addr,age) where B.name is NULL; select A.* from A left join ...

  8. hanlp 加载远程词库示例

    说明 ·目前的实现方式是以远程词库的内容重新构建CustomDictionary.trie,demo主要是为了实现同步远程词库,对性能暂不作考虑,对性能要求要以CustomDictionary.dat ...

  9. Spring&plus;Struts2&plus;Hibernate框架整合流程

    一:基本步骤 新建Maven项目,导入相关依赖(推荐) 在WEB-INF的web.xml中进行配置 ————–Hibernate配置 —————- 创建entity包,创建数据库相关实体类 根据实体类 ...

  10. 13-01 java StringBuffer类,StringBuilder类

    StringBuffer类的构造方法 package cn.itcast_01; /* * 线程安全(多线程讲解) * 安全 -- 同步 -- 数据是安全的 * 不安全 -- 不同步 -- 效率高一些 ...