也谈“避免使用虚函数作为库的接口”
近日拜读了陈硕大牛的文章C++ 工程实践(5):避免使用虚函数作为库的接口,文章的观点认为应该避免使用C++的class纯虚函数来定义API接口,并以COM作为反向教材进行批判,对此本人有些不同意见,记录在此与各位一同探讨。
陈硕大牛认为C++的虚函数是以虚函数在class中定义的位置来确定其虚表的绑定位置,在class扩充的过程中,原有的虚函数位置不可以变动,因此带来了接口扩展的脆弱与僵硬。虚函数位置不可以随意变动是事实,但是给接口扩展造成问题不敢苟同。陈硕举出的linux接口的例子,个人认为完全不妥,完全没有必要使用百层的继承,有很多种技巧可以解决这个问题,比方可以写成这样:
{
//v0.1
int restart_syscall() = 0; //0
int exit() = 0; //1
int fork() = 0; //2
.....
#if LINUX_VER>0.1
int sigaction() = 0;
#endif
#if LINUX_VER>0.11
int sgetmask() = 0; //68
int ssetmask() = 0; //69
int setreuid() = 0; //70
int setregid() = 0; //71
#endif
#if LINUX_VER>0.12
int sigsuspend() = 0; //72
int sigpending() = 0; //73
int sethostname() = 0; //74
int setrlimit() = 0; //75
#endif
//.....
};
一般来说kernel级的API会有点特殊,Kernel是系统最底层的基础,在构建kernel时往往C++编译器都还没有,所以在实践中通常我们不会用C++去定义kernel API,但是这不代表从理论上行不通。搞过系统底层的同学,应该对中断入口表这类的数据结构不会陌生,所谓中断入口表其实就是一组函数指针的数组,跟C++的虚表在物理结构上是很类似的,要把中断入口表映射成虚表,从理论上说是完全可以做到的。
以上这种接口扩展方式是以保留全部遗留函数为前提的。但是在实践中,库的升级往往伴随架构的进化,很多老的API函数需要被废弃,或者函数的定义需要扩充和修改。此时,C-style的API定义风格就会遭遇一些困难,要把老的函数废弃不是那么轻松的。而用COM接口的话就很简单了,只要简单的定义一套新的接口,让新的库带上版本号保存在新的DLL里就可以了。老的客户代码依然可以使用老的COM接口和组件,不需要修改任何代码。新的客户代码直接使用新版本的COM接口,新的接口里也不再需要保留那些已经废弃的老版本的API函数。
这个例子我们可以看OpenGL vs D3D
早期的OpenGL 1.0的接口定义是C-Style的,这种API定义风格比较僵化,在扩展时只能添加函数而不能轻易的删除或修改原有的函数,而图形接口的架构变化是非常快的,2~3年就会伴随一次大的硬件架构的升级。所以到了1.1之后引入了GL Extension机制,不再增加新的API函数接口,而是让客户代码自己去通过GL扩展机制去查询当前硬件支持的特性,获取新特性的函数指针后再进行调用,同时因为使用扩展机制需要程序手工查询再取得函数指针,使用非常麻烦,所以还是增加了一些C-style的API来进行包装和简化。这样虽然使得OpenGL可以快速的跟进硬件系统的发展,但是也导致各个硬件厂商在OpenGL里随意的添加自己的私有扩展,导致平台的混乱和分裂。到了OpenGL2.0时代,大家终于坐下来着手解决这个问题,利用委员会制定的ARB扩展来统一各个厂商之间的私有扩展。但此时OpenGL的扩展已经比较发展的混乱和缓慢了,各种老的API及扩展特性原本应该被完全废弃的也因为需要考虑兼容性的缘故而不得不保留,OpenGL变得越来越庞大和难用。OpenGL这些年发展的失误,跟使用僵化的C-Style API以及采用不成熟的扩展机制是很有关系的。
早期的D3D只是OpenGL后面的一个小跟班,其功能和影响力都远不及OpenGL。但是D3D的进化速度非常的快,从95年到现在大约发布了10来个大的D3D版本。由于D3D采用了COM作为其API接口,在每次架构升级时微软都采取了大刀阔斧式的改进,所有在硬件架构升级中被淘汰的函数都不需要保留到下一代的D3D接口中,同时很多新的功能和接口都可以自由的添加到新的D3D版本中,因此新的D3D组件库不需要考虑为老版本的兼容性买单,老的客户代码依然可以用老的D3D组件库运行,新的客户代码使用新的D3D组件库来支持最新的硬件。
写成伪代码的话大致是这样:
{
DrawTriangle();
};
interface D3D2
{
SetMatrix();
DrawPrimitive();
};
//.....
interface D3D9
{
SetShader();
BindShader();
DrawPrimitive();
};
可以看到,每个新的D3D版本完全不需要去考虑跟老版本的兼容问题,每次发布新版本的时候重新定义接口就是了。客户代码在使用是创建自己需要的D3D版本的对象就可以了。windows平台本身所存在的混乱和兼容性问题,大部分是因为其闭源特性造成的,COM的二进制兼容性从实践证明来看是没有问题的。从WIN95一直到现在的Windows7,新平台保持了对大部分旧软件的向下兼容性,COM组件不兼容的现象很少见,而不采用COM的普通DLL倒是经常出现版本冲突问题。
暂时先写这么多,欢迎更多讨论。
转载于:https://www.cnblogs.com/Hybird3D/archive/2011/03/16/1986256.html
也谈“避免使用虚函数作为库的接口”相关推荐
- 不要使用虚函数作为库的接口
[转] C++ 工程实践:避免使用虚函数作为库的接口 原文: http://blog.csdn.net/Solstice/archive/2011/03/12/6244905.aspx 陈硕 (gia ...
- 避免使用虚函数作为库的接口
转自:陈硕 (giantchen_AT_gmail) Blog.csdn.net/Solstice 摘要:作为 C++ 动态库的作者,应当避免使用虚函数作为库的接口.这么做会给保持二进制兼容性带 ...
- C++ 工程实践:避免使用虚函数作为库的接口
原文: http://blog.csdn.net/Solstice/archive/2011/03/12/6244905.aspx 陈硕 (giantchen_AT_gmail) Blog.csdn. ...
- C++ 工程实践(5):避免使用虚函数作为库的接口
https://blog.csdn.net/solstice/article/details/6244905 摘要:作为 C++ 动态库的作者,应当避免使用虚函数作为库的接口.这么做会给保持二进制兼容 ...
- c++远征之多态篇——纯虚函数和抽象类、接口类
以下内容源于慕课网的学习整理,如有侵权,请告知删除. 1.纯虚函数 没有函数体: =0: 即只有函数声明,而没有函数定义的虚函数,是纯虚函数. 2.抽象类 概念:含有纯虚函数的类,叫抽象类. 抽象类无 ...
- C#中虚函数,抽象,接口的简单说明
虚函数:由virtual声明,它允许在派生类中被重写,要重写方法,必须先声名为virtual public class myclass { public virtual int myint() { 函 ...
- c++虚继承和虚函数和抽象类、接口类,聚合类
虚继承 虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间.存在二义性). 底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间, ...
- 9-2:C++多态之纯虚函数和抽象类以及接口继承和实现继承
文章目录 (1)纯虚函数和抽象类的概念 (2)抽象类的意义 (3)接口继承与实现继承 (1)纯虚函数和抽象类的概念 如果一个类的虚函数后面写上=0,同时不写它的实现,那么这样的虚函数称之为纯虚函数,包 ...
- [C++] - 纯虚函数 抽象基类 接口类
翻译自:https://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-inter ...
- C++ override及虚函数的讲解
胡马依北风 随笔 - 46 文章 - 0 评论 - 11 博客园 首页 新随笔 联系 管理 订阅 C++11 之 override 1 公有继承 公有继承包含两部分:一是 &quo ...
最新文章
- 一些小团队的自动化运维实践经验
- 动态规划--背包问题
- JS 获取浏览器、显示器 窗体等宽度和高度
- C# Azure 存储-Blob
- 升级nodejs至最新
- 2020年特斯拉是在中国获得补贴最多的电动汽车制造商
- formdata 嵌套_解决form嵌套
- SLAM_SLAM面试专题
- BIOS升级之:P5QPL-AM
- html代码中font是什么意思,HTML元素font标签的使用方法及作用
- 基础篇:6.9)形位公差-检测方法Measurement
- 【转】敏捷中国十八年目睹之怪现状
- 诚之和:太平鸟难渡“抄袭劫”?
- python学习基础知识——1
- 基于C++和QT实现的简单数独游戏软件
- linux centos无法进入系统,centos无法进入桌面系统
- android屏幕 录制检测,Android 录制屏幕的实现方法
- mysqlbinlog恢复mysql表数据
- CSS 样式的 initial(默认)和 inherit(继承)以及 unset
- 深信服 2021 面试总结