主頁 >  其他 > qt creator原始碼全方面分析(2-1)

qt creator原始碼全方面分析(2-1)

2020-09-12 23:15:20 其他

目錄
  • coding-style.html
    • 提交代碼
    • 二進制兼容性和源代碼兼容性
    • 代碼構造
    • 格式化
      • 利用識別符號
      • 空格
        • 指標和參考
        • Operator名稱和括號
        • 函式名稱和括號
        • 關鍵詞
        • 注釋
      • 大括號
      • 圓括號
      • 換行符
      • 宣告
        • 宣告變數
      • 命名空間
    • 模式與實踐
      • 命名空間
      • 傳遞檔案名
      • 插件擴展點
      • 使用全域物件池
      • C++特征
        • 空指標
      • C ++ 11和C ++ 14功能
        • Lambdas
        • auto關鍵字
        • 域化列舉
        • 委托建構式
        • 初始化串列
        • 用大括號初始化
        • 非靜態資料成員初始化
        • 默認函式和洗掉函式
        • 覆寫
        • Nullptr
        • 基于范圍的for回圈
      • 使用QObject
      • 檔案頭
      • 包含頭檔案
      • Casting
      • 編譯器和平臺特定的問題
      • 美學
      • 從模板或工具類繼承
      • 繼承與聚合
      • 公共頭檔案的約定
    • 類成員名稱
    • 檔案

coding-style.html

代碼規范很重要,這決定了編碼風格的統一,如果你要向qt貢獻代碼,那么你需要看和他們一致,當然,你也可以學習一下qt的經驗,為什么人家要采用這種風格,有什么好處!


編碼規則旨在指導Qt Creator開發人員,幫助他們撰寫可理解和可維護的代碼,并最大程度地減少混亂和意外,

提交代碼

略,

二進制兼容性和源代碼兼容性

以下串列描述了發行版的編號方式,并定義發行版之間的二進制兼容性和源代碼兼容性:

  • Qt Creator 3.0.0是主要版本,Qt Creator 3.1.0是次要版本,Qt Creator 3.1.3是補丁版本,

  • 后向二進制兼容性意味著,代碼鏈接到庫的舊版本仍然有效,

  • 前向二進制兼容性意味著,可與舊庫一起使用的代碼,可鏈接到庫的新版本,

  • 源代碼兼容性意味著代碼無需修改即可編譯,

當前,我們不保證主要版本和次要版本之間的二進制或源代碼兼容性,

但是,在同一次要版本中,我們嘗試為補丁版本保留后向二進制兼容性和后向源代碼兼容性

  • 軟API凍結:從次要版本的Beta發布之后不久,我們就開始在該次要版本中保持后向源代碼兼容性,包括其補丁版本,這意味著從那時起,使用Qt Creator API的代碼將針對此次要版本的所有后續版本的API進行編譯,包括其補丁版本,該規則偶爾可能會有例外,應該適當地加以傳達,

  • 硬API凍結:從次要版本的候選版本開始,我們在該次要版本保持后向源代碼兼容性,包括其補丁版本,我們還將開始保持后向二進制兼容性,但不會在插件的compatVersion設定中反映出來,因此,針對候選發行版撰寫的Qt Creator插件,實際上不會在最終版本或更高版本中加載,即使這些庫的二進制兼容性在理論上是允許的,請參閱下面有關插件規格的部分,

  • 硬ABI凍結:從次要版本的最終發行開始,我們保持該版本及其所有補丁版本的后向源代碼和二進制兼容性,

為了保持向后兼容性:

  • 請勿添加或洗掉任何公有API(例如,全域函式,公有/受保護/私有成員函式),

  • 不要重新實作功能(甚至是行內函式,受保護的或私有的函式),

  • 檢查二進制兼容性替代方法,以獲取保留二進制兼容性的方法,

有關二進制兼容性的更多資訊,請參見C++二進制兼容性問題,

從插件規范的角度來看,這意味著

  • 補丁版本中的Qt Creator插件將把次要版本作為compatVersion值, 例如,3.1.2版本的插件具有compatVersion ="3.1.0",

  • 次要版本的預發行(包括候選發行)中,把它們自己作為compatVersion值,這意味著針對最終發行版編譯的插件將不會在預發行版中加載,

  • Qt Creator插件開發人員可以決定他們的插件是否需要某個補丁版本(或者更高版本),或者通過在宣告對其他插件的依賴性時設定相應的version值,他們可以使用此次要版本的所有補丁版本,Qt專案提供的Qt Creator插件的默認設定是要求最新的補丁版本,

