Qt的编程风格与规范

参考资料:

  1. https://wiki.qt.io/Qt_Contribution_Guidelines
  2. https://wiki.qt.io/Qt_Coding_Style
  3. https://wiki.qt.io/Coding_Conventions
  4. https://community.kde.org/Policies/Library_Code_Policy
  5. https://wiki.qt.io/UI_Text_Conventions
  6. https://wiki.qt.io/API_Design_Principles
  7. http://doc.qt.io/qt-5/qml-codingconventions.html
  8. https://google.github.io/styleguide/cppguide.html

变量声明

  • 声明每个变量都要用独立的一行
  • 避免短的或无心义的命名
  • 单个字符的变量名只适用于用来计数的临时变量,由于此时该变量的用途十分明显
  • 当一个变量被用到时再声明它
// Wrong int a, b; char *c, *d; // Correct int height; int width; char *nameOfThis; char *nameOfThat;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

变量通常命名法

  • 变量名和函数名以小写字母开头,开头以后的部分每一个单词以大写字母开头
  • 避免使用缩写
// Wrong short Cntr; char ITEM_DELIM = ' '; // Correct short counter; char itemDelimiter = ' ';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

变量在Qt中的命名

  • 类名以大写字母开头,公开类以Q开头,紧跟大写字母;公用函数以q开头。(此为Qt内部规范,咱们可不遵照)
  • 首字母缩写词出如今命名中,采用驼峰命名法,如QXmlStreamReader,而不是QXMLStreamReader(即只有第一个字母大写)

空白行与空格的使用

  • 用空行在适当的地方划分代码块
  • 老是只用一个空行
  • 在关键词和花括号之间老是只用一个空格符
// Wrong if(foo){ } // Correct if (foo) { }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

指针的书写规范

  • 对于指针或引用,在类型名和或&之间用一个空格,可是在或&和变量名之间没有空格
char *x; const QString &myString; const char * const y = "hello";
  • 1
  • 2
  • 3

二元操做符

  • 二元操做符的左右都要有空格
  • 二元操做符对待它的两个参数是一样对待的,只是在该操做符是类外的操做符
  • 例如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
  • 1
  • 2
  • 3
  • 4
  • 5

逗号

  • 逗号左边没有空格,逗号右边有一个空格
#include <QApplication> #include <QMessageBox> int main(int argc, char *argv[]) { QT_REQUIRE_VERSION(argc, argv, "4.0.2") QApplication app(argc, argv); ... return app.exec(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

分号

  • 分号左边没有空格,分号做为语句的结束符,其右边通常再也不有内容
struct Point2D { int x; int y; };
  • 1
  • 2
  • 3
  • 4
  • 5

井号

  • #号右边没有空格
#include <QtGlobal> #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #include <QtWidgets> #else #include <QtGui> #endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

引号

  • 左引号的左边和右引号的右边都有一个空格,左引号的右边和右引号的左边都没有空格
  • 若是右引号右边是又括号的话,它们之间没有空格
qDebug() << Q_FUNC_INFO << "was called with value1:" << value1 << "value2:" << value2; QT_REQUIRE_VERSION(argc, argv, "4.0.2")
  • 1
  • 2
  • 3

cast

  • cast后无须空格
// Wrong char* blockOfMemory = (char* ) malloc(data.size()); // Correct char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size()));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 避免C语言的casts,尽可能用C++的casts (static_cast, const_cast, reinterpret_cast)。 reinterpret_cast 和 C风格的cast用起来都是危险的,但至少 reinterpret_cast 不会把const修饰符去掉。
  • 涉及到QObjects或重构本身的代码时,不要使用dynamic_cast,而是用qobject_cast,例如在引进一个类型的方法时。
  • 用构造函数去cast简单类型,例如:用int(myFloat)代替(int)myFloat

语句

  • 不要在一行写多条语句
  • 另起一行写控制流语句的定义
// Wrong if (foo) bar(); // Correct if (foo) bar();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

