1.3. 前置声明 Tip
尽可能地避免使用前置声明。使用 #include 包含需要的头文件即可。
定义:
所谓「前置声明」(forward declaration)是类、函数和模板的纯粹声明,没伴随着其定义. 优点:
前置声明能够节省编译时间,多余的 #include 会迫使编译器展开更多的文件,处理更多的输入。 前置声明能够节省不必要的重新编译的时间。 #include 使代码因为头文件中无关的改动而被重新编译多次。 缺点:
前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的重新编译过程。
前置声明可能会被库的后续更改所破坏。前置声明函数或模板有时会妨碍头文件开发者变动其 API. 例如扩大形参类型,加个自带默认参数的模板形参等等。
前置声明来自命名空间 std:: 的 symbol 时,其行为未定义。
很难判断什么时候该用前置声明,什么时候该用 #include 。极端情况下,用前置声明代替 #include 甚至都会暗暗地改变代码的含义:
// 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 被 B 和 D 的前置声明替代, test() 就会调用 f(void*) . 前置声明了不少来自头文件的 symbol 时,就会比单单一行的 include 冗长。 仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员)会使代码变得更慢更复杂. 结论:
尽量避免前置声明那些定义在其他项目中的实体. 函数:总是使用 #include. 类模板:优先使用 #include. 至于什么时候包含头文件,参见 1.5. #include 的路径及顺序 。
总结:前置申明有好有坏。原理是:编译时不关注类的细节,单用指针(4字节)分配即可,等真正运行时才关注类型细节,这样好处是提高了编译效率,但是也会造成歧义,代码编写不好就会造成程序的错误,比如上面的void 和D无法区分时,编译器就直接当作void了。*
|