例如,Qt Creator 3.1 beta(內部版本號3.0.82)中的Find插件將具有

  <plugin name="Find" version="3.0.82" compatVersion="3.0.82">
    <dependencyList>
      <dependency name="Core" version="3.0.82"/>
      ....

Qt Creator 3.1.0 final中的Find插件將具有

  <plugin name="Find" version="3.1.0" compatVersion="3.1.0">
    <dependencyList>
      <dependency name="Core" version="3.1.0"/>
      ....

Qt Creator 3.1.1補丁版本中的Find插件將具有版本3.1.1,將向后二進制兼容Find插件版本3.1.0(compatVersion ="3.1.0"),并且將依賴向后二進制兼容的Core插件版本3.1.1:

  <plugin name="Find" version="3.1.1" compatVersion="3.1.0">
    <dependencyList>
      <dependency name="Core" version="3.1.1"/>
      ....

代碼構造

遵循代碼構造準則,以使代碼更快,更清晰, 此外,該準則還允許您利用C++中的強型別檢查,

  • 在可能的情況下,建議前綴遞增,而不是后綴遞增,前綴遞增可能更快,只要考慮一下前/后遞增的明顯實作即可, 此規則也適用于遞減:

      ++T;
      --U;
    
      -NOT-
    
      T++;
      U--;
    
  • 嘗試盡量減少對同一代碼的重復計算, 特別是回圈的時候:

      Container::iterator end = large.end();
      for (Container::iterator it = large.begin(); it != end; ++it) {
              ...;
      }
    
      -NOT-
    
      for (Container::iterator it = large.begin();
           it != large.end(); ++it) {
              ...;
      }
    
  • 您可以在非時間緊缺的代碼中,對Qt容器使用Qt foreach回圈, 這是降低多行冗余,并給回圈變數起適當名稱的一種好方法:

    foreach (QWidget *widget, container)
          doSomething(widget);
    
      -NOT-
    
      Container::iterator end = container.end();
      for (Container::iterator it = container.begin(); it != end; ++it)
          doSomething(*it);
    

    如果可能,將回圈變數設為const, 這可以防止不必要的共享資料分離:

      foreach (const QString &name, someListOfNames)
          doSomething(name);
    
      - NOT -
    
      foreach (QString name, someListOfNames)
          doSomething(name);
    

格式化

利用識別符號

在識別符號中使用駝峰大小寫,

充分利用識別符號中的第一個字符,如下所示:

  • 類名以大寫字母開頭,
  • 函式名稱以小寫字母開頭,
  • 變數名以小寫字母開頭,
  • 列舉名稱和值以大寫字母開頭, 無作用域限制的Enum值包含列舉型別的部分名稱,

空格

  • 使用四個空格來進行縮進,不要使用制表符Tab,
  • 使用空白行將合適的代碼組合在一起,
  • 始終僅使用一個空白行,

指標和參考

對于指標或參考,請始終在星號(*)或與號(&)之前使用單個空格,但不要在其后使用, 盡可能避免使用C語言型別轉換:

  char *blockOfMemory = (char *)malloc(data.size());
  char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size()));

  -NOT-

  char* blockOfMemory = (char* ) malloc(data.size());

當然,在這個特殊的例子中,可能使用new更合適,

Operator名稱和括號

operator名稱和括號之間請勿使用空格, 等式標記(==)是operator名稱的一部分,因此,空格使宣告看起來像一個運算式:

  operator==(type)

  -NOT-

  operator == (type)

函式名稱和括號

不要在函式名稱和括號之間使用空格:

  void mangle()

  -NOT-

  void mangle ()

關鍵詞

請始終在關鍵字和大括號之間使用一個空格:

  if (foo) {
  }

  -NOT-

  if(foo){
  }

注釋

通常,在“ //”之后放置一個空格, 要對齊多行注釋中的文本,可以插入多個空格,

大括號

作為基本規則,將左大括號與陳述句開頭放在同一行:

  if (codec) {
  }

  -NOT-

  if (codec)
  {
  }

例外:函式實作和類宣告的左括號始終在行首處:

  static void foo(int g)
  {
      qDebug("foo: %i", g);
  }

  class Moo
  {
  };

