最近,在公司移植需求,遇到一些比较有代表性的难题,忽然发现没有足够的知识储备,也设计不出好的解决方案,导致这次移植云升级功能时,严重阻塞产品导入的进度。
这本书从同事那里了解到,一个Pimpl惯用法让我对这本书产生了浓厚的兴趣,借鉴书中的思想,重构了我的功能,很快问题得到解决,产品为此没有付出更多代价。
想着,既然要深入学习《C++ API设计》,就这么干看,没有输出也是不好,记录读书笔记,也能加深我的理解,若有幸其他网友通过我的笔记了解到此书,然后深刻阅读此书,提高编程技能,拿到更高薪资,也是极好的。
Pimpl惯用法
产品中的需求,云升级,设备可以向HTTP服务器查询新版本信息,继而执行升级操作。起初我的重构版本,引入一个Manager类,单例创建对象,图1。
![](https://img.haomeiwen.com/i3116615/912a90096057afe6.png)
此管理类,是重构之前更糟糕的代码而来,本以为可保一方平安;开机后start(),无论本地GUI、还是Web只要调用到check(),满足条件后upgrade()即可完成升级。
但是,问题来临了!客户的所有需求只要移植到新建立分支中,虽两个分支差别不是很大,但有些业务还是新分支也简化,最重要的是新分支,GUI界面抽库了。
抽库就以为着,本意解耦合,更方便管理。升级功能需要界面能实现完成,GUI点击check,条件满足,点击upgrade即可,再getUpgradeInfo用于显示进度条即可。
移植过程中,本意想包含Manager类的头文件,即可实现需求移植,但是操作后发现我错了。发生了“头文件包含的爆炸行为“----我给起的名字,因为升级功能之前只在一个库中玩耍,比如libapp.a,所有调用随便玩;现在GUI被移植到libGUI.a,现在我想调用Manager类就要包含头文件,犹豫Manager类复杂,各类功能的引用,想要使用Manager,也就意味需要导入其他模块,这对于只完成页面显示、控件布局库是完全错误操作。
Manager类,对于跨模块的功能提供是非常不友好。设计之初,松耦合、高内聚概念未考虑到。问题提出就像如何解决此问题,开始之初就想,对于libGUI.a来说,它只需一个Manager头文件即可,其他所有东西都是多余的,它不需要知道Manager类其他细节。
想法提出就要解决,其实解决这个问题方法还是很多的,组件方式等等。在这个需求中想到了组件的重量级,于是选择了Pimpl惯用法来解决。
Pimpl,英文pointer to implementation,即指向实现的指针。该技巧避免在公有头文件中暴露私有细节,实现了API接口和实现的完全分离。
![](https://img.haomeiwen.com/i3116615/3246100b49ba2389.png)
在公有类拥有一个私有指针,该指针指向隐藏的实现细节。该方法将实现细节从公有头文件中分离出来。理论知识了解到了,接下来看看,我的需求是如何解决的,图3。
![](https://img.haomeiwen.com/i3116615/0885e60891a6d7fc.png)
把CCloudUpgradeManagerImp当做CCloudUpgradeManager的私有嵌入类,对外的API只提供几个必要接口,其余细节完全隐藏,而且引用的模块只需包含CCloudUpgradeManager一个头文件即可。
实现代码部分,图4
![](https://img.haomeiwen.com/i3116615/fe16e3eac2013362.png)
对于私有类CCloudUpgradeManagerImp采用初始化列表形式赋初值,析构时同时要delete掉,防止内存泄漏。这是Pimpl法容易出问题的地方,程序员难免会忘记delete,针对这一问题,可引入智能指针,图5
![](https://img.haomeiwen.com/i3116615/5bfd545a98e43a56.png)
至此,我的问题就解决。功能主体设计没有问题,剩下就是一些细节优化。
以下摘自原文
Pimpl的优点:
1. 信息隐藏。私有成员现在可以完全隐藏在公有接口之外,使得实现细节得以隐藏。头文件也更加干净、清晰便于理解。
2. 降低耦合。 以上文例子为例,两个模块不必再重复包含头文件,把依赖项放到cpp文件中。
3. 加速编译。
4.更好的二进制兼容性。采用Pimpl法的对象大小从不改变,因为对象总是单个指针的大小。实现类的修改只影响cpp实现类的大小。
5.惰性分配。m_cloudImp成员可以在真正使用时才new,(和代码实现有关系),我的例子中不满足此情况。
Pimpl的缺点:
1. 需要手动释放资源。
2. 真正执行的操作,需要中间增加一层指针的间接调用,增加开销。
3. 通过间接访问,增加了阅读代码的难度,同是引入类似m_cloudImp->的前缀,程序员书写代码也变得复杂。
不过就以上说的缺点,对于优点来说,也是说的过去的,凡事都有两面性嘛!
以上就是我对Pimpl的理解,现学现卖,正好他又解决了我的问题。若您看到这篇文章,有不恰当的地方,欢迎批评指正~~~
网友评论