1、前置声明是C/C++开发中比较常用的技巧,主要用在三种情形:
1>、变量/常量,例如 extern int var1;
2>、函数,例如 void foo();
注意类的成员函数无法单独做前置声明;
3>、类,例如 class Foo;,
也可以前置声明模板类:template class<typename T1, int SIZE>Foo;
如果类包含在名字空间中,需在名字空间内做前置声明:namespace tlanyan {class Foo;};,而不能这样:class tlanyan::Foo;
2、优势:
- 节省编译时间。(修改某个头文件后需要编译多个无关的依赖文件)
- 避免重复定义变量;
- 避免引入函数定义/声明文件,从而函数文件发生更改时不会重新编译依赖文件;
- 解决循环依赖问题。
3、有人推荐使用前置声明,有人不推荐使用,理由在哪?
Google内部从『倾向于使用前置声明』到『倾向于使用#include』的这个过程
事实上在很多年前Google内部就开始了对这两者的比较和探讨。
在2014年,内部有一篇总结性文章指出了前置声明将造成的十种危险。
最终C++ Code Style经历了一个过渡期之后全面倒向了#include。
简单来说,前置声明最大的好处就是『节省编译时间』。
毕竟C++的编译时间长已经是一个臭名昭著人人喊打的问题。
但对于Google来说,这方面的效率节省就不见得那么可观了——毕竟Google内部有超大规模的分布式编译集群『Forge』。
哪怕是十万以上的target,全部build一遍也就是几分钟的事情。
与此同时,前置声明带来的问题则显得更加关键:
例如,如果一个类的实现者需要把这个类改个名字/换个命名空间,出于兼容性他原本可以在原命名空间里/用原名通过using来起一个别名指向新类。然而别名不能被前向声明。
内网有一份代码改动一下子试图修改总计265个头文件,就是实现者为了要改这个类的名字而不得不去改所有的调用处。
想一想,如果这265个文件分属于50个不同的团队,你得拿到50个人的同意才能提交这份改动,想不想打人?
再举一个code style中提到的,更为严重的例子——它可能导致运行时出现错误的结果:
// b.h:
struct B {};
struct D : B {};
// good_user.cc:
#include "b.h"
void f(B*);
void f(void*);
void test(D* x) { f(x); } // calls f(B*)
若把 #include 换成前置声明,由于声明时不知道 D 是 B 的子类,test() 中 f(x) 就会导致f(void*) 被调用,而不是 f(B*)。
再比如,C++标准5.3.5/5中规定,delete一个不完整类型的指针时,如果这个类型有non-trivial的析构函数,那么这种行为是未定义的。
把前置声明换成#include则能保证消除这种风险。
诚然,从理论上说,一个牛逼的程序员当然是可以通过分析一个头文件的源码来决定会不会碰到以上诸多坑,并由此决定用哪个好。
但一来不是所有人都是牛逼程序员,二来,把精力花费在这件事情上真的值得吗?