當條件陳述句的主體包含多行時,以及單行陳述句有些復雜時,請使用大括號, 否則,請省略它們:

  if (address.isEmpty())
      return false;

  for (int i = 0; i < 10; ++i)
      qDebug("%i", i);

  -NOT-

  if (address.isEmpty()) {
      return false;
  }

  for (int i = 0; i < 10; ++i) {
      qDebug("%i", i);
  }

例外1:如果括號中的陳述句包含多行或進行了換行,也請使用大括號:

  if (address.isEmpty()
          || !isValid()
          || !codec) {
      return false;
  }

注意:這可以重寫為:

  if (address.isEmpty())
      return false;

  if (!isValid())
      return false;

  if (!codec)
      return false;

例外2:在if-then-else塊中,如果if-code或else-code覆寫多行,也請使用大括號:

  if (address.isEmpty()) {
      --it;
  } else {
      qDebug("%s", qPrintable(address));
      ++it;
  }

  -NOT-

  if (address.isEmpty())
      --it;
  else {
      qDebug("%s", qPrintable(address));
      ++it;
  }
  if (a) {
      if (b)
          ...
      else
          ...
  }

  -NOT-

  if (a)
      if (b)
          ...
      else
          ...

當條件陳述句的主體為空時,請使用大括號:

  while (a) {}

  -NOT-

  while (a);

圓括號

使用括號將運算式分組:

  if ((a && b) || c)

  -NOT-

  if (a && b || c)
  (a + b) & c

  -NOT-

  a + b & c

換行符

  • 行少于100個字符,
  • 如有必要,請插入換行符,
  • 逗號放在行的結尾,
  • 運算子放在新行的開頭,
if (longExpression
      || otherLongExpression
      || otherOtherLongExpression) {
  }

  -NOT-

  if (longExpression ||
      otherLongExpression ||
      otherOtherLongExpression) {
  }

宣告

  • 對類的訪問部分使用以下順序:公有,受保護,私有, 公有部分對類的每個用戶都很有趣, 私有部分僅對該類的實作者有用,
  • 在類的宣告檔案中,避免宣告全域物件, 如果所有物件都使用相同的變數,請使用靜態成員,
  • 使用class而不是struct,一些編譯器會在符號名稱中混入差異,并在struct宣告后跟class宣告時發出警告, 為了避免持續更改來消除警告,我們將class宣告作為首選方式,

宣告變數

  • 避免使用型別別的全域變數,來排除初始化順序的問題, 如果無法避免,請考慮使用Q_GLOBAL_STATIC,

  • 宣告全域字串文本

    const char aString[] = "Hello";
    
  • 盡可能避免使用短名稱(例如a,rbarr,nughdeget),僅將單字符變數名稱用于計數器和臨時變數,其用途是顯而易見,

  • 每個變數宣告在單獨的行中:

      QString a = "Joe";
      QString b = "Foo";
    
      -NOT-
    
      QString a = "Joe", b = "Foo";
    

    注意:QString a = "Joe"首先創建了由字串文字構造的臨時副本,然后呼叫拷貝建構式, 因此,它可能比通過QString a("Joe")直接構造更昂貴,但是,編譯器可以洗掉副本(即使這樣做有副作用),現代編譯器通常會這樣做, 考慮到這些相等的代價,Qt Creator代碼還是傾向于使用'='習慣用法,因為它符合傳統的C樣式初始化,它不會被誤認為是函式宣告,并且它減少了更多初始化中的嵌套括號的級別,

  • 避免縮寫:

      int height;
      int width;
      char *nameOfThis;
      char *nameOfThat;
    
      -NOT-
    
      int a, b;
      char *c, *d;
    
  • 當變數被需要時,才進行宣告,宣告時完成初始化,這一點尤其重要,

