文件名及版权信息
1、文件名
C++文件名全部都是小写,且单词之间用_
分割,如:verilog_parse.cpp
2、版权信息
所有文件均需要统一格式的版权信息。
头文件
1、在.h
中使用#define
来防止头文件被多重包含,并在最后注释出宏的名字
命名格式:<PROJECT>_<FILE>_H_
#ifndef TIMER_FLOW_H_
#define TIMER_FLOW_H_
...
#endif // TIMER_FLOW_H_
2、尽量避免使用前置声明,优先使用#include
来包含相关头文件
包含头文件:
#include "timer.h" // 优先使用
前置声明:
class Timer; // 尽量避免
3、#include
的包含顺序
头文件的包含顺序为:当前.cpp
文件直接关联的头文件、C库文件、C++库文件、其他项目的头文件、本项目中的其他头文件。
// timer.cpp
#include "timer.h" // 关联头文件
#include <stdio.h> // C库文件
#include <string> // C++库文件
#include "verilog.h" // 其他项目的头文件
#include "node.h" // 本项目中的其他头文件
4、头文件应向稳定的方向包含
头文件的包含关系是一种依赖,一般来说,有以下几种包含原则。
应当让不稳定的模块依赖稳定的模块,从而当不稳定的模块发生变化时,不会影响(编译)稳定的模块。
禁止头文件循环依赖,指a.h包含b.h,b.h包含c.h,c.h包含a.h。
禁止包含用不到的头文件。
头文件应当自包含,指任意一个头文件均可独立编译。
作用域
1、在命名空间的最后注释出命名空间的名字
namespace mynamespace
{
// 注意不要使用缩进
...
} // namespace mynamespace
2、避免使用using
引入整个命名空间的标识符号
// 禁止 —— 污染命名空间
using namespace mynamespace;
3、避免进行大量构造及析构操作
for (int i = 0; i < 10000; i++) {
Timer t; // 低效,构造函数和析构函数分别调用10000次
t.DoSomething(i);
}
建议使用以下代码:
Timer t; // 构造函数和析构函数只调用1次
for (int i = 0; i < 10000; i++) {
t.DoSomething(i);
}
4、尽量在变量声明时进行初始化,且避免进行无效的初始化
int i;
i = f(); // 坏 —— 声明与初始化分离
int j = g(); // 好 —— 声明时初始化
int k = 0; // 坏 —— 无效初始化
k = 0xff;
5、尽量避免全局函数和全局变量,使用命名空间或static
关键字等进行作用域限制
6、禁止定义静态储存周期非POD变量
静态存储周期变量,即包括了全局变量、静态变量、静态类成员变量和函数静态变量,都必须是原生数据类型(POD:Plain Old Data):即int
、char
和float
,以及POD类型的指针、数组和结构体。
禁止使用类的静态存储周期变量,即禁用vector
和string
等:由于构造和析构函数调用顺序的不确定性,它们会导致难以发现的bug。不过constexpr
变量除外,因为它不涉及动态初始化和析构。
类
1、不要在构造函数和析构函数中调用虚函数
如果在派生类的构造函数和析构函数中调用了虚函数,这类调用不会重定向到派生类的虚函数实现。
2、不要在无法报出错误时进行可能失败的初始化
如果代码允许,直接终止程序是一个合适的处理错误方式。否则,可以考虑用Init()
方法或工厂函数。
3、建议使用初始化列表构造对象,且和构造函数、析构函数一样放在类外实现
使用初始化列表构造对象,比起构造函数的代码块初始化效率更高、性能更好。
// a.h
class A
{
public:
A() : {
};
private:
int m_a;
B* m_b;
};
// a.cpp
A::A() : m_a(0), m_b(nullptr) {
}
4、不要定义隐式转换类型
对于转换运算符和单参数构造函数,建议使用explicit
关键字。
举例如下:
class Things
{
public:
explicit Things(const std::string &name = "") :
m_name(name)