C++特性
- 不要使用exceptions
- 不要使用RTTI
- 除非你能熟练地掌握templates的使用,否则不要使用
Qt源码中的传统
- 所有的代码都是ASCII字符集
- 原理:我们内部的语言环境太多,而且UTF-8和latin1系统的组合不健康。通常,您甚至不知道通过单击您喜欢的编辑器中的“保存”就可以破坏编号大于127的字符。
- Object子类都必须具有Q_OBJECT宏,即使它没有信号或插槽也是如此,否则qobject_cast将失败。
- 在connect语句中标准化信号+插槽的参数(请参阅QMetaObject :: normalizedSignature),以更快地进行信号/插槽查找。您可以使用qtrepotools / util / normalize标准化现有代码。
包含头文件
- 在公共头文件中,请始终使用以下形式包括Qt头:#include <QtCore / qwhatever.h>。库前缀对于Mac OS X框架是必需的,对于非qmake项目非常方便。
- 在源文件中,首先包括专用头文件,然后是通用头文件。用空行分隔类别。
#include <qstring.h> // Qt class
#include <new> // STL stuff
#include <limits.h> // system stuff
- 如果需要包括qplatformdefs.h,请始终将其作为第一个头文件。
- 如果需要包括专用头,请小心。不管what_p.h位于哪个模块或目录中,请使用以下语法。
#include <private/whatever_p.h>
类型转换
- 避免使用C风格的类型转换,使用C++采用的类型转换 (
static_cast
,const_cast
,reinterpret_cast
)- 原理:reinterpret_cast和C样式强制转换都是危险的,但是至少reinterpret_cast不会删除const修饰符
- 不要使用dynamic_cast,不要对QObject使用qobject_cast或重构设计,例如通过引入type()方法(请参阅QListWidgetItem)。
- 使用构造函数转换简单类型:int(myFloat)代替(int)myFloat
- 原理:重构代码时,编译器会立即通知您是否强制转换会很危险。
编译器/平台 相关的问题
- 使用问号运算符时要格外小心。如果返回的类型不同,则某些编译器会生成在运行时崩溃的代码(您甚至不会收到编译器警告)。如果返回的类型不同,则某些编译器会生成在运行时崩溃的代码(您甚至不会收到编译器警告)。
QString s;
return condition ? s : "nothing"; // crash at runtime - QString vs. const char *
- 对对齐要格外小心
- 任何具有构造函数或需要运行代码进行初始化的内容都不能用作库代码中的全局对象,因为我们很难确定构造函数/初始化代码会在什么时候运行(首次使用,库加载时,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");
}
- 字符是带符号的还是无符号的,取决于体系结构。如果您明确想要一个有符号/无符号字符,请使用有符号字符或无符号字符。以下代码中的条件在默认字符为无符号的平台上始终为true。
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位枚举值
- aapcs嵌入的ABI硬编码将所有枚举值编码为32位整数。
- Microsoft编译器不支持64位枚举值。 (已针对x64与Microsoft®C / C ++优化编译器版本15.00.30729.01确认)
美学
- 优先使用枚举来定义常量而不是静态const int或define
- 枚举值将在编译时由编译器替换,从而使代码更快
- 定义不是名称空间安全的(看起来很难看)
- 在头文件中首选详细参数名称
- 大多数IDE将在其补全框中显示参数名称。
- 在文档中看起来会更好
- 重新实现虚函数时,请勿将
virtual
关键字放在头文件中。- 在Qt5上,在函数声明之后,’;'之前用
override
关键字注释它们。
- 在Qt5上,在函数声明之后,’;'之前用
要避免的事情
- 不要从模板/工具类继承
- 析构函数不是虚函数,导致潜在的内存泄漏
- 这些符号不会被导出(并且大多是内联的),从而导致有趣的符号冲突。
二进制和源码兼容性
- 定义
- Qt 4.0.0是主要版本,Qt 4.1.0是次要版本,Qt 4.1.1是补丁程序版本
- 向后二进制兼容性:链接到库的早期版本的代码保持正常工作
- 向前的二进制兼容性:链接到新版本库的代码可与旧库一起使用
- 源代码兼容性:代码无需修改即可编译
- 在次要版本中保持向后二进制兼容性+向后源代码兼容性
- 在修补程序版本中保持向前和向后二进制兼容性+向后和向后源代码兼容性
- 请勿添加/删除任何公共API(例如,全局函数,公共/受保护/私有方法)
- 不要重新实现方法(甚至不是内联方法,也不是受保护/私有方法)
- 检查二进制兼容性变通办法,以保持b / c
- 编写QWidget子类时,即使它为空,也要始终重新实现event()。这确保了可以在不破坏二进制兼容性的情况下修复小部件。
- 从Qt导出的所有函数必须以’q’或’Q’开头。使用“符号”自动测试来查找违规。
运算符
平等对待两个参数的二进制运算符不应是成员方法。因为,除了堆栈溢出答案中提到的原因外,当运算符是成员时,参数也不相等。
QLineF的示例,不幸的是它的运算符==作为成员:
QLineF lineF;
QLine lineN;
if (lineF == lineN) // Ok, lineN is implicitly converted to QLineF
if (lineN == lineF) // Error: QLineF cannot be converted implicitly to QLine, and the LHS is a member so no conversion applies
如果运算符 ==在类之外,则转换规则将同样适用于双方。
公共头文件的传统
我们的公共头文件必须在某些用户的严格设置下仍然有效。所有已安装的头文件声明都必须遵循以下规则:
- 不要使用C风格的类型转换
- 不要有浮点数比较
- 不要将虚函数隐藏在子类中
reference |