目錄
coding-style.html
送出代碼
二進制相容性和源代碼相容性
代碼構造
格式化
利用辨別符
空格
大括号
圓括号
換行符
聲明
命名空間
模式與實踐
傳遞檔案名
插件擴充點
使用全局對象池
C++特征
C ++ 11和C ++ 14功能
使用QObject
檔案頭
包含頭檔案
Casting
編譯器和平台特定的問題
美學
從模闆或工具類繼承
繼承與聚合
公共頭檔案的約定
類成員名稱
文檔
代碼規範很重要,這決定了編碼風格的統一。如果你要向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插件将具有
Qt Creator 3.1.0 final中的Find插件将具有
Qt Creator 3.1.1更新檔版本中的Find插件将具有版本3.1.1,将向後二進制相容Find插件版本3.1.0(compatVersion ="3.1.0"),并且将依賴向後二進制相容的Core插件版本3.1.1:
遵循代碼構造準則,以使代碼更快,更清晰。 此外,該準則還允許您利用C++中的強類型檢查。
在可能的情況下,建議字首遞增,而不是字尾遞增。字首遞增可能更快。隻要考慮一下前/後遞增的明顯實作即可。 此規則也适用于遞減:
嘗試盡量減少對同一代碼的重複計算。 特别是循環的時候:
您可以在非時間緊缺的代碼中,對Qt容器使用Qt foreach循環。 這是降低多行備援,并給循環變量起适當名稱的一種好方法:
如果可能,将循環變量設為const。 這可以防止不必要的共享資料分離:
在辨別符中使用駝峰大小寫。
充分利用辨別符中的第一個字元,如下所示:
類名以大寫字母開頭。
函數名稱以小寫字母開頭。
變量名以小寫字母開頭。
枚舉名稱和值以大寫字母開頭。 無作用域限制的Enum值包含枚舉類型的部分名稱。
使用四個空格來進行縮進,不要使用制表符Tab。
使用空白行将合适的代碼組合在一起。
始終僅使用一個空白行。
對于指針或引用,請始終在星号(*)或與号(&)之前使用單個空格,但不要在其後使用。 盡可能避免使用C語言類型轉換:
當然,在這個特殊的例子中,可能使用new更合适。
operator名稱和括号之間請勿使用空格。 等式标記(==)是operator名稱的一部分,是以,空格使聲明看起來像一個表達式:
不要在函數名稱和括号之間使用空格:
請始終在關鍵字和大括号之間使用一個空格:
通常,在“ //”之後放置一個空格。 要對齊多行注釋中的文本,可以插入多個空格。
作為基本規則,将左大括号與語句開頭放在同一行:
例外:函數實作和類聲明的左括号始終在行首處:
當條件語句的主體包含多行時,以及單行語句有些複雜時,請使用大括号。 否則,請省略它們:
例外1:如果括号中的語句包含多行或進行了換行,也請使用大括号:
注意:這可以重寫為:
例外2:在if-then-else塊中,如果if-code或else-code覆寫多行,也請使用大括号:
當條件語句的主體為空時,請使用大括号:
使用括号将表達式分組:
行少于100個字元。
如有必要,請插入換行符。
逗号放在行的結尾。
操作符放在新行的開頭。
對類的通路部分使用以下順序:公有,受保護,私有。 公有部分對類的每個使用者都很有趣。 私有部分僅對該類的實作者有用。
在類的聲明檔案中,避免聲明全局對象。 如果所有對象都使用相同的變量,請使用靜态成員。
使用<code>class</code>而不是<code>struct</code>。一些編譯器會在符号名稱中混入差異,并在struct聲明後跟class聲明時發出警告。 為了避免持續更改來消除警告,我們将<code>class</code>聲明作為首選方式。
避免使用類類型的全局變量,來排除初始化順序的問題。 如果無法避免,請考慮使用Q_GLOBAL_STATIC。
聲明全局字元串文本
盡可能避免使用短名稱(例如a,rbarr,nughdeget)。僅将單字元變量名稱用于計數器和臨時變量,其用途是顯而易見。
每個變量聲明在單獨的行中:
注意:QString a = "Joe"首先建立了由字元串文字構造的臨時副本,然後調用拷貝構造函數。 是以,它可能比通過QString a("Joe")直接構造更昂貴。但是,編譯器可以删除副本(即使這樣做有副作用),現代編譯器通常會這樣做。 考慮到這些相等的代價,Qt Creator代碼還是傾向于使用'='習慣用法,因為它符合傳統的C樣式初始化,它不會被誤認為是函數聲明,并且它減少了更多初始化中的嵌套括号的級别。
避免縮寫:
當變量被需要時,才進行聲明。聲明時完成初始化,這一點尤其重要。
将左大括号與namespace關鍵字放在同一行。
不要縮進内部的聲明或定義。
可選,但如果名稱空間跨越多行,則建議使用:在右大括号後添加注釋,重複該名稱空間。
作為例外,如果名稱空間中隻有一個類聲明,則可以都放在一行上:
不要在頭檔案中使用using指令。
定義類和函數時不要依賴using指令,而應在适當的命名聲明區域中對其進行定義。
通路全局函數時,請勿依賴using指令。
在其他情況下,建議您使用using指令,因為它們可幫助您避免混亂的代碼。 最好将所有的using指令放在檔案頂部附近,#include之後。
閱讀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(),再次擷取特定類型的對象。這應該主要用于插件擴充點的實作。
注意:請勿将單例放入對象池中,也不要從中檢索它。請改用單例模式。
最好是#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 <code>\unicode</code>指令或使用相關的宏。例如:\uuml表示ü。
盡可能使用靜态關鍵字代替匿名命名空間。局部化到編譯單元的名稱,使用static可具有内部連結性。對于在匿名命名空間中聲明的名稱,不幸的是C++标準要求外部連結性(ISO/IEC 14882, 7.1.1/6, 或在gcc郵件清單上檢視有關此内容的各種讨論)。
為空指針常量使用平凡零(0)始終是正确的,并且鍵入最少。
注意:作為例外,導入的第三方代碼以及與本機API接口的代碼(src/support/os_*)可以使用NULL。
代碼應與Microsoft Visual Studio 2013,g++ 4.7和Clang 3.1一起編譯。
使用lambda時,請注意以下幾點:
您不必顯式指定傳回類型。 如果您沒有使用前面提到的編譯器,請注意這是C++ 14功能,您可能需要在編譯器中啟用C++ 14支援。
如果您在類函數實作中使用了lambda,并調用了所在類的靜态函數,則必須明确捕獲<code>this</code>。 否則,它将無法使用g++ 4.7及更早版本進行編譯。
根據以下規則格式化lambda:
把捕獲清單,參數清單,傳回類型和左括号放在第一行,在接下來的幾行中縮進主體,在新的一行上關閉右括号。
将函數調用的右圓括号和分号與lambda的右大括号放在同一行。
如果在'if'語句中使用lambda,請在新行上啟動lambda,以避免在lambda的左大括号和'if'語句的左大括号之間造成混淆。
可選,如果合适,将lambda完整地放在同一行。
可選,在以下情況下,可以使用auto關鍵字。 如有疑問,例如,如果使用auto會使代碼的可讀性降低,請不要使用auto。 請記住,代碼的讀取次數比編寫的次數要多。
避免在同一條語句中重複某個類型。
配置設定疊代器類型時
不需要将非域化枚舉隐式轉換為int,或附加域有用時,使用域化枚舉。
如果多個構造函數使用基本上相同的代碼,請使用委托構造函數。
使用初始化清單來初始化容器,例如:
如果對大括号使用初始化,請遵循與圓括号相同的規則。 例如:
但是請注意不要過度使用它,以免混淆您的代碼。
使用非靜态資料成員初始化進行平凡(trivial)的初始化,但在公共導出類中除外。
考慮使用=default和=delete來控制特殊函數。
覆寫虛函數時,建議使用override關鍵字。不要在已覆寫函數上使用virtual。
確定一個類對所有被覆寫的函數始終使用override,以區分沒有被覆寫的。
所有編譯器都支援nullptr,但在使用方面尚未達成共識。 如有疑問,請詢問子產品的維護者是否更喜歡使用nullptr。
您可以使用基于範圍的for循環,但要注意虛假分離問題。 如果for循環僅讀取容器,且容器是const的還是不共享的,不明顯,請使用std::cref()來確定不會不必要地分離容器。
請記住,将Q_OBJECT宏添加到依賴于元對象系統的QObject子類中。 與元對象系統相關的功能包括信号和槽的定義,qobject_cast <>的使用等。 另請參閱Casting。
優先使用Qt5樣式的connect()調用,而不是Qt4樣式的。
使用Qt4樣式的connect()調用時,請在connect語句中标準化信号和槽的參數,以使更加快速安全地查找信号和槽。 您可以使用$QTDIR/util/normalize來标準化現有代碼。 有關更多資訊,請參見QMetaObject::normalizedSignature。
如果建立一個新檔案,則檔案頂部應包含頭注釋,保持與Qt Creator其他源檔案中的一樣。
使用以下格式來包含Qt标頭:#include 。 不要包括可能在Qt4和Qt5之間進行了更改的子產品。
排列順序應從特定到通用,以確定頭檔案是齊全的。 例如:
<code>#include "myclass.h"</code>
<code>#include "otherclassinplugin.h"</code>
<code>#include <otherplugin/someclass.h></code>
<code>#include <QtClass></code>
<code>#include <stdthing></code>
<code>#include <system.h></code>
将其他插件的頭檔案括在方括号(<>)而不是引号("")中,以便更輕松地發現源代碼中的外部依賴項。
在同樣性質頭檔案的長塊之間添加空行,并嘗試按字母順序在塊内排列頭檔案。
避免使用C強制轉換,最好使用C++強制轉換(static_cast,const_cast,reinterpret_cast)reinterpret_cast和C類型強制轉換都是危險的,但是至少reinterpret_cast不會删除const修飾符。
除非您知道要做什麼,否則不要使用dynamic_cast,對QObject使用qobject_cast或重構設計,例如引入type()函數(請參閱QListWidgetItem)。
使用問号運算符時要格外小心。 如果傳回的類型不相同,則某些編譯器會生成的代碼會在運作時崩潰(您甚至不會收到編譯器警告):
對齊時要格外小心。
每當指針轉換時,目标所需對齊位元組數增加,在某些體系結構上,生成的代碼可能會在運作時崩潰。 例如,如果将const char 強制轉換為const int ,它将在整數兩位元組對齊或四個位元組對齊的機器上崩潰。
使用union來強制編譯器正确對齊變量。 在下面的示例中,可以確定AlignHelper的所有執行個體int邊界對齊:
對于頭檔案中的靜态聲明,請堅持使用整數類型,整數類型數組及其結構。 例如,static float i[SIZE_CONSTANT]; 在大多數情況下,不會在每個插件中進行優化和拷貝,最好避免使用。
任何具有構造函數或需要運作代碼進行初始化的對象,都不能作為庫代碼的全局對象,因為該構造函數或代碼何時運作未定義(首次使用時,庫加載時,main()之前或根本不進行)。
即使為共享庫定義了初始化程式的執行時間,移動該代碼到插件中或庫是靜态編譯時,你會遇到麻煩:
你應該這樣做:
注意:函數範圍内的靜态對象沒有問題。構造函數将在第一次函數進入時運作。但是,該代碼不是可重入的。
<code>char</code>是有符号的還是無符号的,取決于體系結構。 如果您明确需要有符号或無符号的char,請使用有符号<code>char</code>或uchar。 例如,以下代碼将在PowerPC上中斷:
避免使用64位枚舉值。 嵌入式AAPCS(ARM體系結構的過程調用标準)的ABI将所有枚舉值都寫死為32位整數。
不要混合使用const和非const疊代器。 這将在被破壞的編譯器上悄無聲息地崩潰。
不要在導出的類中内聯虛析構函數。 這會導緻依賴插件中的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 {}; 庫B具有類Q_EXPORT Y: public QList {};。突然,QList符号從兩個庫中導出,這産生了沖突。
如果存在明确的is-a關系,請使用繼承。
使用聚合來重複使用正交建構基礎類。
如果可以選擇,則優先選擇聚合而不是繼承。
我們的公共頭檔案必須在某些使用者的嚴格設定下仍然有效。 所有已安裝的頭檔案都必須遵循以下規則:
沒有C類型強制轉換(-Wold-style-cast)。請使用static_cast,const_cast或reinterpret_cast,對于基本類型,使用構造函數形式:int(a)代替(int)a。 有關更多資訊,請參見Casting。
沒有浮點數比較(-Wfloat-equal)。使用<code>qFuzzyCompare</code>來比較值與內插補點。使用<code>qIsNull</code>來檢查浮點數是否為二進制0,而不是将其與0.0進行比較,或者,最好将此類代碼移至實作檔案中。
不要在子類中隐藏虛函數({-Woverloaded-virtual})。如果基類A具有虛函數int val(),子類B具有相同名稱的重載,int val(int x),則類A的val函數将被隐藏。使用<code>using</code>關鍵字使其再次可見,并為被破壞的編譯器添加以下愚蠢的解決方法:
不要同名局部變量遮蔽其他外部變量(-Wshadow)。
如果可能的話,避免this->x = x;。
變量與在類中聲明的函數不要同名。
為了提高代碼的可讀性,始終先檢查預處理變量是否已定義,後擷取變量值(-Wundef)。
使用#defined運算符檢查預處理變量時,請始終把變量放在括号中。
我們使用"m_"字首約定,但公有結構化成員除外(通常在*Private類中,并且真正的公有結構化成員很少見)。<code>d</code>和<code>q</code>指針不受"m_"規則的限制。
<code>d</code>指針("Pimpls")被命名為“ d”,而不是"m_d"。<code>Foo</code>類中<code>d</code>指針的類型為<code>FooPrivate *</code>,其中FooPrivate所在命名空間與Foo相同,或者,如果Foo被導出,則在相應的{Internal}命名空間。
如果需要(例如,當私有對象需要發出對應類的信号時),FooPrivate可以成為Foo的友元。
如果私有類需要反向引用其對應的類,則将指針命名為<code>q</code>,其類型為<code>Foo *</code>。(與Qt中的約定相同:"q"看起來像倒置的"d"。)
不要使用智能指針來守衛<code>d</code>指針,因為它會增加編譯和連結時間開銷,并建立帶有更多符号的臃腫對象代碼,這會減慢調試器的啟動速度:
結果:
文檔是從源檔案和頭檔案生成的。 您編寫文檔是為了其他開發人員,而不是自己。 在頭檔案中,編寫接口文檔。 也就是說,函數是做什麼的,而不是怎麼實作的。
在.cpp檔案中,如果函數實作不明顯,則可以編寫相關的文檔。