命名空間

  • 將左大括號與namespace關鍵字放在同一行,

  • 不要縮進內部的宣告或定義,

  • 可選,但如果名稱空間跨越多行,則建議使用:在右大括號后添加注釋,重復該名稱空間,

      namespace MyPlugin {
    
      void someFunction() { ... }
    
          }  // namespace MyPlugin
    
  • 作為例外,如果名稱空間中只有一個類宣告,則可以都放在一行上:

    namespace MyPlugin { class MyClass; }
    
  • 不要在頭檔案中使用using指令,

  • 定義類和函式時不要依賴using指令,而應在適當的命名宣告區域中對其進行定義,

  • 訪問全域函式時,請勿依賴using指令,

  • 在其他情況下,建議您使用using指令,因為它們可幫助您避免混亂的代碼, 最好將所有的using指令放在檔案頂部附近,#include之后,

      [in foo.cpp]
      ...
      #include "foos.h"
      ...
      #include <utils/filename.h>
      ...
      using namespace Utils;
    
      namespace Foo {
      namespace Internal {
    
      void SomeThing::bar()
      {
          FileName f;              // or Utils::FileName f
          ...
      }
      ...
      } // namespace Internal      // or only // Internal
      } // namespace Foo           // or only // Foo
    
      -NOT-
    
      [in foo.h]
      ...
      using namespace Utils;       // Wrong: no using-directives in headers
    
      class SomeThing
      {
          ...
      };
    
      -NOT-
    
      [in foo.cpp]
      ...
      using namespace Utils;
    
      #include "bar.h"             // Wrong: #include after using-directive
    
      -NOT-
    
      [in foo.cpp]
      ...
      using namepace Foo;
    
      void SomeThing::bar()        // Wrong if Something is in namespace Foo
      {
          ...
      }
    

模式與實踐

命名空間

閱讀Qt In Namespace,并記住所有Qt Creator都是名稱空間感知的代碼,

Qt Creator中的命名空間策略如下:

  • 供其他庫或插件使用的,被匯出的庫或插件中的類/符號,位于庫/插件指定的命名空間中,例如MyPlugin,
  • 未被匯出的庫或插件的類/符號,位于庫/插件的附加的內部名稱空間中,例如MyPlugin::Internal,

傳遞檔案名

Qt Creator API需要使用可移植格式的檔案名,即,在Windows上也要使用斜杠(/)而不是反斜杠(\),傳遞用戶輸入的檔案名給API,請先使用QDir::fromNativeSeparators對其進行轉換,要向用戶顯示檔案名,請使用QDir::toNativeSeparators將其轉換回本地格式,對于這些任務,請考慮使用Utils::FileName::fromUserInput(QString)和Utils::FileName::toUserOutput(),

比較檔案名時請使用Utils::FileName,因為它是大小寫敏感的,還要確保檔案夾使用的路徑,是沒有使用相對路徑的(QDir::cleanPath()),

插件擴展點

插件擴展點是一個插件提供的介面,供其他人實作,然后,插件將檢索介面的所有實作,并使用這些實作,也就是說,它們擴展了插件的功能,通常,介面的實作會在插件初始化期間放入全域物件池中,在插件初始化結束時,插件即可從物件池中檢索到它們,

例如,Find插件為其他插件提供了FindFilter介面,在FindFilter界面中,可以添加其他搜索范圍,并會顯示在高級搜索對話框中, Find插件從全域物件池中檢索所有FindFilter實作,并將其顯示在對話框中,該插件將實際搜索請求轉發到正確的FindFilter實作,然后執行搜索,

使用全域物件池

通過ExtensionSystem::PluginManager::addObject(),您可以將物件添加到全域物件池,并通過ExtensionSystem::PluginManager::getObjects(),再次獲取特定型別的物件,這應該主要用于插件擴展點的實作,

注意:請勿將單例放入物件池中,也不要從中檢索它,請改用單例模式,

C++特征

  • 最好是#pragma once,而不是檔案保護符,

  • 除非您知道自己要做什么,否則不要使用例外,

  • 除非您知道要做什么,否則不要使用RTTI(運行時型別資訊;即typeinfo結構,dynamic_cast或typeid運算子,包括拋出例外),

  • 除非您知道自己要做什么,否則不要使用虛擬繼承,

  • 明智地使用模板,不要只因為你會模板,

    提示:使用編譯期自動測驗,可查看C++功能是否被測驗場中的所有編譯器支持,

  • 所有代碼都是ASCII(僅僅7bit字符,如果不確定,請運行man ascii)

    • 理由:我們內部的語言環境太多,UTF-8和Latin1系統的組合不健康,通常,數值大于127的字符,在您最喜歡的編輯器中進行單機保存的不知情情況下,就可能已被破壞,

    • 對于字串:使用\nnn(其中nnn是對你字串所在的任何語言環境中的八進制表示)或\xnn(其中nn是十六進制),例如:QString s = QString::fromUtf8("\213\005")

    • 對于檔案中的變音符號或其他非ASCII字符,請使用qdoc \unicode命令或使用相關的宏,例如:\uuml表示ü,

  • 盡可能使用靜態關鍵字代替匿名命名空間,區域化到編譯單元的名稱,使用static可具有內部鏈接性,對于在匿名命名空間中宣告的名稱,不幸的是C++標準要求外部鏈接性(ISO/IEC 14882, 7.1.1/6, 或在gcc郵件串列上查看有關此內容的各種討論),