花括号写法

  • 使用紧贴括号:左括号和语句的开头在同一行,若是右括号是紧跟在一个关键词以后的,则右括号和该关键词在同一行
// Wrong if (codec) { } else { } // Correct if (codec) { } else { }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 例外:函数的实现和类的声明中,左括号老是在一行的开头
static void foo(int g) { qDebug("foo: %i", g); } class Moo { };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 当条件语句的执行部分多于一句的时候才使用花括号
// Wrong if (address.isEmpty()) { return false; } for (int i = 0; i < 10; +''i) { qDebug("%i", i); } // Correct if (address.isEmpty()) return false; for (int i = 0; i < 10;i) qDebug("%i", i);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

花括号用途

  • 例外1:若是父语句占有多行,或通过多层封装,子语句要用到花括号
// Correct if (address.isEmpty() || !isValid() || !codec) { return false; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 例外2:对称原则:在if-else语句块中,若是if或else中的一个包含了多行,另外一个为了对称性原则,也要用花括号
// Wrong if (address.isEmpty()) qDebug("empty!"); else { qDebug("%s", qPrintable(address)); it; } // Correct if (address.isEmpty()) { qDebug("empty!"); } else { qDebug("%s", qPrintable(address)); it; } // Wrong if (a) … else if (b) … // Correct if (a) { … } else { if (b) … }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 当条件语句的执行体是空语句的时候,用一个花括号
// Wrong while (a); // Correct while (a) {}
  • 1
  • 2
  • 3
  • 4
  • 5

圆括号

  • 圆括号用来给语句分组
 // Wrong if (a && b || c)  // Correct if ((a && b) || c)  // Wrong a + b & c  // Correct (a + b) & c
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Switch 语句

  • case标签和switch在同一列
  • 每个case语句的末尾都要有一个break语句或return语句,除非因功能须要故意不加或另一个case是紧跟上一个case的。
