Qt Creator Pravila kodiranja
Napomena: Ovaj dokument je u izradi.
Pravila kodiranja imaju za cilj usmjeriti programere sustava Qt Creator, pomoći im napisati razumljiv i održiv kod te smanjiti zabunu i iznenađenja.
Kao i obično, pravila nisu uklesana u kamen. Ako imate dobar razlog da prekršite neko pravilo, učinite to. Ali prvo se uvjerite da se barem neki drugi programeri slažu s vama.
Da biste doprinijeli glavnom izvoru Qt Creator a, trebali biste se pridržavati sljedećih pravila:
ph-0000@deepl.internal programeri, kako bismo im pomogli napisati razumljiv i održavan kod te kako b- Najvažnije pravilo je: KISS (držite ga kratkim i jednostavnim). Uvijek odaberite jednostavniju opciju implementacije umjesto složenije. To znatno olakšava održavanje.
- Pišite dobar C++ kod. To jest, čitljiv, dobro komentiran kada je potrebno i objektno-orijentiran.
- Iskoristite Qt. Nemojte izmišljati toplu vodu. Razmislite o tome koji su dijelovi vašeg koda dovoljno opći da bi mogli biti uključeni u Qt umjesto u Qt Creator.
- Prilagodite kod postojećim strukturama u Qt Creator u. Ako imate ideje za poboljšanja, raspravite ih s drugim programerima prije pisanja koda.
- Slijedite smjernice iz Code Constructs, Formatting, and Patterns and Practices.
- Dokumentirajte sučelja. Trenutno koristimo qdoc, ali se razmatra prelazak na doxygen.
Predaja koda
Da biste poslali kod na Qt Creator, morate razumjeti alate i mehaniku, kao i filozofiju iza Qt razvoja. Za više informacija o tome kako postaviti razvojno okruženje za rad na Qt Creator i kako poslati kod i dokumentaciju za uključivanje, pogledajte Qt smjernice za doprinos.
Binarna i izvorna kompatibilnost
Sljedeći popis opisuje kako se izdanja numeriraju i definira binarnu kompatibilnost i kompatibilnost izvornog koda između izdanja:
0000@deepl.internal, morate razumjeti alate i mehaniku kao i filozofiju iza Qt razvoja. Za više info- Qt Creator 3.0.0 je glavno izdanje, Qt Creator 3.1.0 je manje izdanje, a Qt Creator 3.1.3 je ispravno izdanje.
- Unazadna binarna kompatibilnost znači da kod povezan s ranijom verzijom biblioteke i dalje radi.
- Nadaljnja binarna kompatibilnost znači da kod povezan s novijom verzijom biblioteke radi s starijom bibliotekom.
- Kompatibilnost izvornog koda znači da se kod kompajlira bez izmjena.
Trenutno ne jamčimo binarnu niti izvorno-kodnu kompatibilnost između glavnih i manjih izdanja.
Međutim, nastojimo očuvati unazadnu binarnu kompatibilnost i unazadnu kompatibilnost izvornog koda za ispravke unutar iste manje inačice:
- Meki API zamrzavanje: Neposredno nakon beta izdanja manje važne verzije počinjemo održavati unazadnu kompatibilnost izvornog koda unutar te manje važne verzije, uključujući njezina patch izdanja. To znači da će se od tog trenutka kod koji koristi C++ API-ja Qt Creator -a moći kompajlirati na API-ju svih budućih verzija te manje važne verzije, uključujući njezina patch izdanja. I dalje mogu postojati povremeni iznimci od ovog pravila, o kojima treba pravilno obavijestiti.
- Strogo zamrzavanje API-ja: Počevši od kandidata za izdanje manje izmjene, održavamo unazadnu kompatibilnost izvornog koda unutar te manje izmjene, uključujući njezina patch izdanja. Također počinjemo održavati unazadnu binarnu kompatibilnost, s iznimkom da se to neće odražavati u postavci compatVersion dodataka. Dakle, dodatci za Qt Creator napisani za kandidata za izdanje neće se zapravo učitati u konačnom izdanju ili kasnijim verzijama, iako bi teoretski binarna kompatibilnost biblioteka to dopuštala. Pogledajte odjeljak o specifikacijama dodataka u nastavku.
- Strogo zamrzavanje ABI-ja: Počevši od konačnog izdanja manje verzije, održavamo unatrag kompatibilnost izvornog koda i binarnu kompatibilnost za to izdanje i sve njegove zakrpne izdanja.
Za očuvanje unazad kompatibilnosti:
- Ne dodajte niti uklanjajte javni API (na primjer globalne funkcije, javne/zaštićene/privatne članice funkcije).
- Nemojte ponovno implementirati funkcije (ni inline, ni zaštićene ni privatne funkcije).
- Provjerite zaobilazeća rješenja za binarnu kompatibilnost kako biste sačuvali binarnu kompatibilnost.
Za više informacija o binarnoj kompatibilnosti pogledajte odjeljak Binary Compatibility Issues With C++.
S gledišta metapodataka dodatka to znači da:
- Qt Creator plugini u patch izdanjima imat će manji broj izdanja kao
compatVersion. Na primjer, plugini iz verzije 3.1.2 imat ćecompatVersion="3.1.0". - Predizdava male verzije, uključujući i kandidata za izdanje, i dalje će imati
compatVersion, što znači da se plugini kompajlirani za konačno izdanje neće učitati u predizdanjima. - Qt Creator Razvijači dodataka mogu odlučiti zahtijeva li njihov dodatak određeno izdanje zakrpe (ili novije) drugih dodataka s oznakom Qt Creator, ili će raditi sa svim izdanjima zakrpe te manje verzije, postavljanjem odgovarajućeg polja
versionpri deklariranju ovisnosti o drugom dodatku. Zadano za dodatke s oznakom Qt Creator koje pruža Qt Project je zahtijevati najnovije izdanje zakrpe.
Na primjer, dodatak Find u Qt Creator u 3.1 beta (unutarnji broj verzije 3.0.82) imat će
<plugin name="Find" version="3.0.82" compatVersion="3.0.82"> <dependencyList> <dependency name="Core" version="3.0.82"/> ....
Find dodatak u Qt Creator 3.1.0 final će imati
<plugin name="Find" version="3.1.0" compatVersion="3.1.0"> <dependencyList> <dependency name="Core" version="3.1.0"/> ....
Dodatak Find u Qt Creator 3.1.1 patch izdanju imat će verziju 3.1.1, bit će binarno kompatibilan unatrag s dodatkom Find verzije 3.1.0 (compatVersion="3.1.0"), i zahtijevat će dodatak Core koji je binarno kompatibilan unatrag s dodatkom Core verzije 3.1.1:
<plugin name="Find" version="3.1.1" compatVersion="3.1.0"> <dependencyList> <dependency name="Core" version="3.1.1"/> ....
Konstrukcije koda
Slijedite smjernice za konstrukcije koda kako bi kod bio brži i jasniji. Osim toga, smjernice vam omogućuju da iskoristite snažnu provjeru tipova u C++.
- Radije koristite predinkrement nego postinkrement kad god je to moguće. Predinkrement je potencijalno brži od postinkrementa. Samo razmislite o očitim implementacijama predinkrementa/postinkrementa. Ovo pravilo vrijedi i za dekrement:
++T; --U; -NOT- T++; U--;
- Pokušajte svesti na najmanju mjeru višestruko izvršavanje istog koda. Ovo se posebno odnosi na petlje:
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) { ...; }
Formatiranje
Pisanje identifikatora velikim slovima
Koristite camel case u identifikatorima.
Prvo slovo identifikatora pišite velikim slovom na sljedeći način:
Nazivi klasa započinju velikim slovom.- Nazivi klasa započinju velikim slovom.
- Nazivi funkcija započinju malim slovom.
- Nazivi varijabli započinju malim slovom.
- Nazivi i vrijednosti enuma započinju velikim slovom. Vrijednosti enuma bez opsega sadrže dio naziva tipa enuma.
Prazni znakovi
- Koristite četiri razmaka za uvlačenje, bez tabova.
- Koristite prazne retke za grupiranje izjava gdje je to prikladno.
- Uvijek koristite samo jedan prazan redak.
Pokazivači i reference
Za pokazivače ili reference uvijek koristite jedan razmak prije zvjezdice (*) ili ampersanda (&), ali nikada nakon njih. Izbjegavajte C-style castove kad god je to moguće:
char *blockOfMemory = (char *)malloc(data.size()); char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size())); -NOT- char* blockOfMemory = (char* ) malloc(data.size());
Naravno, u ovom posebnom slučaju korištenje new moglo bi biti još bolja opcija.
Nazivi operatora i zagrade
Ne koristite razmake između naziva operatora i zagrada. Znakovi jednakosti (==) dio su naziva operatora, pa razmaci čine deklaraciju da izgleda kao izraz:
operator==(type) -NOT- operator == (type)
Nazivi funkcija i zagrade
Ne koristite razmake između naziva funkcija i okruglih zagrada:Ne koristite razmake između naziva funkcija i okruglih zagrada:
void mangle() -NOT- void mangle ()
Ključne riječi
Uvijek koristite jedan razmak nakon ključne riječi i prije kose zagrade:
if (foo) { } -NOT- if(foo){ }
Komentari
Općenito, stavite jedan razmak nakon "//". Za poravnanje teksta u višorednim komentarima možete umetOpćenito, stavite jedan razmak nakon "//". Za poravnanje teksta u višorednim komentarima možete umetnuti više razmaka.
Zagrade
Kao osnovno pravilo, postavite lijevu okruglu zagradu na isti redak kao početak izjave:
if (codec) { } -NOT- if (codec) { }
Izuzetak: Implementacije funkcija i deklaracije klasa uvijek imaju lijevu zagradu na početku retka:
static void foo(int g) { qDebug("foo: %i", g); } class Moo { };
Koristite kose zagrade kada tijelo uvjetne izjave sadrži više redaka, kao i ako je izjava na jednom retku pomalo složena. Inače, izostavite ih:
if (address.isEmpty()) return false; for (int i = 0; i < 10;++i) qDebug("%i", i); -NE- if (address.isEmpty()) { return false; } for (int i = 0; i < 10;++i) { qDebug("%i", i); }
Izuzetak 1: Koristite zagrade čak i ako rodna izjava obuhvaća više redaka ili se prelama:
xml-ph-0000@deepl.internalIzuzetak 1: Koristite zagrade i ako rodna izjava obuhvaća više redaka iliif (address.isEmpty() || !isValid() || !codec) { return false; }
Napomena: Ovo bi se moglo preformulirati kao:
if (address.isEmpty()) return false; if (!isValid()) return false; if (!codec) return false;
Izuzetak 2: Koristite zagrade i u blokovima if-then-else kada if-kod ili else-kod zauzimaju više redaka:
if (address.isEmpty()) { --it; } else { qDebug("%s", qPrintable(address)); ++it; } -NE- if (address.isEmpty()) --it; else { qDebug("%s", qPrintable(address)); ++it; }
if (a) { if (b) ... else ... } -NOT- if (a) if (b) ... else ...
Koristite kose zagrade kada je tijelo uvjetne izjave prazno:
while (a) {} -NOT- while (a);
Zagrade
Koristite zagrade za grupiranje izraza:
if ((a && b) || c) -NOT- if (a && b || c)
(a + b) & c -NOT- a + b & c
Pretvaranje retka
- Držite retke kraćima od 100 znakova.
- Umetnite prijelome retka ako je potrebno.
- Zarezi se stavljaju na kraju slomljenog retka.
- Operatori počinju na početku novog retka.
if (longExpression || otherLongExpression || otherOtherLongExpression) { } -NOT- if (longExpression || otherLongExpression || otherOtherLongExpression) { }
Deklaracije
- Koristite ovaj redoslijed za sekcije pristupa u vašoj klasi: javno, zaštićeno, privatno. Javno područje je zanimljivo svakom korisniku klase. Privatno područje zanima samo implementatore klase (vas).
- Izbjegavajte deklariranje globalnih objekata u deklaracijskoj datoteci klase. Ako se ista varijabla koristi za sve objekte, upotrijebite statički član.
- Koristite
classumjestostruct. Neki kompajleri iskrivljuju tu razliku u nazive simbola i ispisuju upozorenja ako deklaraciju strukture slijedi definicija klase. Kako bismo izbjegli stalne promjene s jednog na drugi, deklariramoclasskao preferirani način.
Deklariranje varijabli
- Izbjegavajte globalne varijable tipa class kako biste isključili probleme s redoslijedom inicijalizacije. Razmislite o korištenju
Q_GLOBAL_STATICako se ne mogu izbjeći. - Deklarirajte globalne string literale kaoxml-ph-0000@deepl.internal. Izbjegavajte kratka imena (kao što su a, rbarr, nughdeget) kad god je to
const char aString[] = "Hello";
- Izbjegavajte kratka imena (kao što su a, rbarr, nughdeget) kad god je to moguće. Koristite jednobojna imena varijabli samo za brojače i privremene varijable, gdje je svrha varijable očita.
- Deklarirajte svaku varijablu na zasebnom retku:
Napomena:
QString a = "Joe"formalno poziva konstruktor kopije na privremenoj varijabli koja je konstruirana iz string literalnog izraza. Stoga je to potencijalno skuplje od izravne konstrukcije pomoćuQString a("Joe")-a. Međutim, kompilatoru je dopušteno izostaviti kopiranje (čak i ako to ima sporedne učinke), a moderni kompilatori to obično i čine. S obzirom na jednake troškove, kod Qt Creator -a favorizira se idiom '=' jer je u skladu s tradicionalnom inicijalizacijom u stilu C-a, ne može se zamijeniti s deklaracijom funkcije i smanjuje razinu ugniježđenih okruglih zagrada u složenijim inicijalizacijama. - Izbjegavajte skraćenice:
int height; int width; char *nameOfThis; char *nameOfThat; -NOT- int a, b; char *c, *d;
- Odgodite deklaraciju varijable dok ne bude potrebna. Ovo je posebno važno kada se inicijalizacija obavlja istovremeno.
Prostori imena
- Stavite lijevu okruglu zagradu na isti redak kao ključnu riječ
namespace. - Ne uvlačite deklaracije ili definicije unutra.
- Opcionalno, ali se preporučuje ako prostori imena zauzimaju više od nekoliko redaka: dodajte komentar nakon desne kose zagrade u kojem ponavljate naziv prostora imena.
namespace MyPlugin { void someFunction() { ... } } // namespace MyPlugin
- Kao iznimka, ako se unutar prostora imena nalazi samo jedna deklaracija klase, sve može biti na jednoj liniji:
namespace MyPlugin { class MyClass; }
- Ne koristite using-direktive u zaglavnim datotekama.
- Ne oslanjajte se na using-direktive pri definiranju klasa i funkcija, već ih definirajte u odgovarajuće nazvanom deklarativnom području.
- Ne oslanjajte se na using-direktive pri pristupu globalnim funkcijama.
- U ostalim slučajevima preporučuje se korištenje using-direktiva jer pomažu izbjeći zagušenje koda. Poželjno je staviti sve using-direktive pri vrhu datoteke, nakon svih uključenja.
[in foo.cpp] ... #include "foos.h" ... #include <utils/filename.h> ... using namespace Utils; namespace Foo::Internal { void SomeThing::bar() { FilePath f; // or Utils::FilePath f ... } ... } // Foo::Internal // or // namespace Foo::Internal -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 { ... }
Šabloni i prakse
Imenosprostor
Pročitajte Qt In Namespace i imajte na umu da je sav kôd u imenskom prostoru Qt Creator.
Pravilo imenovanja unutar Qt Creator je sljedeće:
- Klase/simboli biblioteke ili dodatka koji se izvoze za korištenje od strane drugih biblioteka ili dodataka nalaze se u imenskom prostoru specifičnom za tu biblioteku/dodatak, npr.
MyPlugin. - Klase/simboli biblioteke ili dodatka koji se ne izvoze nalaze se u dodatnom imenskom prostoru
Internal, npr.MyPlugin::Internal.
Prenošenje imena datoteka
Qt Creator API očekuje nazive datoteka u prenosivom formatu, tj. s crticama (/) umjesto nazadnih crtica (\) čak i na Windowsu. Da biste proslijedili naziv datoteke od korisnika API-ju, prvo ga pretvorite pomoću QDir::fromNativeSeparators. Da biste korisniku prikazali naziv datoteke, vratite ga u izvorni format pomoću QDir::toNativeSeparators. Razmislite o korištenju Utils::FilePath::fromUserInput(QString) i Utils::FilePath::toUserOutput() za ove zadatke.
Koristite xml-ph-0000@deepl.internal pri usporedbi naziva datoteka, jer ona uzima u obzir osjetljivoKoristite Utils::FilePath pri usporedbi naziva datoteka jer uzima u obzir osjetljivost na velika i mala slova. Također provjerite usporedite li čiste putanje (QDir::cleanPath()).
Klase koje koristiti i klase koje ne koristiti
Značajan dio koda Qt Creator obrađuje podatke na uređajima koji se razlikuju od razvojnog računala. Ti se uređaji mogu razlikovati po aspektima kao što su razdjelnik putanja, završeci retka, detalji pokretanja procesa i slično.
Međutim, neke osnovne Qt klase pretpostavljaju da se Qt aplikacija bavi samo računalima sličnima razvojnom računalu.
Stoga te klase nisu prikladne za korištenje u onom dijelu Qt Creator a koji se bavi ne-lokalnim kodom. Umjesto toga, biblioteka Utils Qt Creator a pruža zamjene, što dovodi do sljedećih pravila:
- Koristite Utils::FilePath za bilo koji QString koji semantički predstavlja datoteku ili direktorij, vidi također Prijenos imena datoteka.
- Radije koristite Utils::FilePath nego bilo koji od QDir i QFileInfo.
- Radije koristite Utils::Process nego QProcess.
- Ako funkcionalnost Utils::FilePath ili Utils::Process nije dovoljna za vašu namjenu, radije ih unaprijedite nego se vratite na QString ili QProcess.
- Izbjegavajte platforme #ifdefs osim ako su apsolutno potrebne za lokalno izvršeni kod, a čak i tada je poželjnije koristiti Utils::HostInfo umjesto #ifdefs.
Ovisnosti dodatka
Kako bismo Qt Creator održali skalabilnim, nastojimo ograničiti stroge ovisnosti o vrijeme izvođenja između dodataka i vanjskih biblioteka na najmanju moguću mjeru.
Postoji nekoliko tehnika za to.Postoji nekoliko tehnika za to.
Proširivanje funkcionalnosti osnovnog plugina pomoću povratnih poziva
Ovaj obrazac omogućuje listnim dodatcima da centralnom dodatku osiguraju dodatnu funkcionalnost injektiranjem povratne funkcije.
Koristi se za funkcionalnost koja je korisna u nekim postavkama, ali nije primjenjiva ili se smatra nametljivom (npr. zbog veličine ili vanjskih ovisnosti) u drugima, pa korisnici mogu željeti omogućiti ili onemogućiti je bez utjecaja na ostatak svoje konfiguracije.
Listni dodatak tada može, na primjer, ovisiti o velikoj (ili inače nezgodnoj) vanjskoj ovisnosti, a da tu ovisnost ne nameće središnjem dodatku.
Cjelokupni obrazac izgleda ovako:
- U
centralplugin/somewhere.hstd::function<void(...)> &fancyLeafCallback(); void possiblyFancyOperation();
- U
centralplugin/somewhere.cppstd::function<void(...)> &fancyLeafCallback() { static std::function<void(...)> callback; return callback; } ... void possiblyFancyOperation() { if (auto callback = fancyLeafCallback()) { // can be used callback(); } else { // not present, use some plain fallback implementation // or error out } }
- U
leafplugin/leafplugin.cppu:void LeafPlugininitialize() { ... Central::fancyLeafCallback() = [this](...) { doSomething; }; ... }
Točke proširenja dodatka
Točka proširenja dodatka je sučelje koje jedan dodatak pruža, a koje drugi implementiraju. Zatim dodatak dohvaća sve implementacije sučelja i koristi ih. To jest, one proširuju funkcionalnost dodatka. Obično se implementacije sučelja stavljaju u globalni bazen objekata tijekom inicijalizacije dodatka, a dodatak ih dohvaća iz bazena objekata na kraju inicijalizacije.
Točke proširenja dodatkaTočka proširenja dodatka je sučelje koje pruža jedan dodatak kako bi ga implNa primjer, dodatak Find pruža sučelje FindFilter koje drugi dodaci mogu implementirati. Pomoću sučelja FindFilter mogu se dodati dodatna područja pretraživanja koja se pojavljuju u dijaloškom okviru za pretraživanje ( Advanced Search ). Dodatak Find dohvaća sve implementacije sučelja FindFilter iz globalnog fonda objekata i prikazuje ih u dijaloškom okviru. Dodatak prosljeđuje stvarni zahtjev za pretraživanje odgovarajućoj implementaciji sučelja FindFilter, koja zatim provodi pretraživanje.
Korištenje globalnog spremnika objekata
Možete dodavati objekte u globalni bazen objekata putem ExtensionSystem::PluginManager::addObject(), i ponovno dohvaćati objekte određene vrste putem ExtensionSystem::PluginManager::getObject(). Ovo bi se uglavnom trebalo koristiti za implementacije točaka proširenja dodataka.
Napomena: Nemojte stavljati singleton u pool i ne dohvaćajte ga odatle. Umjesto toga koristite obrazac singletona.
C++ značajke
- Radije koristite zaštitu glava datoteka (
#pragma once) nego header guardse. - Ne koristite iznimke, osim ako znate što radite.
- Ne koristite RTTI (Run-Time Type Information; tj. strukturu typeinfo, operatore dynamic_cast ili typeid, uključujući bacanje iznimki), osim ako znate što radite.
- Ne koristite virtualno nasljeđivanje, osim ako znate što radite.
- Koristite šablone mudro, a ne samo zato što možete.
Savjet: Koristite compile autotest kako biste provjerili podržava li neka C++ značajka svi kompajleri u testnom okruženju.
- Sav kod je samo ASCII (samo 7-bitni znakovi, pokrenite
man asciiako niste sigurni)- Objašnjenje: Imamo previše lokala unutar tvrtke i nezdravu mješavinu UTF-8 i Latin1 sustava. Obično se znakovi > 127 mogu oštetiti, a da to ni ne znate, klikom na "Spremi" u vašem omiljenom uređivaču.
- Za nizove: Koristite \nnn (pri čemu je nnn oktalna notacija željenog localea) ili \xnn (pri čemu je nn heksadecimalna notacija). Na primjer: QString s = QString::fromUtf8("\213\005");
- Za umlate u dokumentaciji ili druge ne-ASCII znakove upotrijebite naredbu qdoc
\unicodeili odgovarajući makro. Na primjer:\uumlza ü.
- Koristite statičke ključne riječi umjesto anonimnih imenskih prostora kad god je to moguće. Naziv lokaliziran na kompilacijsku jedinicu s atributom 'static' ima zajamčenu internu povezanost. Za nazive deklarirane u anonimnim imenskim prostorima, C++ standard nažalost propisuje vanjsku povezanost (ISO/IEC 14882, 7.1.1/6, ili pogledajte razne rasprave o tome na mailing listama gcc-a).
Nulti pokazivači
Koristite nullptr za konstante pokazivača ništice.
void *p = nullptr; -NOT- void *p = NULL; -NOT- void *p = '\0'; -NOT- void *p = 42 - 7 * 6;
Napomena: Kao iznimka, uvezeni kod trećih strana, kao i kod koji komunicira s nativnim API-jima (src/support/os_*), mogu koristiti NULL ili 0.
Lambe
Formatirajte lambda prema sljedećim pravilima:
- Kada lambda ne prima argumente niti navodi tip povrata, izostavite okrugle zagrade.
[] { ... lambda body ... } -NOT- []() { ... lambda body ... }
- Spajajte kvadratne zagrade s okruglim zagradama pri definiranju lambde.
[](int a) { ... lambda body ... } -NOT- [] (int a) { ... lambda body ... }
- Postavite popis hvatanja, popis parametara, tip povrata i početnu klammu na prvu liniju, tijelo s uvučenim nastavkom na sljedećim linijama i završnu klammu na novu liniju.
[]() -> bool { something(); return isSomethingElse(); } -NOT- []() -> bool { something(); somethingElse(); }
- Postavite zatvarajući zagrljaj i semikolon poziva okružujuće funkcije na istu liniju kao i zatvarajući zagrljaj lambde.
foo([] { something(); });
- Ako koristite lambda u uvjetnoj naredbi 'if', započnite lambda na novom retku kako biste izbjegli zabunu između početnog kosa zagrada za lambda i početnog kosa zagrada za uvjetnu naredbu 'if'.
if (anyOf(fooList, [](Foo foo) { return foo.isGreat(); }) { return; } -NOT- if (anyOf(fooList, [](Foo foo) { return foo.isGreat(); }) { return; }
- Po želji, stavite lambda u potpunosti na jednu liniju ako stane.
foo([] { return true; }); if (foo([] { return true; })) { ... }
auto Ključna riječ
Po želji možete koristiti ključnu riječ auto u sljedećim slučajevima. Ako ste u nedoumici, na primjer ako bi upotreba auto mogla učiniti kod manje čitljivim, ne koristite auto. Imajte na umu da se kod mnogo češće čita nego piše.
- Kada izbjegava ponavljanje tipa u istoj izjavi.
auto something = new MyCustomType; auto keyEvent = static_cast<QKeyEvent *>(event); auto myList = QStringList({ "FooThing", "BarThing" });
- Pri dodjeljivanju tipova iteratora.
auto it = myList.const_iterator();
Ograničeni enumovi
Možete koristiti enumove s opsegom na mjestima gdje implicitna konverzija enumova bez opsega u int nije poželjna ili je dodatni opseg koristan.
Delegirani konstruktoriDelegirani konstruktori
Koristite delegirane konstruktore ako više konstruktora koristi u suštini isti kod.
Popis inicijalizatora
Koristite liste inicijalizatora za inicijalizaciju spremnika, na primjer:
const QList<int> values = {1, 2, 3, 4, 5};
Inicijalizacija s valovitim zagradama
Ako koristite inicijalizaciju s uzvodnim zagradama, slijedite ista pravila kao i kod okruglih zagrada. Na primjer:
class Values // the following code is quite useful for test fixtures { float floatValue = 4; // prefer that for simple types QList<int> 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
Ali budite oprezni da ga ne biste pretjerano koristili i zamutili svoj kod.
Inicijalizacija ne-statističkog člana podataka
Koristite inicijalizaciju ne-statik članova podataka za trivijalne inicijalizacije, osim u javno izvoznim klasama.
Defaultne i izbrisane funkcije
Razmotrite korištenje =default i =delete za kontrolu posebnih funkcija.
Final i Override
Preporučuje se korištenje ključne riječi final za klase od kojih se ne nasljeđuje. To je tipičan slučaj za klase vrijednosti, tvorničke klase i većinu proizvoda tvorničkih klasa.
Preporučuje se koristiti ključnu riječ final kada se prepravljaju virtualne funkcije u final klasama i kad se ne želi da se virtualna funkcija prepravlja u budućnosti. Nemojte koristiti virtual na funkcijama final.
Preporučuje se koristiti ključnu riječ override pri prebrisavanju virtualnih funkcija ako final nije primjenjiv, tj. kada je namijenjeno daljnje nasljeđivanje klase i može biti potrebno daljnje prebrisavanje funkcije.
Imajte na umu da se neki kompajleri žale kada naslijeđena klasa označi samo neke, a ne sve prebrisane funkcije kao final ili override, stoga pazite da označite sve odjednom.
for-petlja temeljena na rasponu
Možete koristiti for-petlje temeljene na rasponu, ali pazite na problem nepoželjnog odvajanja. Ako for-petlja samo čita spremnik i nije očito je li spremnik konstantan ili ne dijeli se, upotrijebite std::cref() kako biste osigurali da se spremnik ne odvoji nepotrebno.
std::optional
Izbjegavajte funkciju value() koja baca iznimke. Prvo provjerite dostupnost vrijednosti, a zatim koristite funkcije koje ne bacaju iznimke za pristup vrijednostima, poput operator* i operator->. U vrlo jednostavnim slučajevima možete koristiti i value_or().
if (optionalThing) { val = optionalThing->member; other = doSomething(*optionalThing); } -NOT- if (optionalThing) { val = optionalThing.value().member; other = doSomething(optionalThing.value()); }
Korištenje QObjecta
- Ne zaboravite dodati makro Q_OBJECT u podklase klase QObject koje ovise o sustavu meta-objekata. Značajke povezane sa sustavom meta-objekata su definiranje signala i slotova, korištenje
qobject_cast<>i druge. Vidi također Casting. - Radije koristite pozive
connect()a u Qt5 stilu nego u Qt4 stilu. - Kod korištenja poziva
connect()a u Qt4 stilu, normalizirajte argumente za signale i slotove unutar naredbi connect kako biste sigurno ubrzali pretraživanje signala i slotova za nekoliko ciklusa. Možete koristiti $QTDIR/util/normalize za normalizaciju postojećeg koda. Za više informacija pogledajte QMetaObject::normalizedSignature. - Izbjegavajte korištenje
QObject::sender(). Umjesto toga pošaljite pošiljatelja eksplicitno.connect(action, &QAction::triggered, this, [this, action] { onActionTriggered(action); }); ... void MyClass::onActionTriggered(QAction *action) { ... use action ... } -NOT- connect(action, &QAction::triggered, this, &MyClass::onActionTriggered); ... void MyClass::onActionTriggered() { QAction *action = qobject_cast<QAction *>(sender()); if (action == nullptr) return; ... use action ... }
- Izbjegavajte
QSignalMapper. Umjesto toga upotrijebite lambda i proslijedite mapiranu vrijednost putem hvatanja lamdbe.
Naslovi datoteka
Ako stvarate novu datoteku, na vrhu datoteke treba biti komentar zaglavlja jednak onome koji se nalazi u drugim izvornih datotekama Qt Creator.
Uključivanje zaglavlja
- Koristite sljedeći format za uključivanje Qt header-a:
#include <QWhatEver>. Nemojte uključivati modul jer se mogao promijeniti između Qt4 i Qt5. - Raspoređite uključivanja u redoslijed od specifičnog prema generičkom kako biste osigurali da su glave samostalne. Na primjer:
#include "myclass.h"#include "otherclassinplugin.h"#include <otherplugin/someclass.h>#include <QtClass>#include <stdthing>#include <system.h>
- Obuhvatite zaglavlja iz drugih dodataka u kutaste zagrade (<>) umjesto u navodnike (""), kako biste lakše uočili vanjske ovisnosti u izvorima.
- Dodajte prazne retke između dugih blokova srodnih zaglavlja i pokušajte poredati zaglavlja abecednim redom unutar bloka.
Konverzija tipa
- Izbjegavajte C-ove castove, radije koristite C++ castove (
static_cast,const_cast,reinterpret_cast) Objereinterpret_casti C-style castovi su opasni, ali baremreinterpret_castneće ukloniti modificator const. - Ne koristite
dynamic_cast, koristiteqobject_castza QObjects ili refaktorirajte svoj dizajn, na primjer uvođenjem funkcijetype()(vidi QListWidgetItem), osim ako znate što radite.
Pitanja specifična za kompajler i platformu
- Budite izuzetno oprezni pri korištenju operatora upitnika. Ako se vraćeni tipovi ne podudaraju, neki kompajleri generiraju kod koji se ruši tijekom izvođenja (nećete dobiti ni upozorenje kompajlera):
QString s; // crash at runtime - QString vs. const char * return condition ? s : "nothing";
- Budite iznimno oprezni s poravnanjem.
Kad god se pokazivač pretvori na način da se povećava potrebna poravnatost cilja, rezultatni kod na nekim arhitekturama može se srušiti tijekom izvođenja. Na primjer, ako se
const char *pretvori uconst int *, program će se srušiti na računalima na kojima se cijeli brojevi moraju poravnati na granicama od dva ili četiri bajta.Koristite union kako biste natjerali kompajler da pravilno poravna varijable. U primjeru ispod možete biti sigurni da su svi primjerci klase AlignHelper poravnati na granicama cijelih brojeva:
Držite se integralnih tipova, nizova integralnih tipova i njihovih struktura za statičke deklaracijeunion AlignHelper { char c; int i; };
- Držite se integralnih tipova, nizova integralnih tipova i njihovih struktura za statičke deklaracije u zaglavljima. Na primjer,
static float i[SIZE_CONSTANT];neće biti optimiziran i kopiran u svaki dodatak u većini slučajeva, pa bi bilo dobro izbjegavati ga. - Sve što ima konstruktor ili zahtijeva izvršavanje koda da bi se inicijaliziralo ne može se koristiti kao globalni objekt u kodu biblioteke, budući da nije definirano kada će se taj konstruktor ili kod izvršiti (pri prvom korištenju, pri učitavanju biblioteke, prije
main()ili uopće ne).Čak i ako je vrijeme izvođenja inicijalizatora definirano za zajedničke biblioteke, naići ćete na probleme kada taj kod premjestite u dodatak ili ako je biblioteka statički kompilirana:
// 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();
Što možete učiniti:
// 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"); }
Napomena: statički objekti u opsegu funkcije nisu problem. Konstruktor će se pokrenuti prvi put kad se funkcija pozove. Međutim, kod nije reentrant.
charje potpisani ili nepotiskani ovisno o arhitekturi. Koristite potpisanichariliucharako izričito želite potpisani ili nepotiskani char. Sljedeći će se kod, na primjer, slomiti na PowerPC-u:// Condition is always true on platforms where the // default is unsigned: if (c >= 0) { ... }
- Izbjegavajte 64-bitne enum vrijednosti. Ugrađeni ABI standarda AAPCS (Standard za pozivanje procedura za ARM arhitekturu) sve enum vrijednosti fiksno kodira kao 32-bitni cijeli broj.
- Nemojte miješati konst i ne-konst iteratore. To će tiho uzrokovati pad na neispravnim kompajlerima.Nemojte inlinirati virtualne razarače u izvozne klase. To dovodi do dupliciranih vtabela u ovisnim d
for (Container::const_iterator it = c.constBegin(); it != c.constEnd(); ++it) -NOT- for (Container::const_iterator it = c.begin(); it != c.end(); ++it)
- Nemojte inlinirati virtualne razarače u izvoznim klasama. To dovodi do dupliciranih vtabela u ovisnim dodatcima, a to također može narušiti RTTI. Vidi QTBUG-45582.
Estetika
- Radije koristite enumove bez opsega za definiranje konstanta nego statičke konstante tipa int ili definicije (defines). Vrijednosti enumeracije kompajler će zamijeniti tijekom kompilacije, što rezultira bržim kodom. Definicije (defines) nisu sigurne u pogledu imenog prostora.
- Radije koristite opisne nazive argumenata u zaglavljima. Qt Creator će prikazati nazive argumenata u njihovom okviru za dopunu. Bolje će izgledati u dokumentaciji.
Nasljeđivanje od klasa predložaka ili alata
Nasljeđivanje od template ili pomoćnih klasa ima sljedeće potencijalne zamke:
- Destruktor nije virtualan, što može dovesti do curenja memorije.
- Simboli se ne izvoze (i uglavnom su inlinirani), što može dovesti do sukoba simbola.
Na primjer, biblioteka A ima klasu Q_EXPORT X: public QList<QVariant> {};, a biblioteka B klasu Q_EXPORT Y: public QList<QVariant> {};. Odjednom se simboli QList izvoze iz dviju biblioteka, što dovodi do sukoba.
Nasljeđivanje naspram agregacije
- Koristite nasljeđivanje ako postoji jasna relacija "jest-a".
- Koristite agregaciju za ponovnu upotrebu ortogonalnih gradivnih blokova.
- Radije koristite agregaciju nego nasljeđivanje ako imate izbor.
Konvencije za javne zaglavne datoteke
Naše javne zaglavne datoteke moraju izdržati stroga postavljanja nekih naših korisnika. Sve instalirane zaglavne datoteke moraju slijediti ova pravila:
- Bez C-stila konverzija (
-Wold-style-cast). Koristitestatic_cast,const_castilireinterpret_cast; za osnovne tipove koristite konstruktor:int(a)umjesto(int)a. Za više informacija pogledajte Casting. - Ne upotrebljavajte usporedbe float tipova (
-Wfloat-equal). KoristiteqFuzzyCompareza usporedbu vrijednosti s delta. KoristiteqIsNullza provjeru je li float binarno 0, umjesto usporedbe s 0.0, ili, poželjnije, premjestite takav kod u datoteku implementacije. - Nemojte skrivati virtualne funkcije u podklasama ({-Woverloaded-virtual}). Ako bazna klasa A ima virtualnu funkciju
int val(), a podklasa B preopterećenje s istim imenom,int val(int x), funkcijavalklase A bit će skrivena. Koristite ključnu riječusingkako biste je ponovno učinili vidljivom i dodajte sljedeći smiješni zaobilazni trik za neispravne kompajlere:Ne maskirajte varijable (xml-ph-0000@deepl.internal).class B: public A { #ifdef Q_NO_USING_KEYWORD inline int val() { return A::val(); } #else using A::val; #endif };
- Ne zaklanjajte varijable (
-Wshadow). - Izbjegavajte stvari poput
this->x = x;ako je moguće. - Ne dajte varijablama isto ime kao funkcijama deklariranima u vašoj klasi.
- Kako biste poboljšali čitljivost koda, uvijek provjerite je li preprocesorska varijabla definirana prije nego što provjerite njezinu vrijednost (
-Wundef).#if defined(Foo) && Foo == 0 -NOT- #if Foo == 0 -NOT- #if Foo - 0 == 0
- Kada provjeravate definiciju preprocesora pomoću operatora
defined, uvijek uključite naziv varijable u zagradama.#if defined(Foo) -NOT- #if defined Foo
Nazivi članova klase
Koristimo konvenciju prefiksa "m_", osim za javne članove strukture (obično u *Privatnim klasama i u vrlo rijetkim slučajevima zaista javnih struktura). Pokazivači d i q izuzeti su od pravila "m_".
Pokazivači d ("Pimpls") nazivaju se "d", a ne "m_d". Tip pokazivača d u class Foo je FooPrivate *, gdje je FooPrivate deklariran u istom imenskom prostoru kao i Foo, ili, ako se Foo izvozi, u odgovarajućem imenskom prostoru {Internal}.
Ako je potrebno (na primjer kada privatni objekt treba emitirati signale odgovarajuće klase), FooPrivate može biti prijatelj klase Foo.
Ako privatnoj klasi treba referencu natrag na stvarnu klasu, pokazivač se naziva q, a njegov tip je Foo *. (Ista konvencija kao u Qt-u: "q" izgleda kao obrnuto "d".)
Nemojte koristiti pametne pokazivače za zaštitu pokazivača d jer to nameće dodatno opterećenje pri kompilaciji i povezivanju te stvara masivniji objektni kod s više simbola, što, na primjer, dovodi do usporenog pokretanja otklonika (debuggera):
############### 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(); }
Rezultati:
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
Dokumentacija
Dokumentacija se generira iz izvornih i glava datoteka. Dokumentirate za druge programere, a ne za sebe. U glavama datoteka dokumentirajte sučelja. To jest, što funkcija radi, a ne implementaciju.
U .cpp datotekama možete dokumentirati implementaciju ako implementacija nije očita.
Copyright © The Qt Company Ltd. and other contributors. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.