TDDD86 Datastrukturer, algoritmer och programmeringsparadigm
Stilguide
Stilguide
Följande är några stilkrav vi förväntar oss att dina program ska uppfylla för att de ska bli godkända. Detta är inte en fullständig lista; vänligen se respektive labb för andra specifika krav att uppfylla.
- Whitespace och indentering
- Namngivning och variabler
- Grundläggande programstruktur
- Redundans
- Effektivitet
- Kommentarer
- Funktioner och procedurell design
- Klassdesign
- 1: Whitespace och indentering
-
- 1-1: Indentera: Öka indenteringen ett steg för varje måsvinge
{
och minska den ett steg efter varje avslutande måsvinge}
. - Placera en radbrytning efter varje
{
. -
Placera inte mer än ett uttryck på samma rad.
// dåligt int x = 3; int y = 4; x++; if (a == b) { foo(); }
-
1-2: Långa rader: Bryt långa rader genom att trycka Enter efter en operator och fortsätta på nästa rad. Indentera den andra delen av raden två steg. (Qt Creator sköter detta atuomatiskt.) Till exempel:
int result = reallyLongFunctionOne() + reallyLongFunctionTwo() + reallyLongFunctionThree() + reallyLongFunctionFour(); int result2 = reallyLongFunction(parameterOne, parameterTwo, parameterThree, parameterFour, parameterFive, parameterSix);
-
1-3: Uttryck: Placeera ett mellanslag mellan operatorer och deras operander.
int x = (a + b) * c / d + foo();
-
1-4: Tomrader: Placera en tom rad mellan funktioner och grupper av satser. Placera inte ut tomrader i onödan, e.g., inuti logiskt sammanhängande stycken av kod. Det ska inte bli luftigt, det ska bli lättläst.
void foo() { ... } // tom rad här void bar() { ... }
- 1-1: Indentera: Öka indenteringen ett steg för varje måsvinge
- 2: Namngivning och variabler
-
-
2-1: Namn: Ge variabler deskriptiva namn, som
firstName
ellerhomeworkScore
. Undvik enbokstavsnamn somx
ellerc
om de inte har en betydelse i sammanhanget (x-koordinaten till exempel). Ett vanligt undantag här är loopräknare somi
,j
,k
och gränser somm
,n
. En bra tumregel för namngivning av funktioner är att fundera på hur det ser ut i kod som ska anropa den. -
2-2: Versaler: Namnge variabler och funktioner med kamelnotation
likeThis
, namnge klasser med PascalnotationLikeThis
och namnge konstanter med versalerLIKE_THIS
. -
2-3: Definitionsområde (scope): Deklarera variabler i så snävt definitionsområde som möjligt. Om till exempel en variabel endast används inuti en specifik
if
-sats definitionsområde, deklarera den då hellre inutiif
-satsen än i början av funktionen eller filen. -
2-4: Typer: Välj ändamålsenliga datatyper för dina variabler. Om en given variabel enbart kommer att lagra heltal, ge den då typen
int
snarare ändouble
. -
2-5: Föredra C++-strängar framför C-strängar: C++ har, förvirrande nog, två sorters strängar:
string
-klassen från C++ och den äldrechar*
(array/fält av characters) från C. Så långt det är möjligt bör du användastring
-typen från C++ snarare än den äldre C-strängstypen.
// dåligt: sträng i C-stil char* str = "Hello there";
// bra: sträng i C++-stil string str = "Hello there";
-
2-6: Konstanter: Om ett specifikt konstant värde används ofta i din kod, deklarera den som en konstant med
const
eller ett konstantuttryck medconstexpr
och referera hela tiden till den konstanten i resten av din kod istället för att referera till det motsvarande värdet.constexpr int MEANING_OF_LIFE = 42;
-
2-7: Undvik globala variabler: Deklarera aldrig en global variabel. De enda globalt deklararerade namnen i din kod bör vara
const
-deklarerade konstanter. Istället för att göra ett värde globalt, överför det som en paramter och/eller returnera det vid behov.
// dåligt int count; // global variabel; dåligt! void func1() { count = 42; } void func2() { count++; } int main() { func1(); func2(); }
-
2-1: Namn: Ge variabler deskriptiva namn, som
- 3: Grundläggande programstruktur
-
-
3-1: for vs while: Använd en
for
-loop när antalet repetitioner är känt; använd enwhile
-loop när antalet repetitioner är okänt. Föredra range-based iteration framför index om målet är något i stil med "gå igenom varje element".// upprepa exakt size/2 gånger for (int i = 0; i < size / 2; i++) { ... } // gå igenom varje User i vektorn for (User& user : set<User> loggedInUsers)) { ... } // upprepa till det inte finns flera rader while (...) { ... }
-
3-2: break och continue: I allmänhet bör du undvika att använda
break
- ellercontinue
-satser i loopar om inte absolut nödvändigt. -
3-3: if/else-mönster: Vid användning av
if/else
-satser, välj omsorgsfullt mellan de olikaif
- ochelse
-mönstren beroende på hur villkoren är relaterade till varandra. Undvik redundanta eller onödigaif
-tester.// dåligt if (grade >= 90) { cout << "You got an A!"; } if (grade >= 80 && grade < 90) { cout << "You got a B!"; } if (grade >= 70 && grade < 80) { cout << "You got a C!"; } ...
// bra if (grade >= 90) { cout << "You got an A!"; } else if (grade >= 80) { cout << "You got a B!"; } else if (grade >= 70) { cout << "You got a C!"; } ...
-
3-4: Boolesk zen 1: Om du har en
if/else
-sats som returnerar ett värde av typbool
baserat på ett test, returnera bara resultatet av testet direkt istället.// dåligt if (score1 == score2) { return true; } else { return false; }
// bra return score1 == score2;
-
3-5: Boolesk zen 2: Testa aldrig om ett värde av typ
bool
är==
eller!=
literalernatrue
ellerfalse
.// dåligt if (x == true) { ... } else if (x != true) { ... }
// bra if (x) { ... } else { ... }
-
- 4: Redundans
-
-
4-1: Minimera redundant kod: Om du upprepar samma kod fler än två gånger, hitta ett sätt att ta bort den redundanta koden så att den endast uppträder en gång. Placera den till exempel i enhjälpfunktion som anropas från båda platserna. Om den upprepade koden är nästan men inte helt lik kan det gå att låta hjälpfunktionen ta en parameter för att representera det som skiljer.
// dåligt foo(); x = 10; y++; ... foo(); x = 15; y++;
// bra helper(10); helper(15); ... void helper(int newX) { foo(); x = newX; y++; }
-
4-2: if/else-faktorering: Flytta ut gemensam kod från
if/else
-satser så att den inte upprepas.// dåligt if (x < y) { foo(); x++; cout << "hi"; } else { foo(); y++; cout << "hi"; }
// bra foo(); if (x < y) { x++; } else { y++; } cout << "hi";
-
4-3: Strukturering av funktioner: Om du har en enskild funktion som är väldigt lång, dela upp den i mindre underfunktioner. Definitionen av "väldigt lång": låt oss säga att 40-50 rader är på gränsen. Om du försöker beskriva syftet med en funktion och märker att du säger "och" väldigt ofta betyder det antagligen att funktionen gör för många saker och borde delas upp i underfunktioner.
-
- 5: Effektivitet
-
5-1: Spara resultat av dyra funktionsanrop i en variabel: Om du anropar en dyr funktion och använder resultatet många gånger, spara det resultatet i en variabel snarare än att anropa funktionen flera gånger.
// dåligt if (reallySlowSearchForIndex("abc") >= 0) { remove(reallySlowSearchForIndex("abc")); }
// bra int index = reallySlowSearchForIndex("abc"); if (index >= 0) { remove(index); }
- 6: Kommentarer
-
-
6-1: Filhuvud: Placera en deskriptiv kommentar längst upp i varje fil som beskriver filens syfte. Antag att läsaren av dina kommentarer är en intelligent programmerare som inte sett labben förut. Ditt kommentarshuvud bör inkludera åtminstone ditt namn, liuid och en kort beskrivning av uppgiften. Om uppgiften omfattar inskickning av flera filer bör respektive fils kommentarhuvud beskriva den filen/klassen och dess huvudsyfte i programmet.
-
6-2: Citera källor: Era labbar ska vara ert arbete i labbparet. Om du tagit del av några externa resurser som hjälper dig att skapa ditt program (en bok, föreläsningbilder från andra kurser, webbsidor, råd från andra personer, etc.) bör du lista dem i en kommentar i början av filen. Att inte lista sådant kan bedömas som vilseledning, och rapporteras till disciplinnämnden.
-
6-3: Funktionshuvud: Placera ett kommentarshuvud på varje funktion i din fil. Kommentaren bör beskriva vad funktionen gör.
-
6-4: Parametrar/returvärden: Om din funktion tar emot parametrar, beskriv kortfattat deras syfte och mening. Om din funktion returnerar ett värde, beskriv kortfattat vad den returnerar.
-
6-5: För-villkor/antaganden: Om din funktion gör några antaganden, som att parametrar har vissa värden, nämn detta i dina kommentarer.
-
6-6: Undantag: Om din funktion uttryckligen kastar undantag för olika förväntade klasser av fel, nämn detta i dina kommentarer. Var specifik med vilken typ av undantag du kastar och under vilka omständigheter det sker. (Till exempel, "Throws an IllegalArgumentException if the liuid passed is invalid.")
-
6-7: Taktiska kommentarer: Inuti dina funktioner, om du har kodavsnitt som är långa, komplexa eller icke-triviala, placera väl valda kommentarer nära dessa rader som beksriver vad de gör.
-
6-8: Implementationsdetaljer: Kommentarer i funktions-, klass eller fil-huvuden bör beskriva dessas syfte och beteende. Däremot ska de inte beskriva i detalj hur de är implementerade. Beskriv koden på en hög nivå. Att skriva "summera alla poäng" är mer beskrivande och lärorikt än "vi använder en for-loop för att gå igenom och lägga till alla värden till akumulatorn".
-
6-9: TODO: Ta bort alla
// TODO:
-kommentarer innan du lämnar in ett program. -
6-10: Bortkommenterad kod: Det betraktas som dålig stil att lämna in kod där stycken av koden är "bortkommenterad". Det går bra att kommentera bort kod under arbetets gång, men om programmet är färdigt och sådan kod inte behövs, ta helt enkelt bort den.
-
- 7: Funktioner och procedurell design
-
-
7-1: Design av en bra funktion: En väldesignad funktion har egenskaper som:
- Genomför fullständigt en välavgränsad uppgift.
- Gör inte för stor arbete.
- Är inte onödigt sammankopplad med andra funktioner.
- Lagrar data i så snävt definitionsområde som möjligt.
- Påvisar och underdelar strukturen i det övergripande progammet.
- Hjälper till att ta bort redundans som annars skulle finnas i det övergripande programmet.
-
7-2: Värde- vs. referensparametrar: Använd referensparametrar för att skicka 'ut' information från en funktion, eller när funktionen kan behöva ändra på värdet på paramtern som förs in, eller när funktionen behöver returnera flera värden. Använd inte referensparametrar när det inte är nödvändigt eller inte medför några fördelar. Notera att
a
,b
ochc
inte är referensparametrar till följande funktion eftersom de inte behöver vara det./* * Solves a quadratic equation ax^2 + bx + c = 0, * storing the results in output parameters root1 and root2. * Assumes that the given equation has two real roots. */ void quadratic(double a, double b, double c, double& root1, double& root2) { double d = sqrt(b * b - 4 * a * c); root1 = (-b + d) / (2 * a); root2 = (-b - d) / (2 * a); }
-
7-3: 'Ut'-referensparameter vs. return: När ett enda värde behöver skickas tillbaka från en funktion och kan tillhandahållas genom en 'ut'-referens eller ett returvärde, föredra ett returvärde.
// dåligt void max(int a, int b, int& result) { if (a > b) { result = a; } else { result = b; } }
// bra int max(int a, int b) { if (a > b) { return a; } else { return b; } }
-
7-4: Överför objekt genom referens: När du skickar ett objekt som parameter till en funktion bör du normalt överföra den via referens eftersom hela objektet måste kopieras som det överförs som värde. Kopiering av objekt är dyrt.
// dåligt void process(BankAccount account) { ... }
// bra void process(BankAccount& account) { ... }
-
7-5: Referens vs. pekare: Om du känner till pekare i C/C++ från tidigare programmeringserfarenhet, föredra att överföra parametrar som referenser över pekare så mycket som möjligt. Ett skäl till detta är att referenser, olikt pekare, inte kan anta värdet
nullptr
.// dåligt // accepts a pointer to an account void process(BankAccount* account) { ... }
// bra // accepts a reference to an account void process(BankAccount& account) { ... }
-
7-6: referens till const som parameter: Om du överför ett objekt till en funktion och din kod inte kommer att ändra på tillståndet i objektet, överför det som en referens till
const
.
// dåligt // accepts a reference to an account void display(BankAccount& account) { ... }
// bra // accepts a const reference to an account void display(const BankAccount& account) { ... }
-
7-7: Undvik kedjning, där flera funktioner anropar varandra i en kedja utan att återvända till
main
. Säkerställ attmain
är en koncis sammanfattning av ditt övergripande program. Här är ett översiktligt diagram över ett anropsflöde med (vänster) och utan (höger) kedjning:
// dåligt main | +-- function1 | +-- function2 | +-- function3 | +-- function4 | +-- function5 | +-- function6
// bra main | +-- function1 | +-- function2 | | | +-- function3 | +-- function4 | | | +-- function5 | +-- function6
-
- 8: Klassdesign
-
-
8-1: Inkapsling: Kapsla in dina objekt genom att deklarera datafälten i din klass
private
.class Student { private: int homeworkScore; ...
-
8-2: .h vs. .cpp: Placera alltid deklarationen av en klass och dess medlemmar i sin egen fil,
ClassName.h
. Placera definitionen av dessa medlemmar i en egen fil,ClassName.cpp
. Skydda alltid.h
-filens klassdeklaration med ett preprocessorblock#ifndef/define/endif
för att undvika multipla deklarationer av sama klass.// Point.h #ifndef POINT_H #define POINT_H class Point { public: Point(int x, int y); int getX() const; int getY() const; string toString() const; void translate(int dx, int dy); private: int m_x; int m_y; }; #endif // END POINT_H
// Point.cpp #include "Point.h" Point::Point(int x, int y) { m_x = x; m_y = y; } void Point::translate(int dx, int dy) { m_x += dx; m_y += dy; } ...
-
8-3: class vs. struct: Använd alltid
class
förutom om du skapar en väldigt liten och enkel datatyp som enbart behöver några få publika medlemsvariabler och kanske en konstruktor för att initialisera dem- Exempel på sådana småstruct
-typer kan vara enPoint
eller enLinkedListNode
. - 8-4: Undvik onödiga fält; använd fält för att lagra data som är viktigt för ditt objekt men inte för att lägra temporära värden som bara används inuti ett enskilt anrop till en funktion.
-
8-5: Hjälpfunktioner: Om du lägger till en medlemsfunktion till din klass som inte är en del av en uppgiftslydelse, deklarera den
private
så att utomstående kod inte kan anropa den.
class Student { ... private: double computeTuitionHelper();
-
8-6: const-medlemmar: Om en given medlemsfunktion inte ändrar det anropade objektets tillstånd, deklarera den
const
.
class Student { public: int getID() const; double getGPA(int year) const; void payTuition(Course& course); string toString() const; ...
-
Sidansvarig: Ahmed Rezine
Senast uppdaterad: 2022-08-23