目錄
- 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類中,并且真正的公有結構化成員很少見),d和q指標不受"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
標籤:其他