空指標

為空指標常量使用平凡零(0)始終是正確的,并且鍵入最少,

  void *p = 0;

  -NOT-

  void *p = NULL;

  -NOT-

  void *p = '\0';

  -NOT-

  void *p = 42 - 7 * 6;

注意:作為例外,匯入的第三方代碼以及與本機API介面的代碼(src/support/os_*)可以使用NULL,

C ++ 11和C ++ 14功能

代碼應與Microsoft Visual Studio 2013,g++ 4.7和Clang 3.1一起編譯,

Lambdas

使用lambda時,請注意以下幾點:

  • 您不必顯式指定回傳型別, 如果您沒有使用前面提到的編譯器,請注意這是C++ 14功能,您可能需要在編譯器中啟用C++ 14支持,

      []() {
          Foo *foo = activeFoo();
          return foo ? foo->displayName() : QString();
      });
    

如果您在類函式實作中使用了lambda,并呼叫了所在類的靜態函式,則必須明確捕獲this, 否則,它將無法使用g++ 4.7及更早版本進行編譯,

  void Foo::something()
  {
      ...
      [this]() { Foo::someStaticFunction(); }
      ...
  }

  -NOT-

  void Foo::something()
  {
      ...
      []() { Foo::someStaticFunction(); }
      ...
  }

