Kirjoittaja: Metabolix
Kirjoitettu: 25.10.2008 – 10.08.2013
Tagit: koodi näytille, vinkki
Tämä on C++-spesifinen versio aiemmasta vinkistä Koodin jakaminen eri tiedostoihin, joka keskittyi enemmän C-ohjelmoijan näkökulmaan. Periaate on täysin sama, mutta ilmeisesti vinkin soveltaminen C++-koodiin ei ole kaikille triviaalia. Tämän vinkin tarkoitus onkin olla edellistä selkeämpi ja käsitellä kattavammin asiaa tyypillisen C++-ohjelman kannalta.
Olennaista tiedostoihin jakamisen kannalta on se, mitkä osat koodista tuottavat käännösvaiheessa välittömästi lopullista ohjelmakoodia ja mitkä vain vaikuttavat kääntäjän toimintaan. Esimerkiksi rivi int x = 1;
aiheuttaa sen, että kyseiselle muuttujalle varataan ohjelmasta tilaa ja että sen alkuarvo tallennetaan jonnekin. Sen sijaan rivi extern int x;
vaikuttaa vain kääntäjään: Nyt kääntäjä tietää, että ohjelmaan kuuluu tällainen muuttuja, jolloin sen esiintyminen koodissa on hyväksyttävää. Muuttujaa ei kuitenkaan tässä luoda, joten ohjelman linkitysvaiheessa tapahtuu virhe, jos sitä tarvittaisiin mutta sitä ei löydykään mistään.
Perinteiset funktiot käännetään heti paikalla ja sisällytetään ohjelmiin. C++ kuitenkin sisältää myös toisenlaisia funktioita, inline-funktioita, jotka nimensä mukaisesti liitetään ohjelmassa siihen, missä niitä kutsutaan, ja käännetään paikan päällä. Näin toimivat myös ne luokkien jäsenfunktiot, jotka on kirjoitettu luokkamäärittelyn sisään. Seuraavassa esimerkkiohjelmassa käsitellään erilaisia muuttujia ja funktioita C++:n näkökulmasta.
palikka.hpp, luokan otsikkotiedosto
// Varmistetaan, että otsikko tulee liitettyä vain kerran. // Huomaa ehdon lopetus tiedoston lopussa. #ifndef PALIKKA_HPP #define PALIKKA_HPP 1 // Otsikkoon kuuluvat ilmoitusluontoiset asiat kuten luokan esittely // Nämä asiat eivät itsessään vielä tuota binaariin mitään sisältöä, // mutta kääntäjä tarvitsee tiedot. Seuraavassa esimerkkejä: class palikka { private: // staattisen muuttujan esittely static int lukumaara; // jäsenmuuttujat double sivu; public: // inline-funktiot, jotka kääntäjä asettaa koodin sekaan aina kutsukohtaan; // tyypillisesti luonti- ja tuhoamisfunktiot sekä jäsenten asettamis- ja hakemisfunktiot, kuten tässä palikka() { ++lukumaara; // pidetään kirjaa palikoiden määrästä, kätevä debug-tapa } ~palikka() { --lukumaara; } double hae_sivu() const { return sivu; } void aseta_sivu(double arvo) { sivu = arvo; } // ulkoisten funktioiden esittelyt double tilavuus() const; // ulkoisen staattisen funktion esittely static int kerro_maara(); }; // Myös luokkaan kuulumattomien funktioiden esittelyt kuuluvat otsikkoon. extern bool operator < (palikka const& a, palikka const& b); // Ulkopuolisetkin inline-funktiot kirjoitetaan kokonaisuudessaan otsikkoon. // Huomaa sana "inline", joka tekee inline-funktion luokan ulkopuolella. inline bool operator > (palikka const& a, palikka const& b) { return a.tilavuus() > b.tilavuus(); } // Otsikossa esitellään myös globaalit muuttujat, huomaa sana "extern". extern palikka G; // Lopuksi suljetaan tarkistusehto (#ifndef PALIKKA_HPP) #endif
palikka.cpp, luokan kooditiedosto
// Liitetään luokkaan liittyvä otsikkotiedosto #include "palikka.hpp" // Kooditiedostoon tulevat luokan ulkoiset funktiot (ei-inline) ja staattiset jäsenet sekä globaalit muuttujat int palikka::lukumaara = 0; palikka G; // globaali palikka int palikka::kerro_maara() { return lukumaara; } double palikka::tilavuus() const { return sivu * sivu * sivu; } bool operator < (palikka const& a, palikka const& b) { return a.tilavuus() < b.tilavuus(); }
ohjelma.cpp, pääohjelman kooditiedosto
// Liitetään palikka.hpp, joka sisältää kääntäjän tarvitsemat tiedot luokasta #include "palikka.hpp" // Lisäksi tietenkin tavallisia otsikoita #include <iostream> int main(void) { using namespace std; palikka a, b; G.aseta_sivu(1); // globaali palikka a.aseta_sivu(0.5); b.aseta_sivu(1.5); cout << "sivu = " << a.hae_sivu() << " <=> tilavuus = " << a.tilavuus() << endl; cout << "sivu = " << b.hae_sivu() << " <=> tilavuus = " << b.tilavuus() << endl; cout << "Palikka a on " << (a < G ? "pienempi" : "suurempi") << " kuin palikka G." << endl; cout << "Palikka b on " << (b > G ? "suurempi" : "pienempi") << " kuin palikka G." << endl; palikka *c = new palikka(); cout << "Luotiin palikka c, palikoita on nyt " << palikka::kerro_maara() << '.' << endl; delete c; cout << "Tuhottiin c, ja jäljelle jäi vain " << palikka::kerro_maara() << '.' << endl; return 0; }
# Käännös (GCC:llä) osissa: g++ ohjelma.cpp -c -o ohjelma.o g++ palikka.cpp -c -o palikka.o g++ ohjelma.o palikka.o -o ohjelma.bin # Käännös (GCC:llä) kerralla: g++ ohjelma.cpp palikka.cpp -o ohjelma.bin # Ajaminen: ./ohjelma.bin
Tuloste:
sivu = 0.5 <=> tilavuus = 0.125 sivu = 1.5 <=> tilavuus = 3.375 Palikka a on pienempi kuin palikka G. Palikka b on suurempi kuin palikka G. Luotiin palikka c, palikoita on nyt 4. Tuhottiin c, ja jäljelle jäi vain 3.
Juu, tästä on hyötyä... :D
Sain pelaan tän avulla sen tiedostojaon, nyt peli on jo paljon edistynyt :D
Suosittelen vielä opettelemaan nimeämiset heti alkumetreillä. (Mod. siirsi nimeämiskeskustelun keskustelualueelle.)
Kieli- ja nimeämiskysymykset ovat mielipidekysymyksiä, joihin ei ole oikeaa vastausta. Koodivinkki on kirjoitettu suomen kielellä, jotta nuortenkin suomalaisten on helpompi lukea sitä. Nimet koodissa on kirjoitettu samanlaisella tyylillä kuin C++:n standardikirjastossa (eli pienillä kirjaimilla ja tarvittaessa alaviivoja välimerkkeinä käyttäen), ja olisi aika tyhmää väittää, että kielen kehittäjä olisi ollut väärässä nimeämiskäytännöstä. Saat itse tehdä eri tavalla, mutta älä väitä, että oma tapasi olisi ainoa oikea totuus.