Tyyliseikkoja¶
C++:lla on mahdollista kirjoittaa hyvinkin kryptistä koodia, mutta turhaa kryptisyyttä on syytä välttää. Ennemminkin tulee pyrkiä kirjoittamaan selkeää koodia, jota on helppo lukea ja ymmärtää. Tätä kautta myös ohjelmien ylläpito helpottuu.
Edellä tuli mainittua joitakin ohjelmien laatuvaatimuksia (selkeys, luettavuus, ymmärrettävyys, ylläpidettävyys). Muita tärkeitä asioita ovat ohjelman toimivuus ja tehokkuus. Laatuvaatimuksille ominaista on, että ei ole yhtä ainoaa oikeaa tapaa kirjoittaa tyylikästä koodia.
Alla olevissa kohdissa luetellaan tällä kurssilla käytössä olevia tyylisääntöjä, joihin projektien arvostelussa kiinnitetään huomiota. Tavoitteena toki on esittää mahdollisimman yleispäteviä sääntöjä, joista olisi hyötyä laajemminkin kuin vain tällä kurssilla.
Toimivuus¶
Ohjelman toimivuuden voi ajatella olevan kaikkein olennaisin laatuvaatimus. Mutta jos ohjelma toimii oikein, onko sitten mitään merkitystä, miltä ohjelma näyttää tai miten ymmärrettävää koodi on? Kuitenkin jos ohjelman rakenne on looginen ja mahdollisimman yksinkertainen, se on myös helpompi saada toimimaan oikein.
Alla olevasta listasta löytyy joitakin toimivuuteen liittyviä vaatimuksia:
- Ohjelman on toimittava oikein kaikilla sallituilla syötteillä.
- Ohjelman on ilmoitettava virheellisistä syötearvoista.
- Jos ohjelmaa yritetään käyttää jotenkin väärin, ohjelman on varoitettava käyttäjää.
- Tarkistusten on oltava riittävän kattavia, niin että ohjelma ei kaadu millään syötearvoilla vaan päättyy hallitusti.
- Ohjelman ei pidä kysyä sellaisia tietoja, jotka se voi itse laskea.
- Ohjelman käytön pitää olla mahdollisimman helppoa ja havainnollista.
Strukturointi¶
Strukturoinnilla tarkoitetaan tapaa jakaa ohjelma osiin. Tarkoituksena on, että nämä osat ovat riittävän pieniä ja yksinkertaisia sekä helposti hallittavia.
Strukturointi koskee mitä tahansa ohjelman osaa, esimerkiksi funktioita ja luokkia.
Erityisesti funktiot eivät saa olla liian pitkiä eivätkä myöskään liian leveitä (rivit eivät saa olla liian pitkiä). Funktion pitäisi mahtua näytölle kokonaan.
Toisteista koodia pitää välttää. Tällä tarkoitetaan saman tai melkein saman koodin kirjoittamista useaan kertaan. Toisteista koodia voi välttää esimerkiksi funktioiden avulla tai parametrisoimalla funktioita.
Nimeäminen¶
C++ on merkkikokoriippuvainen (case-sensitive), eli isot ja pienet
kirjaimet tulkitaan eri merkeiksi.
Vaikka siis olisikin mahdollista käyttää tunnisteita
nimi
ja Nimi
samassa näkyvyysalueessa, niin ohjelmointityylin
kannalta tämä ei ole hyvä ratkaisu.
Muutenkin liian samanlaisia nimiä kannattaa välttää, koska ne voi
helposti sekoittaa toisiinsa.
Muuttujien, vakioiden, funktioiden, tyyppien, luokkien ym. tunnisteiden nimien tulee olla kuvaavia. Ei haittaa, vaikka nimet olisivat pitkiä.
Tyypillisesti muuttujien ja funktioiden nimet alkavat pienellä kirjaimella,
luokkien ja tietueiden (struct
) nimet taas isolla kirjaimella.
Vakiot kirjoitetaan kokonaan isoilla kirjaimilla ja erottimena käytetään
alaviivaa.
Funktioiden nimeämisessä käytetään pääsääntöisesti kahta tapaa:
printField
ja print_field
.
Näistä ensiksi mainittu on suositeltavampi, mutta joka tapauksessa
samassa ohjelmassa tulee käyttää yhtenäistä tyyliä.
Funktioiden niminä tulee käyttää komentoja kuten print
(tai tulosta
) eikä substantiiveja kuten printing
(tai tulostaminen
).
Totuusarvoisten (bool
) funktioiden nimien suositellaan alkavan
merkkijonolla is
(tai onko
).
Tällöin jo nimestä voi päätellä, missä tilanteessa funktio palauttaa
arvon true
ja missä arvon false
.
Tästä syystä esimerkiksi isReady
on parempi nimi totuusarvoiselle
funktiolle kuin checkStatus
.
Funktio voi kuitenkin palauttaa totuusarvon tietona siitä, onnistuiko
kyseinen operaatio vai ei.
Tällöin edellä kuvattua totuusarvoisten funktioiden nimeämissääntöä
ei tarvitse noudattaa.
Esimerkiksi funktion setValue
pääasiallisena tarkoituksena on asettaa
arvo, mutta se voi silti palauttaa totuusarvon: arvon true
operaation onnistuessa ja arvon false
muulloin.
Luokkien niminä tulee käyttää yksikkömuotoa mieluummin kuin monikkoa, sillä luokka yleensä kuvaa yhtä oliota.
Jäsenmuuttujien nimissä on hyvä käyttää viimeisenä merkkinä alaviivaa. Näin ne on helpompi erottaa muista muuttujista.
Nimettyjä vakioita pitäisi käyttää taikanumeroiden sijaan.
Koodin lukeminen on helpompaa, jos siinä esiintyy vaikkapa vakio
NUMBER_OF_STUDENTS
numeron 600
sijasta.
Nimettyjen vakioiden etuna on myös se, että jos vakion arvoa halutaan
muuttaa, muutoksen joutuu tekemään vain yhteen kohtaan koodissa.
Myös vakioilla pitää olla käyttötarkoitusta kuvaavat nimet,
ei siis ole järkevää määritellä: const int HUNDRED = 100
.
Kommentointi¶
Kommenteilla voidaan parantaa koodin ymmärrettävyyttä. Toisaalta ymmärrettävyyttä voidaan parantaa myös sillä, että muuttujille annetaan kuvaavat nimet. Tästä löytyy esimerkki Steve McConnellin kirjasta Code Complete (s. 473):
// if allocation flag is zero
if( AllocFlag == 0 ) ...
Yllä oleva kommentti on täysin turha, koska saman asian voi lukea koodista. Kommenttia voi yrittää parantaa seuraavasti:
// if allocating new member
if( AllocFlag == 0 ) ...
Parempi tapa olisi kuitenkin käyttää nimettyä vakiota nollan sijasta:
// if allocating new member
if( AllocFlag == NEW_MEMBER ) ...
Toisaalta nyt kommentti tulee taas turhaksi (mikä on nyt hyvä asia, ja kommentti voidaan poistaa).
Koodia kommentoidessaan on hyvä pysähtyä miettimään, onko kommentista hyötyä. Toisaalta tässä vaiheessa, kun ohjelmointia vasta opetellaan, on hyvä kommentoida liikaa kuin liian vähän.
Ohjelmassa esiintyvät funktiot tulee kommentoida.
Erityisesti jos kyseessä on luokan jäsenfunktio, kommentti
kirjoitetaan otsikkotiedostoon (.hh
) funktion yläpuolelle.
Kommentista tulee selvitä, mitä funktio tekee,
mitkä ovat sen parametrien ja mahdollisen paluuarvon merkitykset
sekä mahdolliset arvot.
Vastaavasti myös kunkin tiedoston alussa tulee olla koko tiedostoa koskeva kommentti. Usein myös kontrollirakennetta on hyvä edeltää kommentti, jossa kerrotaan kontrollirakenteen tarkoitus.
Koodin asettelu¶
Vaikka C++ ei vaadikaan sisennyksiä, niitä tulee käyttää luettavuuden parantamiseksi.
Kontrollirakenteissa käytetään koodilohkon alku- ja loppusulkuja
(eli aaltosulkuja: { }
) aina, vaikka kyseisen kontrollirakenteen
rungossa olisi vain yksi lause.
Koodilohkon alku- ja loppusulut voi sijoittaa omille riveilleen,
tai alkusulun voi kirjoittaa edellisen rivin loppuun.
Voit käyttää kumpaa tahansa tapaa, kunhan käytät samaa tapaa
kautta koko ohjelman.
Olio-ohjelmointi¶
Tämän kohdan tyyliseikat liittyvät erityisesti luokkiin ja olio-ohjelmointiin.
Tämän ja seuraavan kohdan tyyliseikat on valikoitu Matti Rintalan ja Jyke Jokisen kirjasta Olioiden ohjelmointi C++:lla (ss. 341-353). Useimmat niistä on mainittu ja selitetty laajemmin muualla materiaalissa, joten tässä ne vain luetellaan lyhyesti.
Yhdessä otsikkotiedostossa (
.hh
) on yksi julkinen rajapinta (luokka). Otsikkotiedosto sisältää vain esittelyitä (ei toteutuksia).Otsikkotiedosto on suojattava moninkertaiselta käyttöönotolta:
#ifndef CLASS_A_HH #define CLASS_A_HH ... #endif // CLASS_A_HH
include
-direktiivillä saa ottaa käyttöön vain otsikkotiedostoja. Jos otetaan käyttöön useita tiedostoja, ensin luetellaan omat otsikkotiedostot ja sen jälkeen kirjaston tiedostot.Rajapinnan toteutukset esitetään toteutustiedostossa (
.cpp
) samassa järjestyksessä kuin ne ovat vastaavassa otsikkotiedostossa (.hh
).Luokalle on määritelty hyvä julkinen rajapinta (mahdollisimman pieni), ja kaikki operaatiot tapahtuvat tämän rajapinnan kautta.
- Erityisesti olio ei saa muuttaa toisen olion attribuuttien arvoja, ei vaikka oliot olisivat saman luokan ilmentymiä.
Luokan
public
-osa kirjoitetaan ennenprivate
-osaa.Luokan
public
-osassa ei ole jäsenmuuttujia.Luokan
private
-osassa ei ole tarpeettomia muuttujia.Luokan operaatioita ei tehdä jäsenfunktioiden ulkopuolella.
Luokan jäsenmuuttujat alustetaan joko niiden esittelyn yhteydessä tai rakentajan alustuslistassa, jossa ne luetellaan siinä järjestyksessä kuin ne on esitelty otsikkotiedostossa.
Jäsenfunktiosta tehdään vakiojäsenfunktio (
const
-funktio) aina kun mahdollista.
Muita tyyliseikkoja¶
Lopuksi luetellaan lyhyesti vielä joitakin yleisiä tyylisääntöjä.
Muuttujien näkyvyysalue tulee suunnitella mahdollisimman pieneksi (esimerkiksi koodilohko, funktio, luokka).
Jokainen muuttuja on määriteltävä eri lauseessa. Vältä siis muotoa
int a, b;
olevia esittelyitä.Ohjelmassa ei saa olla alustamattomia muuttujia.
Osoittimen ja viitteen merkit kirjoitetaan heti tyypin nimen perään ilman välilyöntiä. Esimerkiksi
Date* ptr = nullptr; void printDate( Date& dateObject );
Funktion
main
paluuarvo on ainaint
.Funktion parametrien nimet on annettava sekä esittelyssä että määrittelyssä, ja niiden on oltava molemmissa samat.
Funktion olioparametrin välitykseen käytetään viitettä aina kun mahdollista.
Funktion mahdolliset oletusarvoiset parametrit ovat näkyvissä esittelyssä, eikä niitä saa lisätä määrittelyssä.
Funktio ei saa palauttaa osoitinta tai viitettä sen omaan paikalliseen dataan.
Tietueella (
struct
) ei saa olla jäsenfunktioita, ainoastaan rakentajat, (tyhjä) purkaja sekä operaattorifunktiot kuitenkin sallitaan.Älä indeksoi vektoreita hakasuluilla, vaan käytä operaatiota
at
. (Sama pätee muillekin säiliöille, joille kyseiset operaatiot on määritelty.)Vältä turhaa monimutkaisuutta. Esimerkiksi totuusarvoisen lausekkeen arvoa ei yleensä tarvitse tutkia, vaan kyseinen totuusarvo voidaan palauttaa suoraan. Konkreettisena esimerkkinä funktio
bool is_negative(int number) { if(number < 0) { return true; } else { return false; } }
voidaan kirjoittaa yksinkertaisemmin:
bool is_negative(int number) { return number < 0; }
(jolloin tässä tapauksessa herää kysymys, onko funktio edes tarpeellinen.)
Ellet ole ehdottoman varma, että osaat käyttää C++:n poikkeusmekanismia, niin älä yritä käyttää sitä. C++:n poikkeusten käsittely opetetaan vasta seuraavalla ohjelmointikurssilla. Erityisesti C++:n poikkeusmekanismia ei pidä käyttää kontrollirakenteiden sijasta (kuten olet ehkä tehnyt Python-ohjelmissa).
C++:ssa on muitakin tyylisääntöjä, jotka liittyvät esimerkiksi dynaamiseen muistinhallintaan ja luokkien periytymiseen. Näihin palataaan kyseisten aiheiden kohdalla.
Attention
Kurssin koodipohjissa ja esimerkeissä olemme pyrkineet noudattamaan yllä kerrottuja tyylisääntöjä. Aina emme ole onnistuneet, sillä koodeja ovat kirjoittaneet useat eri henkilöt usein kovassa kiireessä. Toivomme, että ilmiannat tyylisääntöjen rikkeet.