C++ 虚拟函数vs 回调函数 像有虚拟方法表一样有一个虚拟变量表就可以实现类级回调函数了
2012-11-25 04:26
816 查看
虚拟函数使得对象用同一个名字,调用不同函数.
这种多态实现了某种概念的抽象.
用回调也可以达到类似的效果,某种意义上,用回调更灵活.
由于C++的类回调函数的强类型导致用起来不够灵活方便,
这里的回调函数可以用Delegate代替.
其实用回调是典型的使用聚合而不使用继承的思路.
最典型的例子就是Thread类,
通常的做法是写一个非虚拟的Run,调用虚拟的Excute.
用到Thread的地方,继承Thread实现Excute即可.
而用回调的方式,只需设置回调即可,不用新建一个类来继承.
尤其当用到的Thread较多时,避免了类的数目的膨胀.
针对接口编程,经常声明一个接口类,
一方拥有这个接口,使用这个接口,另一方从这个接口继承,实现接口.
如果采用Delegate的方法,有时可以省去单独的接口类.
虚拟函数的问题在于其同时实现了接口继承和实现继承.
而回调函数可以看作接口继承,因此更灵活.
虚拟方法表由编译器自动处理,回调函数由人工管理.
但是,虚拟函数基于类,同一个类的对象共享一个虚拟方法表,
而回调函数却是基于对象的,每个对象都有一个回调函数指针.
能否让对象共享同一个回调函数指针?
类可以共享静态变量,但类和子类也共享该静态变量.
(单例模式不能继承就面临的这个问题,用模板实现编译时多态).
那么除非根据函数名结合类名查表,调用不同函数.
如果语言支持与子类独立的类级共享变量,
也就是说在父类声明一个类级变量,子类中也有一个同名的类级变量.这两个变量同名但独立.
就像虚拟方法表一样有一个虚拟变量表就好了.
这样就可以实现前面的基于类的回调函数.
语言不直接支持,我们只有diy了
//ClassVariable代表类共享变量.
//ClassVariable中主要存放的是类级回调函数,或者说Deletegate.
struct ClassVariable
{
};
//每个类都声明一个同名的静态成员变量ClassVar;
class TObject
{
static ClassVariable* ClassVar;
virtual ClassVariable* GetClassVariable(){return ClassVar};
//或者通过一个全局的hash表,由类名得到相应的ClassVar
};
每个类从父类继承并扩展相应的ClassVariable结构.
那么如何实现多态呢?
在一个非虚拟函数里,调用ClassVar所指向的一个运行时设定的Deletegate.
使用ClassVar前
借鉴QT中的RTTI的相关思路.
提供一个函数,进行类型转换,将通用的ClassVariable,
转换为与类相关的特定类型的指针.
某种意义上RTTI信息就是类的共享的数据.
可以研究下C++的Rtti,以及类库的Rtti的实现原理.
如果C++直接支持delegate,虚拟变量表,消息机制就好了
如果每一个类对应的rtti构成一个包含vmt成员的MetaClass,并且用户可以自定义继承,扩充之就好了.
这种多态实现了某种概念的抽象.
用回调也可以达到类似的效果,某种意义上,用回调更灵活.
由于C++的类回调函数的强类型导致用起来不够灵活方便,
这里的回调函数可以用Delegate代替.
其实用回调是典型的使用聚合而不使用继承的思路.
最典型的例子就是Thread类,
通常的做法是写一个非虚拟的Run,调用虚拟的Excute.
用到Thread的地方,继承Thread实现Excute即可.
而用回调的方式,只需设置回调即可,不用新建一个类来继承.
尤其当用到的Thread较多时,避免了类的数目的膨胀.
针对接口编程,经常声明一个接口类,
一方拥有这个接口,使用这个接口,另一方从这个接口继承,实现接口.
如果采用Delegate的方法,有时可以省去单独的接口类.
虚拟函数的问题在于其同时实现了接口继承和实现继承.
而回调函数可以看作接口继承,因此更灵活.
虚拟方法表由编译器自动处理,回调函数由人工管理.
但是,虚拟函数基于类,同一个类的对象共享一个虚拟方法表,
而回调函数却是基于对象的,每个对象都有一个回调函数指针.
能否让对象共享同一个回调函数指针?
类可以共享静态变量,但类和子类也共享该静态变量.
(单例模式不能继承就面临的这个问题,用模板实现编译时多态).
那么除非根据函数名结合类名查表,调用不同函数.
如果语言支持与子类独立的类级共享变量,
也就是说在父类声明一个类级变量,子类中也有一个同名的类级变量.这两个变量同名但独立.
就像虚拟方法表一样有一个虚拟变量表就好了.
这样就可以实现前面的基于类的回调函数.
语言不直接支持,我们只有diy了
//ClassVariable代表类共享变量.
//ClassVariable中主要存放的是类级回调函数,或者说Deletegate.
struct ClassVariable
{
};
//每个类都声明一个同名的静态成员变量ClassVar;
class TObject
{
static ClassVariable* ClassVar;
virtual ClassVariable* GetClassVariable(){return ClassVar};
//或者通过一个全局的hash表,由类名得到相应的ClassVar
};
每个类从父类继承并扩展相应的ClassVariable结构.
那么如何实现多态呢?
在一个非虚拟函数里,调用ClassVar所指向的一个运行时设定的Deletegate.
使用ClassVar前
借鉴QT中的RTTI的相关思路.
提供一个函数,进行类型转换,将通用的ClassVariable,
转换为与类相关的特定类型的指针.
某种意义上RTTI信息就是类的共享的数据.
可以研究下C++的Rtti,以及类库的Rtti的实现原理.
如果C++直接支持delegate,虚拟变量表,消息机制就好了
如果每一个类对应的rtti构成一个包含vmt成员的MetaClass,并且用户可以自定义继承,扩充之就好了.
| |||||||||||||||||
C++ FAQ / Section 31 / FAQ 31.2 Section 31:
E-mail the author [ Subject index | About the author | © ] Revised Jul 4, 2012 | [31.2] What is "virtual data," and how-can / why-would I use it in C++? virtual data allows a derived class to change the exact class of a baseclass's member object. virtual data isn't strictly "supported" by C++,however it can be simulated in C++. It ain't pretty, but it works. To simulate virtual data in C++, the base class must have a pointer to themember object, and the derived class must provide a new object to bepointed to by the base class's pointer. The base class would also have oneor more normal constructors that provide their own referent (again via new),and the base class's destructor would delete the referent. For example, class Stack might have an Array member object (using apointer), and derived class StretchableStack might override the baseclass member data from Array to StretchableArray. For this to work,StretchableArray would have to inherit from Array, so Stack would have anArray*. Stack's normal constructors would initialize this Array* with anew Array, but Stack would also have a (possibly protected)constructor that would accept an Array* from a derived class.StretchableStack's constructor would provide a new StretchableArrayto this special constructor. Pros: Easier implementation of StretchableStack(most of the code is inherited) Users can pass a StretchableStack as a kind-of Stack Cons: Adds an extra layer of indirection to access the Array Adds some extra freestore allocation overhead(both new and delete) Adds some extra dynamic binding overhead(reason given in next FAQ) In other words, we succeeded at making our job easier as theimplementer of StretchableStack, but all our users payfor it. Unfortunately the extra overhead was imposed on both users ofStretchableStack and on users of Stack. Please read the rest of this section. (You will not get a balancedperspective without the others.) |
相关文章推荐
- nginx 配置虚拟主机,实现在一个服务器可以访问多个网站的方法
- java有几种方法可以实现一个线程?用什么关键字修饰同步方法?
- [ecshop 资料]ecshop商品自定义销量(虚拟销量)实现方法 后台可以自由设置
- C++数据结构用一个变量或一个临时栈实现栈的复制
- 在c++中可以调用java中的方法,从而实现java与c++的的交互。
- 在一个程序中需要用到全局变量(在多个class之间共享数据),请问如何定义具有这种功能的变量?或者是否有其他的方法解决多个class之间的数据共享(尽量简单实现)。 首先应该明确 Java中没有全局变
- ORACEL存储过程中声明一个可存储记录的像数据表一样的变量的简便方法。
- 在进行C#编程时候,有的时候我们需要判断一个字符串是否是数字字符串,我们可以通过以下两种方法来实现。 【方法一】:使用 try{} catch{} 语句。 我们可以在try语句块中试图
- 【C++】用栈实现倒序输出一个字符串(可以带空格)
- 回调函数的C++ 封装(非静态成员函数的回调函数实现方法)
- c#调用c++带有回调函数方法的实现
- 一个实现ajax之后既可以刷新又可以让提示信息延长的js实现方法
- java中有几种方法可以实现一个线程?
- C++中的Thunk技术 / 非静态类成员函数作为回调函数 的实现方法
- 实现一个通讯录; 通讯录可以用来存储1000个人的信息,每个人的信息包括: 姓名、性别、年龄、电话、住址 提供方法: 1. 添加联系人信息 2. 删除指定联系人信息 3. 查找指定
- 一个接口Test可以被new吗?new完就可以直接使用Test中的方法了吗(其实Test中的方法在TestIMP中实现)?
- ROS回调函数的类方法实现,both in Python 和 C++
- XHTML 的一个实例, 其实这个实例的方法已经可以实现所有要实现的布局. by Emerald 绿色学院 - Green Institute
- 一个Windows C++的线程类实现(封装API,形成一个类,但不完善。其实可以学习一下Delphi的TThread的写法)
- # include <errno.h >查看错误代码errno是调试程序的一个重要方法。当Linux C API函数发生异常时,一般会将errno变量赋值一个整数,不同的值表示不同的含义,可以通过查看