根據以下規則格式化lambda:

  • 把捕獲串列,引數串列,回傳型別和左括號放在第一行,在接下來的幾行中縮進主體,在新的一行上關閉右括號,

      []() -> bool {
          something();
          return isSomethingElse();
      }
    
      -NOT-
    
      []() -> bool { something();
      somethingElse(); }
    
  • 將函式呼叫的右圓括號和分號與lambda的右大括號放在同一行,

  foo([]() {
      something();
  });
  • 如果在'if'陳述句中使用lambda,請在新行上啟動lambda,以避免在lambda的左大括號和'if'陳述句的左大括號之間造成混淆,

      if (anyOf(fooList,
              [](Foo foo) {
                  return foo.isGreat();
              }) {
          return;
      }
    
      -NOT-
    
      if (anyOf(fooList, [](Foo foo) {
                  return foo.isGreat();
              }) {
          return;
      }
    
  • 可選,如果合適,將lambda完整地放在同一行,

      foo([] { return true; });
    
      if (foo([] { return true; })) {
          ...
      }
    

auto關鍵字

可選,在以下情況下,可以使用auto關鍵字, 如有疑問,例如,如果使用auto會使代碼的可讀性降低,請不要使用auto, 請記住,代碼的讀取次數比撰寫的次數要多,

  • 避免在同一條陳述句中重復某個型別,

      auto something = new MyCustomType;
      auto keyEvent = static_cast<QKeyEvent *>(event);
      auto myList = QStringList({ "FooThing",  "BarThing" });
    
  • 分配迭代器型別時

      auto it = myList.const_iterator();
    

域化列舉

不需要將非域化列舉隱式轉換為int,或附加域有用時,使用域化列舉,

委托建構式

如果多個建構式使用基本上相同的代碼,請使用委托建構式,

初始化串列

使用初始化串列來初始化容器,例如:

  const QVector<int> values = {1, 2, 3, 4, 5};

用大括號初始化

如果對大括號使用初始化,請遵循與圓括號相同的規則, 例如:

  class Values // the following code is quite useful for test fixtures
  {
      float floatValue = https://www.cnblogs.com/codeForFamily/p/4; // prefer that for simple types
      QVector values = {1, 2, 3, 4, integerValue}; // prefer that syntax for initializer lists
      SomeValues someValues{"One", 2, 3.4}; // not an initializer_list
      SomeValues &someValuesReference = someValues;
      ComplexType complexType{values, otherValues} // constructor call
  }

  object.setEntry({"SectionA", value, doubleValue}); // calls a constructor
  object.setEntry({}); // calls default constructor

但是請注意不要過度使用它,以免混淆您的代碼,

非靜態資料成員初始化

使用非靜態資料成員初始化進行平凡(trivial)的初始化,但在公共匯出類中除外,

默認函式和洗掉函式

考慮使用=default和=delete來控制特殊函式,

覆寫

覆寫虛函式時,建議使用override關鍵字,不要在已覆寫函式上使用virtual,

確保一個類對所有被覆寫的函式始終使用override,以區分沒有被覆寫的,

Nullptr

所有編譯器都支持nullptr,但在使用方面尚未達成共識, 如有疑問,請詢問模塊的維護者是否更喜歡使用nullptr,

基于范圍的for回圈

您可以使用基于范圍的for回圈,但要注意虛假分離問題, 如果for回圈僅讀取容器,且容器是const的還是不共享的,不明顯,請使用std::cref()來確保不會不必要地分離容器,

使用QObject

  • 請記住,將Q_OBJECT宏添加到依賴于元物件系統的QObject子類中, 與元物件系統相關的功能包括信號和槽的定義,qobject_cast <>的使用等, 另請參閱Casting,
  • 優先使用Qt5樣式的connect()呼叫,而不是Qt4樣式的,
  • 使用Qt4樣式的connect()呼叫時,請在connect陳述句中標準化信號和槽的引數,以使更加快速安全地查找信號和槽, 您可以使用$QTDIR/util/normalize來標準化現有代碼, 有關更多資訊,請參見QMetaObject::normalizedSignature,

檔案頭

如果創建一個新檔案,則檔案頂部應包含頭注釋,保持與Qt Creator其他源檔案中的一樣,

包含頭檔案

  • 使用以下格式來包含Qt標頭:#include

  • 排列順序應從特定到通用,以確保頭檔案是齊全的, 例如:

    • #include "myclass.h"
    • #include "otherclassinplugin.h"
    • #include <otherplugin/someclass.h>
    • #include <QtClass>
    • #include <stdthing>
    • #include <system.h>
  • 將其他插件的頭檔案括在方括號(<>)而不是引號("")中,以便更輕松地發現源代碼中的外部依賴項,

  • 在同樣性質頭檔案的長塊之間添加空行,并嘗試按字母順序在塊內排列頭檔案,

Casting

  • 避免使用C強制轉換,最好使用C++強制轉換(static_cast,const_cast,reinterpret_cast)reinterpret_cast和C型別強制轉換都是危險的,但是至少reinterpret_cast不會洗掉const修飾符,
  • 除非您知道要做什么,否則不要使用dynamic_cast,對QObject使用qobject_cast或重構設計,例如引入type()函式(請參閱QListWidgetItem),

編譯器和平臺特定的問題

  • 使用問號運算子時要格外小心, 如果回傳的型別不相同,則某些編譯器會生成的代碼會在運行時崩潰(您甚至不會收到編譯器警告):

      QString s;
      // crash at runtime - QString vs. const char *
      return condition ? s : "nothing";
    
  • 對齊時要格外小心,

    每當指標轉換時,目標所需對齊位元組數增加,在某些體系結構上,生成的代碼可能會在運行時崩潰, 例如,如果將const char *強制轉換為const int *,它將在整數兩位元組對齊或四個位元組對齊的機器上崩潰,

    使用union來強制編譯器正確對齊變數, 在下面的示例中,可以確保AlignHelper的所有實體int邊界對齊:

      union AlignHelper
      {
          char c;
          int i;
      };
    
  • 對于頭檔案中的靜態宣告,請堅持使用整數型別,整數型別陣列及其結構, 例如,static float i[SIZE_CONSTANT]; 在大多數情況下,不會在每個插件中進行優化和拷貝,最好避免使用,

  • 任何具有建構式或需要運行代碼進行初始化的物件,都不能作為庫代碼的全域物件,因為該建構式或代碼何時運行未定義(首次使用時,庫加載時,main()之前或根本不進行),

    即使為共享庫定義了初始化程式的執行時間,移動該代碼到插件中或庫是靜態編譯時,你會遇到麻煩:

      // global scope
    
      -NOT-
    
      // Default constructor needs to be run to initialize x:
      static const QString x;
    
      -NOT-
    
      // Constructor that takes a const char * has to be run:
      static const QString y = "Hello";
    
      -NOT-
    
      QString z;
    
      -NOT-
    
      // Call time of foo() undefined, might not be called at all:
      static const int i = foo();
    

    你應該這樣做:

      // global scope
      // No constructor must be run, x set at compile time:
      static const char x[] = "someText";
    
      // y will be set at compile time:
      static int y = 7;
    
      // Will be initialized statically, no code being run.
      static MyStruct s = {1, 2, 3};
    
      // Pointers to objects are OK, no code needed to be run to
      // initialize ptr:
      static QString *ptr = 0;
    
      // Use Q_GLOBAL_STATIC to create static global objects instead:
    
      Q_GLOBAL_STATIC(QString, s)
    
      void foo()
      {
          s()->append("moo");
      }
    

    注意:函式范圍內的靜態物件沒有問題,建構式將在第一次函式進入時運行,但是,該代碼不是可重入的,

  • char是有符號的還是無符號的,取決于體系結構, 如果您明確需要有符號或無符號的char,請使用有符號char或uchar, 例如,以下代碼將在PowerPC上中斷:

      // Condition is always true on platforms where the
      // default is unsigned:
      if (c >= 0) {
          ...
      }
    
  • 避免使用64位列舉值, 嵌入式AAPCS(ARM體系結構的程序呼叫標準)的ABI將所有列舉值都硬編碼為32位整數,

  • 不要混合使用const和非const迭代器, 這將在被破壞的編譯器上悄無聲息地崩潰,

  for (Container::const_iterator it = c.constBegin(); it != c.constEnd(); ++it)

  -NOT-

  for (Container::const_iterator it = c.begin(); it != c.end(); ++it)
  • 不要在匯出的類中行內虛解構式, 這會導致依賴插件中的vtable表重復,這也可能破壞RTTI,參見QTBUG-45582.

美學

  • 優先選擇域化列舉來定義const常量,而不是static const int變數或define宏定義,列舉值將在編譯時由編譯器替換,從而使代碼更快, define宏定義不是命名空間安全的,
  • Prefer verbose argument names in headers. Qt Creator will show the argument names in their completion box. It will look better in the documentation.(???)

從模板或工具類繼承

從模板或工具類繼承有以下潛在陷阱:

  • 解構式不是虛函式,這可能導致記憶體泄漏,

  • 這些符號不會匯出(并且大部分是行內的),這可能導致符號沖突,

例如,庫A具有類Q_EXPORT X: public QList

繼承與聚合

  • 如果存在明確的is-a關系,請使用繼承,
  • 使用聚合來重復使用正交構建基礎類,
  • 如果可以選擇,則優先選擇聚合而不是繼承,

公共頭檔案的約定

我們的公共頭檔案必須在某些用戶的嚴格設定下仍然有效, 所有已安裝的頭檔案都必須遵循以下規則:

  • 沒有C型別強制轉換(-Wold-style-cast),請使用static_cast,const_cast或reinterpret_cast,對于基本型別,使用建構式形式:int(a)代替(int)a, 有關更多資訊,請參見Casting,

  • 沒有浮點數比較(-Wfloat-equal),使用qFuzzyCompare來比較值與差值,使用qIsNull來檢查浮點數是否為二進制0,而不是將其與0.0進行比較,或者,最好將此類代碼移至實作檔案中,

  • 不要在子類中隱藏虛函式({-Woverloaded-virtual}),如果基類A具有虛函式int val(),子類B具有相同名稱的多載,int val(int x),則類A的val函式將被隱藏,使用using關鍵字使其再次可見,并為被破壞的編譯器添加以下愚蠢的解決方法:

      class B: public A
      {
      #ifdef Q_NO_USING_KEYWORD
      inline int val() { return A::val(); }
      #else
      using A::val;
      #endif
      };
    
  • 不要同名區域變數遮蔽其他外部變數(-Wshadow),

  • 如果可能的話,避免this->x = x;,

  • 變數與在類中宣告的函式不要同名,

  • 為了提高代碼的可讀性,始終先檢查預處理變數是否已定義,后獲取變數值(-Wundef),

      #if defined(Foo) && Foo == 0
    
        -NOT-
    
      #if Foo == 0
    
      -NOT-
    
      #if Foo - 0 == 0
    
  • 使用#defined運算子檢查預處理變數時,請始終把變數放在括號中,

      #if defined(Foo)
    
      -NOT-
    
      #if defined Foo
    

類成員名稱

我們使用"m_"前綴約定,但公有結構化成員除外(通常在*Private類中,并且真正的公有結構化成員很少見),dq指標不受"m_"規則的限制,

d指標("Pimpls")被命名為“ d”,而不是"m_d",Foo類中d指標的型別為FooPrivate *,其中FooPrivate所在命名空間與Foo相同,或者,如果Foo被匯出,則在相應的{Internal}命名空間,

如果需要(例如,當私有物件需要發出對應類的信號時),FooPrivate可以成為Foo的友元,

如果私有類需要反向參考其對應的類,則將指標命名為q,其型別為Foo *,(與Qt中的約定相同:"q"看起來像倒置的"d",)

不要使用智能指標來守衛d指標,因為它會增加編譯和鏈接時間開銷,并創建帶有更多符號的臃腫物件代碼,這會減慢除錯器的啟動速度:

  ############### bar.h

  #include <QScopedPointer>
  //#include <memory>

  struct BarPrivate;

  struct Bar
  {
      Bar();
      ~Bar();
      int value() const;

      QScopedPointer<BarPrivate> d;
      //std::unique_ptr<BarPrivate> d;
  };

  ############### bar.cpp

  #include "bar.h"

  struct BarPrivate { BarPrivate() : i(23) {} int i; };

  Bar::Bar() : d(new BarPrivate) {}

  Bar::~Bar() {}

  int Bar::value() const { return d->i; }

  ############### baruser.cpp

  #include "bar.h"

  int barUser() { Bar b; return b.value(); }

  ############### baz.h

  struct BazPrivate;

  struct Baz
  {
      Baz();
      ~Baz();
      int value() const;

      BazPrivate *d;
  };

  ############### baz.cpp

  #include "baz.h"

  struct BazPrivate { BazPrivate() : i(23) {} int i; };

  Baz::Baz() : d(new BazPrivate) {}

  Baz::~Baz() { delete d; }

  int Baz::value() const { return d->i; }

  ############### bazuser.cpp

  #include "baz.h"

  int bazUser() { Baz b; return b.value(); }

  ############### main.cpp

  int barUser();
  int bazUser();

  int main() { return barUser() + bazUser(); }

結果:

  Object file size:

   14428 bar.o
    4744 baz.o

    8508 baruser.o
    2952 bazuser.o

  Symbols in bar.o:

      00000000 W _ZN3Foo10BarPrivateC1Ev
      00000036 T _ZN3Foo3BarC1Ev
      00000000 T _ZN3Foo3BarC2Ev
      00000080 T _ZN3Foo3BarD1Ev
      0000006c T _ZN3Foo3BarD2Ev
      00000000 W _ZN14QScopedPointerIN3Foo10BarPrivateENS_21QScopedPointerDeleterIS2_EEEC1EPS2_
      00000000 W _ZN14QScopedPointerIN3Foo10BarPrivateENS_21QScopedPointerDeleterIS2_EEED1Ev
      00000000 W _ZN21QScopedPointerDeleterIN3Foo10BarPrivateEE7cleanupEPS2_
      00000000 W _ZN7qt_noopEv
               U _ZN9qt_assertEPKcS1_i
      00000094 T _ZNK3Foo3Bar5valueEv
      00000000 W _ZNK14QScopedPointerIN3Foo10BarPrivateENS_21QScopedPointerDeleterIS2_EEEptEv
               U _ZdlPv
               U _Znwj
               U __gxx_personality_v0

  Symbols in baz.o:

      00000000 W _ZN3Foo10BazPrivateC1Ev
      0000002c T _ZN3Foo3BazC1Ev
      00000000 T _ZN3Foo3BazC2Ev
      0000006e T _ZN3Foo3BazD1Ev
      00000058 T _ZN3Foo3BazD2Ev
      00000084 T _ZNK3Foo3Baz5valueEv
               U _ZdlPv
               U _Znwj
               U __gxx_personality_v0

檔案

檔案是從源檔案和頭檔案生成的, 您撰寫檔案是為了其他開發人員,而不是自己, 在頭檔案中,撰寫介面檔案, 也就是說,函式是做什么的,而不是怎么實作的,

在.cpp檔案中,如果函式實作不明顯,則可以撰寫相關的檔案,


原創造福大家,共享改變世界

獻出一片愛心,溫暖作者心靈


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/17787.html

標籤:其他

上一篇:qt creator原始碼全方面分析(2)

下一篇:qt creator原始碼全方面分析(2-1-1)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more