C++ IMPL 模式解析(下)
文章目录
- 二进制兼容
- 什么是二进制兼容
- 为什么会二进制不兼容
- 什么情况会导致二进制不兼容
- 隐藏子类——解决二进制兼容
- 参考资料
二进制兼容
在 上一章 的结尾处提到了二进制兼容的概念,这里先说说二进制兼容的问题。
什么是二进制兼容
简单说,就是我的可执行程序调用你的动态库(so/dll),若动态库发生改动,我替换库文件后仍可以直接运行,这就是二进制兼容。若需要重新编译才能运行,就是二进制不兼容。
为什么会二进制不兼容
二进制不兼容的根本原因是 升级动态库导致内存模型发生了改变 。如原本类中有一个 int 属性 a,升级之后在 a 前面添加了一个 int 属性 b。升级后,类多占用了 4 个字节。如果调用着不重新编译,原来调用 a 的地方已经变成 b 了,自然就会出问题。增加接口也会造成这样的问题,此时必须重新编译链接可执行程序,以更新动态库在程序中的对象模型。
什么情况会导致二进制不兼容
简单来说,只要改变了对象的内存模型,就会导致二进制不兼容 。例如:
- 增加虚函数或改变虚函数的声明顺序,这会导致虚函数表中的函数位置发生变化(在末尾增加是一种取巧行为,如果类被继承,也还是会出问题);
- 增加非静态属性;
- 调整非静态属性的声明顺序。
例如,对于第三个版本,略做一点修改:
class NetworkV3 {public:virtual int Send(const std::string str) = 0;virtual int Recv(std::string &str) = 0;// 创建和销毁函数static NetworkV3* New();static void Delete(NetworkV3 *net);
};
这里,我将 Send 和 Recv 的声明顺序颠倒一下,若不重新编译,调用时就会出问题。我是在 CentOS 上使用 g++ 9.3 编译了,此时虽然没有发生崩溃,但明显有调用错误,即原来调用 Send 的地方改为调用 Recv 了,若参数不一致,则直接没有调用。只有重新编译后,程序才能正常运行。
隐藏子类——解决二进制兼容
可以通过一个隐藏的子类和友元的方式来解决二进制兼容的问题,其实就是 上一章 中两个版本(版本二和版本三)的结合。具体代码如下:
// network.h
// 版本4
class NetworkV4 {public:int Send(const std::string str);int Recv(std::string &str);// 创建和销毁函数static NetworkV4* New();static void Delete(NetworkV4 *net);protected:NetworkV4();~NetworkV4();
};// network.cpp
// 版本4
class NetworkV4Impl : public NetworkV4
{// 友元,NetworkV4 中可以访问 NetworkV4Impl 的私有成员friend class NetworkV4;private:// NetworkV4 的成员变量std::string str_;
};NetworkV4::NetworkV4() {}
NetworkV4::~NetworkV4() {}int NetworkV4::Send(const std::string str) {NetworkV4Impl *impl = (NetworkV4Impl*)this;// TODO: 通过访问 impl 的成员变量,实现 NetworkV4::Sendimpl->str_ = str;std::cout << "NetworkV4::Send: " << impl->str_ << std::endl;return impl->str_.size();
}int NetworkV4::Recv(std::string &str) {NetworkV4Impl *impl = (NetworkV4Impl*)this;// TODO: 通过访问 impl 的成员变量,实现 NetworkV4::Recvimpl->str_ = "ok";str = impl->str_;std::cout << "NetworkV4::Recv: " << str << std::endl;return str.size();
}// 创建和销毁函数
NetworkV4* NetworkV4::New() {return (new NetworkV4Impl());
}
void NetworkV4::Delete(NetworkV4 *net) {delete (NetworkV4Impl*)net;
}
NetworkV4 的接口只有成员函数(非虚函数),无成员变量,但构造函数和析构函数必须定义为 protected,防止外部创建对象,要使用静态方法创建对象。
只是有一点看着比较奇怪,为啥 NetworkV4Impl 一定要继承 NetworkV4 ,不继承是否可以。很显然的可以的,但如果不继承,就需要定义一个 NetworkV4Impl 全局变量,用于在 NetworkV4 中调用,而这与 NetworkV2 的方法几乎是一致的(NetworkV2 中使用的是成员变量)。但使用全局变量就有悖于 C++ 封装的特性,之所以搞这么复杂,还是为了封装得好用一些。
个人觉得这一种方法过于复杂了一点,没有上一章中的两种方法直观,这里就权当做个参考吧。
参考资料
- C++接口工程实践:有哪些实现方法?
- C++二进制兼容
C++ IMPL 模式解析(下)相关推荐
- C++ IMPL 模式解析(上)
文章目录 抛砖引玉 C++ IMPL 模式 完全隐藏成员变量 抛砖引玉 试想一个问题,如果有一套收发数据的网络接口,需要提供给其他同事或厂家使用,包含头文件和动态库,假设头文件如下: // 版本1 c ...
- [设计模式] IMPL 模式
说明: 模块型代码中经常可以看到有一些类叫做 class Aimpl , 同时还会存在一个叫 class A的类,从名称上看 Aimpl 是 A 的 implement . 实际上,A 往往是当前模 ...
- 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 从脚本到主类
1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [一] 可以去看原文.这里是补充专栏.请看原文 2. 前言 主要针对yarn-per-job模式进行代码分析. ...
- android色彩模式,Android Q 色彩(颜色)模式解析(一)
Android 色彩(颜色)模式解析(一) Android Q 在系统设置中提供了可设置的色彩模式,当然这功能很多厂商早就有了-,落后归落后,我们还是看看Android是怎么实现的! Android ...
- 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 TaskMasger 启动
1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [四] 上一篇: [flink]Flink 1.12.2 源码浅析 : yarn-per-job模式解析 Jo ...
- 跨境电商独立站模式解析与机会分析?
随着以Shopify为代表的快速建站工具的兴起,独立站的入门门槛大大降低.因此2019年可能会是独立站一个新的爆发之年.那么国内跨境电商独立站是如何发展演变的,到底有哪些模式?对于希望转型做独立站的平 ...
- 实体操盘手开发分销商城小程序商业模式解析
今天董技叔要分享的分销模式是B2C新零售的一种,说起新零售分销商城系统开发这个模式已经是非常普片了,但模式千千万,每一家企业都可以拥有一套属于自己的分销模式来构建商业体系 ,分销模式在小程序里依旧是主 ...
- 4种常见分支模式解析及优劣对比 | 研发效能提升36计
摘要:4 种常见分支模式解析及优劣对比.团队研发的本质并不是团队规模越大,研发的效率就越高.我们以为团队规模越大,研发效率就会越高,可以做越多的东西,但是我们发现团队规模大到一定程度,整个研发效率是会 ...
- 联想 n700 android,联想双模触控无线鼠标N700 双模式解析
联想双模触控无线鼠标N700 双模式解析 底部开关特写 联想双模触控无线鼠标N700鼠标在底部,有一个模式切换开关,我们看到档位是三档,但是实际上这个模式开关只有两档.最左边的是蓝牙4.0配对模式, ...
最新文章
- 第39-43课 thinkphp5完成商品会员价格功能(后置勾子afterInsert)
- IOS-TextField控件详解
- mysql5.7.18压缩包下载_Windows安装MySQL5.7教程
- 【Node学习】—Express框架的安装
- 这不是我想要的Serverless
- QByteArray使用方法举例
- L2-010 排座位 (25 分)(并查集)
- 笔记本锁定计算机功能键,如何锁上笔记本键盘_怎样锁定笔记本键盘
- 安川机器人io对照表_安川机器人IO信号对照表.pdf
- 云计算系统典型物理架构
- 关键词云图,实现搜索功能
- 广州艺术博物院走进春睡画院旧址“云赏画”
- 模糊综合评价在实际问题中的应用(案例)
- 利用ajax实现excel报表导出(解决乱码问题)
- 去中心化存储项目终极指南 | Filecoin, Storj 和 PPIO 项目异同 (上)
- 全息投影的三种热门展示方式
- 比较好用的服务器和网站在线测速工具分享
- Galaxy S4 GT-I9500如何root 安卓5.0.1
- real time robust malicious traffic detection via frequency domain analysis记录一下
- Idea中使用maven打包出现Cleaning up unclosed ZipFile for archive?
热门文章
- 写给20岁---30岁男人的忠告
- python sorted原理_深入理解Python的sorted
- manjaro增加虚拟内存(linux通用)
- pyecharts绘制地铁图_Python数据分析:柱形图的绘制方法
- mysql是mpp数据库_mysql迁移mpp数据库Greenplum
- 互联网快讯:极米多款产品获消费者青睐;粉笔科技口碑护城河拓展至线下;网信部门工作督导组进驻豆瓣网
- java 格式化日期_Java的日期格式化常用方法
- ImageView 使用详解
- ESP8266-使用浏览器动态连接路由器
- 百度地图API浏览器端的使用