Qt UI 编码规范

变量声明
  • 每行只声明一个变量

  • 避免使用短的/无意义的命名

  • 当一个变量被用到时再声明

    // 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规范已有代码
包含头文件顺序
  1. 源文件对应的头文件
  2. <分隔>
  3. C系统文件
  4. <分隔>
  5. C++系统文件
  6. <分隔>
  7. Qt库文件
  8. <分隔>
  9. 其他目录

每组文件按字母升序排列

编译器/平台
  1. 使用三目运算符 ?时要特别小心,如果每次的返回值的类型可能不一样的话,一些编译器会在运行时生成冲突的代码(此时编译器甚至不会报错) QString s; return condition ? s : "nothing"; // crash at runtime - QString vs. const char *

  2. 要特别小心对齐问题。无论何时,当一个指针被cast后的对齐数是增加的时候,它都可能会崩溃。例如一个const char 被cast成了cons int,当cast之后的数字不得不在2或4个字节之间对齐时,指针就会在机器上崩溃

  3. 任何需要需要执行构造函数或相关代码进行初始化的实例,都不能用作库代码中的全局实例。因为当构造函数或代码将要运行的时候,该实例还没有被定义(在第一次调用该实例时,在加载库时,在执行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

  4. 用Q_GLOBAL_STATIC定义全局实例

     Q_GLOBAL_STATIC(QString, s)
    
    void foo()
    {
     s()->append("moo");
    }
    
  5. 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

  6. 避免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");
参考资料
  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
Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计