一般来说,每一个.cc或者.cpp文件对应一个头文件(.h文件),当然,也有例外,例如一些测试单元或者main文件,头文件的一些规范可以令代码可读性、程序的性能等大为改观,所以还是要注意头文件的规范问题。

一、#define保护

所有头文件为了防止文件被多重包含(multiple inclusion),一般就需要#define保护。#define保护的格式如下:

<PROJECT>_<PATH>_<FILE>_H_

例如:

#ifndef FOO_BAR_BARZ_H_
#define FOO_BAR_BARZ_H_ ... #endif //FOO_BAR_BARZ_H_

这个是比较老的一个做法了,所以很多编译器基本都是可以兼容的,但是还有另一个语句也是可以起到相同作用,但是一些太老的编译器据说用不了的(但是我好像没遇到过),VS下好像默认新建一个类的头文件就会用这个新的:

#program once

这一种相对于第一种的好处是,不通过#define一个变量来起到防止重复包含,所以,不存在重复变量的问题,#define后面的变量在同一个工程里是不能重复的,重复的话是只认一个的,就可能导致另一个文件编译的时候被排除。

二、头文件依赖

在头文件中减少包含其他头文件,改用前置声明(forward declarations),理由是:头文件被包含的同时会引入一项新的依赖(dependency),只要头文件被修改,代码就要重新编译,如果你的头文件包含了其它头文件,这些头文件的任何改动都将导致那些包含了你的头文件的代码重新编译。

使用前置声明就是在头文件中添加:

class Foo;

然后在对应的源文件中包含对应的头文件

#include <Foo.h>

可以这么做的几种情况:

1)、将数据声明为指针(Foo*)或者引用(Foo&);

2)、参数、返回值为Foo的函数只声明不定义;

3)、静态数据成员可以被声明为Foo,因为静态数据成员的定义在类定义之外;

但是,如果你的类是Foo的子类或者包含类型为Foo的非静态成员数据,则必须包含头文件。

补充说明,什么是声明,什么是定义,一般在头文件中经常看到一个函数,有返回值有输入参数,但是没有主体,或者一个变量声明为指针,但是没有new等操作,像这样的就是声明:

Foo *pFoo;
void setInputNumber(int num);

然后是定义,定义的话就是有函数主体,或者说就有变量的内存操作,包括非指针变量的声明也已经包含了定义,像这样的:

int nInputNumber;
int nCount = 0;
pFoo= new Foo(); void setInputNumber(int num)
{
nInputNumber = num;
}

三、内联函数

当函数小于10行的时候可以定义为内联函数(inline function)。

定义:当函数被声明为内联函数后,编译器会将其内联展开,无需像通常的函数调用机制一样来调用内联函数。

优点:可以令目标代码更高效。适合存取函数或一些较短的关键代码。

缺点:滥用内联适得其反,内敛较大代码(如果编译器允许),将增加代码量;

不可用或者尽量不要用内联函数的情况:

1)、超过10行代码;

2)、析构函数;

3)、递归函数(可能大多数编译器不支持);

4)、包含循环或者switch语句的函数;

四、头文件包含实现代码

一般来说,头文件是只包含声明,而不要包含实现代码的,但是用到内联函数或者函数模板的话就需要将代码实现写在一起,为了可读性,是可以新建一个后缀为-inl.h的头文件,专门用于存放内联函数和模板函数,这样可以增强代码可读性。

需要注意,这样的头文件也要加#define保护的。

五、函数参数顺序(Function Parameter Ordering)

顺序:输入参数在前,输入输出参数居中,输出参数在后。

或者:输入参数在前,输入输出参数居中,输出参数在后,控制参数垫后。

输入参数一般为传值或者常数引用(const references),输出或者输入输出参数一般为传非常数指针(non-const pointers),有些参数是输入参数,但是属于控制变量意义的参数,那我是觉得应该放最后面的。

六、包含头文件顺序

如果我有一个命名为Foo.h的头文件以及一个对应的Foo.cpp的源文件,那么,头文件中的包含顺序应该是:

C库 > C++库 > 其他库的.h文件 > 项目内的.h文件;

而源文件中的包含顺序应该是:

#include "Foo.h" > C库 > C++库 > 其他库的.h文件 > 项目内的.h文件

还有一点强迫症的可以将同目录下的头文件按首字母顺序来排列。

补充说明,#include ""和#include <>的区别:

1)、#include <>引用的是编译器的类库路径里面的头文件;一般编译器会在编译器设置的include目录和系统中的INCLUDE环境变量中找头文件;一般用于标准文件;

2)、#include ""引用程序目录的相对位置的头文件;一般是先从当前文件所在的文件夹内找,找不到再去编译器设置的include目录或者系统的INCLUDE环境变量中找;一般用于自定义的文件。

结语

