变量声明
-
每行只声明一个变量
-
避免使用短的/无意义的命名
-
当一个变量被用到时再声明
// Wrong int a, b; char* c, * d; // Correct int height; int width; char* nameOfOne; char* nameOfOther;
变量命名
-
变量名/函数名采用驼峰命名法(lowerCaseCamel), 首字母缩写词出现的命名中, 缩写也用驼峰命名
// Wrong short Cntr; char ITEM_DELIM = ''; void myXMLStreamReader(); // Correct short counter; char itemDelimiter = ''; void myXmlStreamReader();
空行/空格
-
用一个且仅用一个空行在适当的地方划分代码块
-
在关键词和小括号之间总是只用一个空格符
// Wrong if(foo) { } // Correct if (foo) { }
指针/引用
-
在类型名和
*
或&
之间没有空格, 在*
或&
与变量名之间有一个空格char* someValue; const QString& myString; const char* const WOR = "hello";
符号与空格
- 二元操作符左右两边都有一个空格
- 一元操作符与变量之间不留空格
- 逗号左右没有空格, 右边一个空格
- 分号左边没有空格; 分号作为语句的结束符, 右边一般不再有内容
#
号右边没有空格- 左引号的左边和右引号的各一个空格, 左引号的右边和右引号的左边没有空格
- 如果右引号右边是右括号, 它们之间没有空格
cast
-
避免C语言的cast, 尽量用C++的cast(static_cast, const_cast, reinterpret_cast). reinterpret_cast 和 C风格的cast用起来都是危险的,但至少 reinterpret_cast 不会把const修饰符去掉
-
涉及到QObjects或重构自己的代码时,不要使用dynamic_cast,而是用qobject_cast,例如在引进一个类型的方法时
-
用构造函数去cast简单类型,例如:用int(myFloat)代替(int)myFloat
// Wrong char* blockOfMemory = (char* ) malloc(data.size()); // Correct char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size()));
语句
- 不要在一行写多条语句
括号
-
每个大括号单独一行
-
不论条件语句的执行部分有几行, 必须使用大括号
-
小括号用来给语句分组
// Wrong if (address.isEmpty()) { return false; }
for (int i = 0; i < 10; +‘‘i) { qDebug("%i", i); }
// Correct if (address.isEmpty()) { return false; } else { return true; }
for (int i = 0; i < 10;i) { qDebug("%i", i); }
// Wrong if (a && b || c)
// Correct if ((a && b) || c)
// Wrong a + b & c
// Correct (a + b) & c
switch语句
-
case缩进
-
除enum外, 每组case最后都要加default;
switch (myEnum) { case Value1: doSomething(); break; case Value2: case Value3: doSomethingElse(); // fall through break; default: defaultHandling(); break; }
goto
- 禁止使用goto
换行
-
每行代码不多于120字符
-
逗号在行尾. 操作符在新行的开头位置
-
换行时尽量避免行与行之间看起来参差不齐
// Wrong if (longExpression + otherLongExpression + otherOtherLongExpression) { }
// Correct if (longExpression + otherLongExpression + otherOtherLongExpression) { }
C++
特性
- 不要使用异常处理
- 不要使用运行时类型识别
- 理智地使用模板
Qt源码中的规范
- 所有代码都是ascii,使用者如果不确定的话,只可能是7字节
- 每一个QObject的子类都必须有Q_OBJECT宏,即使这个类没用到信号或槽。否则qobject_cast将不能使用
- 在connect语句中,使信号和槽的参数规范化(参看 QMetaObject::normalizedSignature),可以加快信号/槽的查找速度。可以使用qtrepotools/util/normalize规范已有代码
包含头文件顺序
- 源文件对应的头文件
- <分隔>
- C系统文件
- <分隔>
- C++系统文件
- <分隔>
- Qt库文件
- <分隔>
- 其他目录
每组文件按字母升序排列
编译器/平台
-
使用三目运算符 ?时要特别小心,如果每次的返回值的类型可能不一样的话,一些编译器会在运行时生成冲突的代码(此时编译器甚至不会报错)
QString s; return condition ? s : "nothing"; // crash at runtime - QString vs. const char *
-
要特别小心对齐问题。无论何时,当一个指针被cast后的对齐数是增加的时候,它都可能会崩溃。例如一个const char 被cast成了cons int,当cast之后的数字不得不在2或4个字节之间对齐时,指针就会在机器上崩溃
-
任何需要需要执行构造函数或相关代码进行初始化的实例,都不能用作库代码中的全局实例。因为当构造函数或代码将要运行的时候,该实例还没有被定义(在第一次调用该实例时,在加载库时,在执行main()之前)
// global scope static const QString x; // Wrong - default constructor needs to be run to initialize x static const QString y = "Hello"; // Wrong - constructor that takes a const char * has to be run QString z; // super wrong static const int i = foo(); // wrong - call time of foo() undefined, might not be called at all
可以使用下面方法:// global scope static const char x[] = "someText"; // Works - no constructor must be run, x set at compile time static int y = 7; // Works - y will be set at compile time static MyStruct s = {1, 2, 3}; // Works - will be initialized statically, no code being run static QString *ptr = 0; // Pointers to objects are ok - no code needed to be run to initialize ptr
-
用Q_GLOBAL_STATIC定义全局实例
Q_GLOBAL_STATIC(QString, s) void foo() { s()->append("moo"); }
-
char型变量是有符号的还是无符号的取决于它运行环境的架构。如果你明确地想使用一个signed或unsinged char,就使用signed char或unsigned char。以下代码运行在把char默认为无符号的平台上时,其条件判断恒为真
char c; // c can't be negative if it is unsigned /********/ /*******/ if (c > 0) { … } // WRONG - condition is always true on platforms where the default is unsigned
-
避免64位的枚举值
- 嵌入式应用系统二进制接口将所有的枚举类型的值硬编码成32位int值
- 微软的编译器不支持64位的枚举值
编程偏好
- 用枚举值定义常量而非用const int或defines
- 枚举值会在编译时被编译器用实际值替换掉,因而运行时得出结果的速度更快
- defines不是命名空间安全的(并且看起来很丑)
- 当重新实现一个虚方法时,在Qt5中,用 Q_DECL_OVERRIDE宏在函数声明之后,分号之前注解它
- 不要把const-iterator和none-const iterator搞混
for (Container::const_iterator it = c.begin(); it != c.end(); ++it) // Wrong for (Container::const_iterator it = c.cbegin(); it != c.cend(); ++it) // Right
命名空间
- 除跟UI直接交互的类外, 其他类必须处在命名空间内
float值
- 用qFuzzyCompare去和delta比较其值
- 用qIsNull去判断float值是不是二进制0,而不是和0.0比较
[static] bool qFuzzyCompare(double p1, double p2) // Instead of comparing with 0.0 qFuzzyCompare(0.0,1.0e-200); // This will return false // Compare adding 1 to both values will fix the problem qFuzzyCompare(1 + 0.0, 1 + 1.0e-200); // This will return true
类的成员命名
-
成员变量一般为名词
-
函数成员一般为动词/动词+名词,但是当动词为get时,get常常省略。当返回值为Bool型变量时,函数名一般以前缀’is’开头
public: void setColor(const QColor& c); QColor color() const; void setDirty(bool b); bool isDirty() const; private Q_SLOTS: void onParentChanged();
构造函数
- 为了使构造函数被错误使用的可能性降到最小,每一个构造函数(除了拷贝构函数)都应该检查自己是否需要加上explicit 符号
注意代码陷阱
- 不要为了图方便少些一些代码。因为代码是一次书写,后期不止一次地要去理解。例如
QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, "volume");
- 改成下面的方式会更容易理解
QSlider *slider = new QSlider(Qt::Vertical); slider->setRange(12, 18); slider->setPageStep(3); slider->setValue(13); slider->setObjectName("volume");
参考资料
- https://wiki.qt.io/Qt_Contribution_Guidelines
- https://wiki.qt.io/Qt_Coding_Style
- https://wiki.qt.io/Coding_Conventions
- https://community.kde.org/Policies/Library_Code_Policy
- https://wiki.qt.io/UI_Text_Conventions
- https://wiki.qt.io/API_Design_Principles
- http://doc.qt.io/qt-5/qml-codingconventions.html
- https://google.github.io/styleguide/cppguide.html