C++的前置声明

前言

  今天一开始看一篇几年前翻译的Google风格规范,知道了前置声明“可以显著减少需要包含的头文件数量”,因此搜索读了一些文章,最后才发现现在的Google风格规范是不推荐使用的。这一篇大概写了3个小时,然后在剪贴板被我覆盖了,重写花了1个多小时。

前置声明是什么

「前置声明」(forward declaration)是类、函数和模板的纯粹声明,没伴随着其定义。

  在类的声明(.h)文件中使用另一个类有两种方式,一种自然是直接包含头文件,另一种就是前置声明。
如下,这样做可以通过编译,但是为什么呢。

//foo.h
#pragma once

class bar;

class foo
{
public:
	Bar getBar();
private
	Bar* _bar;
};

如何使用前置声明

  前置声明要求:其声明的类是文件所声明的类的数据成员时,是指针成员或引用成员(而不是对象成员);其声明的类是文件所声明的类的成员函数的参数或返回值时,该函数在文件中不存在定义。
  其声明的类是文件所声明的类的数据成员时,因为编译器申请空间时需要其定义,而指针需要的空间固定(引用的实现也基于指针)。
  其声明的类是文件所声明的类的成员函数的参数或返回值时,因为函数的声明并没有使用到该类的定义,而函数的定义则很大可能使用到该类的定义。但是一般情况下,除非函数定义极短,否则成员函数的声明和定义一般是分离在.h文件和.cpp文件中的,所以可以说其声明的类是文件所声明的类的成员函数的参数或返回值时可以使用前置声明

为什么使用前置声明

  • 当两个类相互包含头文件时无法通过编译。(必须使用)
  • 有助于分离类的声明和定义文件。(只在.cpp文件中include .h文件)(按照个人风格)
  • 节省编译时间。(修改某个头文件后需要编译多个无关的依赖文件)(按照需求)

为什么不使用前置声明

引用Google C++ 风格规范

尽可能地避免使用前置声明。

  • 前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过 必要的重新编译过程。
  • 前置声明可能会被库的后续更改所破坏。前置声明函数或模板有时会妨碍头文件开发者变动其 API。例如扩大形参类型,加个自带默认参数的模板形参等等。
  • 前置声明来自命名空间 std:: 的 symbol 时,其行为未定义。
  • 极端情况下,用前置声明代替 includes 甚至都会暗暗地改变代码的含义:
// 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); }  // 调用 f(B*)
//如果 #include 被 B 和 D 的前置声明替代, test() 就会调用 f(void*) 。
  • 前置声明了不少来自头文件的 symbol 时,就会比单单一行的 include 冗长。
  • 仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员)会使代码变得更慢更复杂。

  注:“跳过重新编译过程”和“极端情况”应是在在.cpp文件中使用了前置声明,最小化include的情况下发生的,个人认为如果只是在.h文件中使用前置声明减少依赖,前置声明是有益无害的。

总结

  前置声明最好只在需要时使用,并且不在定义文件中使用。

参考资料

  Google C++ 风格规范(最新)_readthedocs.io
  Google 编程规范(2016.07)_csdn.net
  C++中头文件相互包含与前置声明_cnblogs.com
  如何使用前置声明取代包括头文件_csdn.net

  • 13
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值