Kirjautuminen

Haku

Tehtävät

Kilpailu

Ohjelmoi tekoäly!
Tulokset on julkaistu.
Onnea voittajalle!

Koodivinkit: C++: Koodin jakaminen eri tiedostoihin

Kirjoittaja: Metabolix; viimeksi muokattu 10.08.2013.

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.

Kommentit

vehkis91 [25.10.2008 14:56:06]

Lainaa #

Juu, tästä on hyötyä... :D

vehkis91 [26.10.2008 16:11:01]

Lainaa #

Sain pelaan tän avulla sen tiedostojaon, nyt peli on jo paljon edistynyt :D

Lintu [12.11.2008 02:15:48]

Lainaa #

Suosittelen vielä opettelemaan nimeämiset heti alkumetreillä. (Mod. siirsi nimeämiskeskustelun keskustelualueelle.)

Metabolix [12.11.2008 20:55:00]

Lainaa #

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.

Kirjoita kommentti

Muista lukea keskustelun ohjeet.
Tietoa sivustosta