switch (myEnum) { case Value1: doSomething(); break; case Value2: case Value3: doSomethingElse(); // fall through default: defaultHandling(); break; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

跳转语句

  • 包括:break, continue, return, and goto
  • 不要在跳转关键词后边加else
// Wrong if (thisOrThat) return; else somethingElse(); // Correct if (thisOrThat) return; somethingElse();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 例外:若是这段代码是固有的对称结构,用else实现视觉上的对称也是能够的

换行

  • 每行代码很少于100个字符;如有必要,用括号括起来
  • 逗号在行尾。操做符在新行的开头位置,这是由于编辑器过窄的话,操做符在行尾容易看不见
  • 换行时尽可能避免行于行之间看起来良莠不齐
// Wrong if (longExpression + otherLongExpression + otherOtherLongExpression) { } // Correct if (longExpression + otherLongExpression + otherOtherLongExpression) { }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

通常例外与Artistic style选项

  • 通常例外:当严格遵照一条规范会让你的代码看起来很糟糕时,废弃这条规范
  • 用astyle格式化代码时的选项
--style=kr --indent=spaces=4 --align-pointer=name --align-reference=name --convert-tabs --attach-namespaces --max-code-length=100 --max-instatement-indent=120 --pad-header --pad-oper
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 例如,你能够这样用以上的代码
 int foo = some_really_long_function_name(and_another_one_to_drive_the_point_home( first_argument, second_argument, third_arugment));
  • 1
  • 2

C++特性

  • 不要使用异常处理
  • 不要使用运行时类型识别
  • 理智地使用模板,不要仅仅由于你会用就去用

Qt源码中的规范

  • 全部代码都是ascii,使用者若是不肯定的话,只多是7字节
  • 每个QObject的子类都必须有Q_OBJECT宏,即便这个类没用到信号或槽。不然qobject_cast将不能使用
  • 在connect语句中,使信号和槽的参数规范化(参看 QMetaObject::normalizedSignature),能够加快信号/槽的查找速度。可使用qtrepotools/util/normalize规范已有代码

包含头文件

  • 用#include
#include <qstring.h> // Qt class #include <new> // STL stuff #include <limits.h> // system stuff
  • 1
  • 2
  • 3
  • 4
  • 5
  • 若是你想包含qplatforms.h,把它做为第一个被包含的头文件
  • 若是你想包含私有头文件,要十分当心。使用如下的语法而不用管whatever_p.h属于哪一个模块在哪一个文件目录下
#include <private/whatever_p.h>
  • 1

编译器/平台特定问题

  • 使用三目运算符 ?时要特别当心,若是每次的返回值的类型可能不同的话,一些编译器会在运行时生成冲突的代码(此时编译器甚至不会报错)
QString s;
 return condition ? s : "nothing"; // crash at runtime - QString vs. const char *
  • 1
  • 2
  • 要特别当心对齐问题。不管什么时候,当一个指针被cast后的对齐数是增长的时候,它均可能会崩溃。例如一个const char 被cast成了cons int,当cast以后的数字不得不在2或4个字节之间对齐时,指针就会在机器上崩溃。
  • 使用一个union强迫编译器正确地对齐变量,示例以下,你能够肯定AlignHelper中全部的实例都和int边界对齐了
union AlignHelper { char c; int i; };
  • 1
  • 2
  • 3
  • 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 你能够按照下面的方法去作:
// 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 用Q_GLOBAL_STATIC定义全局实例
Q_GLOBAL_STATIC(QString, s)

 void foo() { s()->append("moo"); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 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
  • 1
  • 2
  • 3
  • 4
  • 避免64位的枚举值 
    • 嵌入式应用系统二进制接口将全部的枚举类型的值硬编码成32位int值
    • 微软的编译器不支持64位的枚举值

编程美学

  • 偏心用枚举值定义常量而非用const int或defines 
    • 枚举值会在编译时被编译器用实际值替换掉,于是运行时得出结果的速度更快
    • defines不是命名空间安全的(而且看起来很丑)
  • 偏心使用冗长而详细的参数名
  • 当从新实现一个虚方法时,不要在头文件中用virtual关键字,在Qt5中,用 Q_DECL_OVERRIDE宏在函数声明以后,分号以前注解它。

避免出现的事

  • 不要继承模版/工具类 
    • 其析构函数不是虚函数,会致使潜在的内存泄漏
    • 其符号不是导出的(not exported),会致使符号冲突
// 例如:A库有如下代码 class Q_EXPORT X: public QList<QVariant> {};
  • 1
  • 2
//B库有如下代码 class Q_EXPORT Y: public QList<QVariant> {};
  • 1
  • 2

这样,QList的符号就被导出了两次javascript

  • 不要把const-iterator和none-const iterator搞混。
for (Container::const_iterator it = c.begin(); it != c.end(); ++it) // W R O N G for (Container::const_iterator it = c.cbegin(); it != c.cend(); ++it) // Right
  • 1
  • 2
  • Q[Core]Application 是一个单例类。同一时间只能有一个实例在运行,可是这个实例能够被销毁,新的实例将能够被建立,以下的代码容易产生崩溃
static QObject *obj = 0; if (!obj) obj = new QObject(QCoreApplication::instance());
  • 1
  • 2
  • 3
  • 当QCoreApplication application被销毁时,obj成为了迷途指针(野指针),能够用 Q_GLOBAL_STATIC 和qAddPostRoutine清理
  • 为了尽量地支持静态关键词,避免使用匿名命名空间。编译单位内的一个静态名称能够保证它是一个内部链接。而一个位于匿名命名空间的名称,C++规定它是一个外部连接。

二进制和源兼容性

  • 定义 
    • Qt 4.0.0是一个主版本,Qt 4.1.0是一个微调版本,Qt 4.1.1是一个补丁版本。
    • 在此以后的版本:代码连接到以前版本的库能够运行
    • 在此以前的版本:代码连接到一个新版本的库只对旧版本的库能工做。
    • 源码兼容性:源码在不修改的状况下进行编译
  • 微调版本保持向后的二进制兼容性
  • 补丁版本保持向后和向前的二进制兼容性 
    • 不要增长或去掉任何公共API(例如公共的函数,公有/保护/私有的方法)
    • 不要从新实现方法(甚至不要修改内连方法,也不要修改保护/私有方法)
  • 当继承一个QWidget的子类时,老是要去重写event(),即便它是空的。这将使你的widget类能够被操做而不破坏其二进制兼容性
  • 全部从Qt中导出的方法,必须以q或Q开头。用autotest符号检测是否存在违反此规则的状况。(此为Qt自己要求的规范,咱们不须要严格执行。)

命名空间

  • 请记住,Qt中,除了Tests和WibKit,所有都是处在命名空间中的代码

float值

  • 没有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
  • 1
  • 2
  • 3
  • 4
  • 5

虚方法

  • 不要在子类中隐藏父类的虚方法:假设A类中有个virtual int val()方法,如下代码是不规范的。
class B: public A { using A::val; int val(int x); };
  • 1
  • 2
  • 3
  • 4
  • 5

宏定义

  • 在操做一个预处理器以前,先判断它是否被定义
#if Foo == 0 // W R O N G #if defined(Foo) && (Foo == 0) // Right #if Foo - 0 == 0 // Clever, are we? Use the one above instead, for better readability
  • 1
  • 2
  • 3

类的成员命名

  • 成员变量通常为名词
  • 函数成员通常为动词/动词+名词,可是当动词为get时,get经常省略。当返回值为Bool型变量时,函数名通常之前缀’is’开头
public: void setColor(const QColor& c); QColor color() const; void setDirty(bool b); bool isDirty() const; private Q_SLOTS: void slotParentChanged();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

定义私有类

//.h文件 class KFooPrivate; class KFoo { public: /* public members */ private: const QScopedPointer<KFooPrivate> d; };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
//.cpp文件
class KFooPrivate
{
    public:
        int someInteger;
};

KFoo::KFoo() : d(new KFooPrivate)
{
    /* ... */ } KFoo::~KFoo() { // You must define a non-inline destructor in the .cpp file, even if it is empty // else, a default one will be built in placed where KFooPrivate is only forward // declare, leading to error in the destructor of QScopedPointer }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

标记(flags)

  • 避免使用无心义的bool型参数,如下是糟糕的例子
static QString KApplication::makeStdCaption( const QString &caption, bool withAppName, bool modified);
  • 1
  • 2
  • 3
  • 解决方案是用QFlags。即便其中只有一个值,也建议这么作,这将容许你之后很方便地添加更多的值而且保持二进制兼容性。
  • 示例以下:
class KApplication
{
public: /* [...] */ enum StandardCaptionOption { /** * Indicates to include the application name */ WithApplicationName = 0x01, /** * Note in the caption that there is unsaved data */ Modified = 0x02 }; Q_DECLARE_FLAGS(StandardCaptionOptions, StandardCaptionOption) /** * Builds a caption using a standard layout. * * @param userCaption The caption string you want to display * @param options a set of flags from MakeStandartCaptionOption */ static QString makeStandardCaption(const QString& userCaption, const StandardCaptionOptions& options = WithApplicationName); /* [...] */ }; Q_DECLARE_OPERATORS_FOR_FLAGS(KApplication::StandardCaptionOptions)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

常引用

  • 每个对象,只要它不是基础类型(int, float, bool, enum, or pointers),都应该以常量引用的形式传递。这条使得代码运行得更快。即便一个对象是隐式共享的,也应该这么作
QString myMethod( const QString& foo, const QPixmap& bar, int number );
  • 1
  • 2
  • 3
  • 避免常引用的返回类型
const QList<int> &someProperty() const;
  • 1
  • 有种状况仍是可使用常引用的返回类型的,这种状况下,此处代码的运行性能相当重要,此处的代码实现也是固定的,思考再三以后,你能够写成这样:
QList<int> someProperty() const;
  • 1

库代码中的信号&槽

  • 在库代码中,用Q_SIGNALS 和 Q_SLOTS 代替 signals 和 slots。它们在语法上是相等的,用以免和boost信号的冲突。和python协同工做时使用”slots”

属性

  • 设置属性时用Q_PROPERTY。理由是属性能够被javascript 接口访问到
  • moc中设置特定的flag用 QMetaProperty.

构造函数

  • 为了使构造函数被错误使用的可能性降到最小,每个构造函数(除了拷贝构函数)都应该检查本身是否须要加上explicit 符号。

#include

  • 尽可能减小在头文件中包含其余头文件的数量
  • 以下所示,能够用前置声明法
#include <kfoobase.h> class KBar; class KFoo : public KFooBase { public: /* [...] */ void myMethod(const KBar &bar); };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 包含Qt自带头文件或外部库的头文件用尖括号
#include <iostream> #include <QDate> #include <zlib.h>
  • 1
  • 2
  • 3
  • 包含本身的项目头文件用双引号
#include "myclass.h"
  • 1
  • 包含Qt类的头文件不用包含它所在的模块名
//正确示例 #include <QDate> //correct
  • 1
  • 2
//错误示例 #include <QtCore/QDate> //incorrect, no need to specify the module QtCore #include <QtCore> //incorrect, do not include the top-level Qt module
  • 1
  • 2
  • 3
  • 假如你有一个Foo类,有Foo.h文件和Foo.cpp文件,在你的Foo.cpp文件中,要先包含Foo.h文件再包含其余头文件
  • 若是你的代码写成下面这样:
//.h文件 class Foo { public: Bar getBar(); };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
.cpp文件
#include "bar.h" #include "foo.h"
  • 1
  • 2
  • 3
  • 你的cpp文件可以正常编译,但当其余人用到Foo.h文件时,若是没有包含bar.h文件,编译器将不能进行编译。
  • 所以在cpp文件中首先包含其相应的.h文件,是为了.h文件可以被其余人使用
  • 头文件必须进行包含保护:避免屡次包含而引发屡次的编译
#ifndef MYFOO_H #define MYFOO_H ... <stuff>... #endif /* MYFOO_H */
  • 1
  • 2
  • 3
  • 4

信号&槽的标准化写法

  • 标准化的写法增长代码可读性
  • 不标准的写法多是以下写法
QObject::connect(this, SIGNAL( newValue(const QString&, const MyNamespace::Type&) ), other, SLOT( value(const QString &) ));
  • 1
  • 2
  • 3
  • 建议采用如下写法
QObject::connect(this, SIGNAL(newValue(QString,MyNamespace::Type)), other, SLOT(value(QString)));
  • 1
  • 2

API-最小化原则

  • 最小化的API意味着,每一个API中使用尽量少的类,每一个类中使用尽量少的公用成员(public members)。这样作的好处是使得API易于理解、记忆、调试和修改

API-完整性原则

  • 一个完整的API意味着实现它预期的功能。这和最小化的特性可能会产生冲突。另外若是一个成员函数出如今一个错误的类里,API的使用者们可能会找不到它

API-有明确和简单的语意

  • 正如其余的设计工做同样,你应当遵照“最小惊奇”原则。通常状况下,这是容易作到的。请不要用解决方案所解决的问题过于笼统。(例如Qt3中的QMimeSourceFactory,应该被叫作 QImageLoader从而做为一个不一样的API)

API-直观性原则

  • 不一样的经历和背景让人们对什么具备“直观性”什么不具备,有着不一样的感受。如下状况咱们能够认为一个API是直观的
  • 一个稍有一些经验的用户在不看帮助文档的状况下,可以正确地使用该API
  • 一个从不知道该API的用户可以看懂使用该API写成的代码

API-便于记住

  • 选择一个一致的和准确的命名约定
  • 使用可识别的模式和概念
  • 避免使用缩写

API-易读性原则

  • 代码是一次写成,但须要屡次阅读,易读的代码可能写的时候会花费稍长的时间,可是在整个产品的生命周期之中,但节省你不少阅读和理解的时间
  • 最后,记住不一样的用户会用到一个API的不一样部分。虽然直接使用Qt的类生成一个实例是直观的,但咱们仍是有理由期待用户在派生一个Qt的类以前先阅读它的官方文档

API-静态多态性

  • 类似的类应该有类似的API,这能够用继承的方式实现,这用到了运行时多态。
  • 可是多态也能够发生在设计类的时候,例如,你把一个对象的类型从QProgressBar 换成Qslider,或者从QString 换成QByteArray,你会发现它们之间的API是何其的类似,以致于你能够很简单地用一个去替换另外一个。这就是咱们所说的“静态多态性”
  • “静态多态性”使得记住这些API和使用编程模式都更为简单了。所以,为一系列相关类设计类似的API好过为每一个类设计独立的、更切合自身的API
  • 通常来讲,在Qt中咱们偏心使用“静态多态性”而非实际的继承,除非一些不可控制的缘由要求咱们不得不如此。

常引用

  • 若是一个类型超过16个字节,用常引用传递它。
  • 若是一个类型有一个非平凡拷贝构造函数(non-trivial copy-constructor),或一个非平凡析构函数(non-trivial destructor),用常引用传递它的值而避免使用以上方法
  • 全部的其余类型都应该直接传递其值
void setAge(int age); void setCategory(QChar cat); void setName(QLatin1String name); void setAlarm(const QSharedPointer<Alarm> &alarm); // const-ref is much faster than running copy-constructor and destructor // QDate, QTime, QPoint, QPointF, QSize, QSizeF, QRect are good examples of other classes you should pass by value.
  • 1
  • 2
  • 3
  • 4
  • 5

枚举类型和枚举值的命名

  • 如下的示例说明了枚举值命名时给出通常的名称的危险
namespace Qt
{
enum Corner { TopLeft, BottomRight, … }; enum CaseSensitivity { Insensitive, Sensitive }; … }; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
tabWidget->setCornerWidget(widget, Qt::TopLeft); str.indexOf("$(QTDIR)", Qt::Insensitive);
  • 1
  • 2
  • 在最后一行,Insensitive 是什么含义呢?这是不易于理解的。所以,枚举值命名时,至少重复枚举类型名中的一个字母
namespace Qt
{
enum Corner { TopLeftCorner, BottomRightCorner, … }; enum CaseSensitivity { CaseInsensitive, CaseSensitive }; … };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
tabWidget->setCornerWidget(widget, Qt::TopLeftCorner); str.indexOf("$(QTDIR)", Qt::CaseInsensitive);
  • 1
  • 2

企图少写代码的陷阱

  • 不要为了图方便少些一些代码。由于代码是一次书写,后期不止一次地要去理解。例如
QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, "volume");
  • 1
  • 改为下面的方式会更容易理解
QSlider *slider = new QSlider(Qt::Vertical); slider->setRange(12, 18); slider->setPageStep(3); slider->setValue(13); slider->setObjectName("volume");
  • 1
  • 2
  • 3
  • 4
  • 5

Bool型参数陷阱

  • 这方面经典的例子是Qt中的repaint()函数,它的bool型参数用来标记窗口的背景是否被擦除。用法以下:
widget->repaint(false);
  • 1
  • 上面的代码很容易被理解成“不从新绘制”
  • 上面代码的中用到的repaint()函数,其设计时的考虑无非是为了能够少定义一个函数,结果反而带来了误解,有多少人能够对如下三行代码所表明队含义进行准确地区分呢?
widget->repaint(); widget->repaint(true); widget->repaint(false);
  • 1
  • 2
  • 3
  • 稍微好一点的作法是这样
widget->repaint(); widget->repaintWithoutErasing();
  • 1
  • 2
  • 还有一个很明显的作法是尽量地用枚举类型代替bool型参数,请对比如下的两行代码
str.replace("USER", user, false); // Qt 3 str.replace("USER", user, Qt::CaseInsensitive); // Qt 4

 

https://blog.csdn.net/qq_35488967/article/details/70055490php

相关文章
相关标签/搜索