编程规范一方面是给机器看,提高代码的效率,另一方面也非常重要的用于给人看,提高人的效率。上述规范不一定就完全正确或者必须这么做,只是给个建议,看别人代码是超级累的一件事,简直想大呼这TM写的啥啊,何况更可怕的是看完发现这TM是我自己写的代码,想死的心都有了,利人终利己,就算出错了也好意思找别人帮忙看啊。

川口雨晴风复止,蜻蜓上下鱼东西。

[C/C++]编程规范一:头文件篇的更多相关文章

  1. arpa/inet.h所引起的Segmentation fault及网络编程常见的头文件

    最近在学习Linux网络编程方面的知识,感觉还是有些困难.主要是对协议过程的理解,还有socket的API的理解不够深刻.今天复习编写了一个TCP的服务端和客户端的程序实现client.c从命令行参数 ...

  2. c/c++编码规范(1)--头文件

    最近工作稍微轻松一点,就再学习了一下编码规范.遂记些笔记,以便查阅. 这次学习的是Google 开源项目风格指南中文版,地址是:http://zh-google-styleguide.readthed ...

  3. linux系统编程快速定位头文件的技巧之强大的grep命令

    这个技巧来自于我的实际开发碰到的: inet_addr这个函数用于把ip地址转成网络字节序,他的原型:in_addr_t inet_addr(const char *cp); 返回值为一个in_add ...

  4. UNIX环境高级编程 apue.h头文件的配置

    http://jimslinbing.blog.163.com/blog/static/85054319201292712414518/ 1.到http://www.apuebook.com下载源码2 ...

  5. C++编程规范和编译过程详解

    前言:因为c++基础打得不牢,所以准备花点时间再学一下c++的基础知识,主要是看网易云课堂里面的免费课程,把一些知识点做个笔记记下来. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ...

  6. 头文件intrins.h

    intrins.h 在C51单片机编程中,头文件INTRINS.H的函数使用起来,就会让你像在用汇编时一样简便. 内部函数 描述 _crol_ 字符循环左移   _cror_ 字符循环右移   _ir ...

  7. C51单片机头文件和启动文件

    STARTUP.A51//启动文件. 清理RAM.设置堆栈等.即执行完start.a51后跳转到.c文件的main函数 <reg51.h>  //特殊寄存器的字节地址和位地址,sfr定义字 ...

  8. C++编程规范之23:头文件应该自给自足

    摘要: 各司其责:应该确保所编写的每个头文件都能够独自进行编译,为此需要包含其内容所依赖的所有头文件. 如果一个文件包含某个头文件时,还要包含另一个头文件才能工作,就会增加交流障碍,给头文件的用户增添 ...

  9. google C++编程风格指南之头文件的包括顺序

    google C++编程风格对头文件的包括顺序作出例如以下指示: (1)为了加强可读性和避免隐含依赖,应使用以下的顺序:C标准库.C++标准库.其他库的头文件.你自己project的头文件.只是这里最 ...

随机推荐

  1. 002.Delphi插件之QPlugins,菜单插件

    运行之后的效果如下, 图一 图二 主界面代码如下 unit Frm_Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUt ...

  2. 数据库连接需要dll

    连接oracle引用: Oracle.ManagedDataAccess.dll和Oracle.ManagedDataAccess.EntityFramework.dll, 连接sqlserver 连 ...

  3. Web基础之Dubbo

    Dubbo RPC即Remote Procedure Call,即为远程调用.这和Java的远程代理RMI有点类似,不过RMI只能在Java系统之间进行调用,并且是使用序列化对象的方式进行通信.相比之 ...

  4. UVA - 12174 Shuffle (预处理+滑动窗口)

    题意:已知歌单中的歌曲数目s,和部分的播放历史,问下一首可能播放的歌曲种数. 分析: 1.按照歌单数目s,将播放历史划分为几部分. 2.将播放历史的n首歌曲之前加上s首歌曲,之后加上s首歌曲,为防止标 ...

  5. iphone 面试题(转)

    转]iPhone 面试题解答 2011-07-20 0:51 转载自  492437598 最终编辑  492437598 1.main()  {     int a[5]={1,2,3,4,5};  ...

  6. PATH环境 变量

    PATH环境 变量 $HOME/.bash_profile和/etc/profile 两个文件在登陆用户的时候会执行, /etc/profile 所有用户都执行,$HOME/.bash_profile ...

  7. jQuery通过name获取值

    使用jQuery获取name="day"的input对象: 方法1 var dayObj=$('input[name="day"]'); for(int i=0 ...

  8. 配置anaconda 的仓库镜像

    conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config -- ...

  9. Oracle IF-ELSE条件判断结构

    关于条件判断的几个函数: 一.IF-ELSE 判断语句1.IF 语法 IF 表达式 THEN ... END IF; 输入账号名 kiki 以登陆账号 declare v_name ):='& ...

  10. 百度easydl之图像分类构建是否佩戴口罩模型

    2020-02-14 今天试了下百度esaydl的图像分类方面的功能,其优点是主需要上传自己的数据集,不需要关注模型训练,就可以得到相应的结果.最后得到的模型可以调用云api在本地进行运行. 网址:h ...