Na ovoj stranici

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 će compatVersion="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 version pri deklariranju ovisnosti o drugom dodatku. Zadano za dodatke s oznakom Qt Creator koje pruža Qt Project je zahtijevati najnovije izdanje zakrpe.
Na primjer, xml-ph-0000@deepl.internal dodatak u xml-ph-0001@deepl.internal 3.1 beta (unutarnja broj

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 xml-ph-0000@deepl.internal-a moglo bi biti još bolja opc

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 operatora i zagradeNe koristite razmake između naziva operatora i zagrada. Znakovi jednakosti

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 umet

Opć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 ili
if (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:

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 class umjesto struct. 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, deklariramo class kao preferirani način.

Deklariranje varijabli

  • Izbjegavajte globalne varijable tipa class kako biste isključili probleme s redoslijedom inicijalizacije. Razmislite o korištenju Q_GLOBAL_STATIC ako se ne mogu izbjeći.
  • Deklarirajte globalne string literale kao
    const char aString[] = "Hello";
    xml-ph-0000@deepl.internal. Izbjegavajte kratka imena (kao što su a, rbarr, nughdeget) kad god je to
  • 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:
    QString a = "Joe";
    QString b = "Foo";
    
    -NOT-
    
    QString a = "Joe", b = "Foo";

    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ću QString 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 osjetljivo

Koristite 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:

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.h
    std::function<void(...)> &fancyLeafCallback();
    
    void possiblyFancyOperation();
  • U centralplugin/somewhere.cpp
    std::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.cpp u:
    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 impl

Na 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 ascii ako 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 \unicode ili odgovarajući makro. Na primjer: \uuml za ü.
    Koristite statičke ključne riječi umjesto anonimnih imenskih prostora kad god je to moguće. Ime loka
  • 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; })) {
        ...
    }
Ključna riječ

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 konstruktori

Delegirani 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) Obje reinterpret_cast i C-style castovi su opasni, ali barem reinterpret_cast neće ukloniti modificator const.
  • Ne koristite dynamic_cast, koristite qobject_cast za QObjects ili refaktorirajte svoj dizajn, na primjer uvođenjem funkcije type() (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 u const 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:

    union AlignHelper
    {
        char c;
        int i;
    };
    Držite se integralnih tipova, nizova integralnih tipova i njihovih struktura za statičke deklaracije
  • 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.

  • char je potpisani ili nepotiskani ovisno o arhitekturi. Koristite potpisani char ili uchar ako 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.
    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 izvozne klase. To dovodi do dupliciranih vtabela u ovisnim d
  • 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). Koristite static_cast, const_cast ili reinterpret_cast; za osnovne tipove koristite konstruktor: int(a) umjesto (int)a. Za više informacija pogledajte Casting.
  • Ne upotrebljavajte usporedbe float tipova (-Wfloat-equal). Koristite qFuzzyCompare za usporedbu vrijednosti s delta. Koristite qIsNull za 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), funkcija val klase A bit će skrivena. Koristite ključnu riječ using kako biste je ponovno učinili vidljivom i dodajte sljedeći smiješni zaobilazni trik za neispravne kompajlere:
    class B: public A
    {
    #ifdef Q_NO_USING_KEYWORD
    inline int val() { return A::val(); }
    #else
    using A::val;
    #endif
    };
    Ne maskirajte varijable (xml-ph-0000@deepl.internal).
  • 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.