找回密码
 立即注册
收起左侧

C++老鸟日记023 如何避免修改头文件时导致项目重新编译

5
回复
8237
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2018-9-16 22:56:35 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 baizy77 于 2018-10-1 20:55 编辑

版权声明
---------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白 (白振勇)
转载请注明出处!
---------------------------------------------------------------------------------------------------------------------
本套课程属于:《C++跨平台开发干货》系列课程。
-----------------------------------------------------------------------------

引言:
----------------------------------------------------------------------------
       我们都知道,在使用某个class或者struct的时候,需要包含这个class或者struct所在的头文件,以便成功编译。但是,如果在一个大型项目中,代码动辄几十万行,如果一旦修改头文件,就会导致包含该头文件的所有文件都要重新编译,这将耗费非常多的时间。那么,怎样才能尽可能防止重新编译呢?

正文:
----------------------------------------------------------------------------
       很多童鞋马上想到了使用增量编译。其实,对我们来说增量编译是基本的设置,因此我们这里假定已经使用了增量编译。那么还有什么方法可以做到吗?
       答案是,当然有了。
       我们在头文件中使用某个class的时候,如果不是使用对象而是使用指向对象的地址的指针,那么我们就可以轻松做到避免不必要的编译了。
       假定类型CMyClass是在他处定义的类,我们的代码如下:

  1. // user.h

  2. #include myclass.h       // CMyClass所在的头文件
  3. class CUser {
  4. public:
  5.        ……
  6. public:
  7.        string getInfo(CMyClass& obj); // 此处用到了CMyClass
  8. };
复制代码
  1. // user.cpp
  2. #include “user.h”
  3. string CUser::getInfo(CMyClass& obj) {
  4.     pObj->xxx();
  5. }
复制代码

       在上述代码中,”user.h”中,因为接口getInfo使用CMyClass定义了对象,因此编译器必须知道该对象是什么样子,所以它要求我们包含myclass.h以便知道其具体定义。但是,一旦CMyClass的定义发生变化,那么user.h将被重新编译,这会导致所有包含user.h的文件都被重新编译。这是没有必要的,而且浪费编译时间。

       我们可以换用另一种设计,把对象改为指针,具体代码如下:
  1. // user.h
  2. class CMyClass;     // CMyClass的声明,用来告诉编译器,CMyClass是一个类,但是在其他地方已定义。
  3. class CUser {
  4. public:
  5.         ……
  6. public:
  7.         string getInfo(CMyClass* pObj); // 此处用到了CMyClass*的指针
  8. };
复制代码

  1. // user.cpp
  2. #include “user.h”
  3. #include “myclass.h” // 仅需要在cpp中包含该头文件即可
  4. string CUser::getInfo(CMyClass* pObj) {
  5. }
复制代码

    在改动后的代码中,在user.h中,getInfo() 接口不再使用CMyClass对象,而是使用CMyClass定义了一个指针。因此仅需要在user.h的开头部分使用前向声明把CMyClass声明一下即可。在真正使用的地方是在user.cpp中,因为getInfo需要使用CMyClass指针调用接口,所以在user.cpp中增加了 #include “myclass.h”。这样就无需在user.h中包含myclass.h了。

结语:
----------------------------------------------------------------------------
       我们所作的改动设计:在头文件的接口中使用指针传递参数,在头文件开头部分使用前向声明。这种设计,将CMyClass改动时影响的代码范围控制到最小(在本例中,仅影响user.cpp,而不会影响user.h,否则会连带影响包含user.h的代码)。建议大家使用前向声明的方式使用引入的类,避免直接#include 类的头文件。


参考资料
----------------------------------------------------------------------------
《C++编程思想》两卷合订本中文版(5.6.2章节),(美) Bruce Eckel  Chuck Allison著
回复

使用道具 举报

累计签到:436 天
连续签到:1 天
2018-9-17 15:48:59 显示全部楼层
也就是所谓的pImpl
回复 支持 反对

使用道具 举报

累计签到:41 天
连续签到:1 天
2018-9-17 17:22:45 显示全部楼层
miroox 发表于 2018-9-17 15:48
也就是所谓的pImpl呗

您给的文章很好。推荐。
回复 支持 反对

使用道具 举报

累计签到:50 天
连续签到:1 天
2018-9-21 08:08:33 显示全部楼层
  string getInfo(CMyClass& obj);    此处是引用么?     如果是引用也箱单于指针么?

回复 支持 反对

使用道具 举报

累计签到:41 天
连续签到:1 天
2018-9-21 08:36:46 显示全部楼层
tan 发表于 2018-9-21 08:08
string getInfo(CMyClass& obj);    此处是引用么?     如果是引用也箱单于指针么?

...

是引用。作用跟指针完全一样。唯一的不同是使用引用的语法,可以使代码看上去更像是对一个对象操作,因为使用指针时需要用->。
回复 支持 反对

使用道具 举报

累计签到:50 天
连续签到:1 天
2018-9-22 11:03:07 显示全部楼层
baizy77 发表于 2018-9-21 08:36
是引用。作用跟指针完全一样。唯一的不同是使用引用的语法,可以使代码看上去更像是对一个对象操作,因为 ...

ok                                   
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了