Kirjautuminen

Haku

Tehtävät

Oppaat: C++-ohjelmointi: Osa 10 - Luokkien erikoiset jäsenet

  1. Osa 1 - Johdanto
  2. Osa 2 - Vakiot, muuttujat ja perustietotyypit
  3. Osa 3 - Ehdot, silmukat ja poikkeukset
  4. Osa 4 - Rakenteet, taulukot ja merkkijonot
  5. Osa 5 - Funktiot
  6. Osa 6 - Esittelyt, määrittelyt ja elinajat
  7. Osa 7 - Viittaukset, osoittimet ja dynaaminen muisti
  8. Osa 8 - Mallit
  9. Osa 9 - Luokkien perusteet
  10. Osa 10 - Luokkien erikoiset jäsenet
  11. Osa 11 - Luokkien perintä
  12. Liite 1 - C++-kehitysympäristöt
  13. Liite 2 - Valmis C++-kääntäjäpaketti

Kirjoittaja: Metabolix (2011).

Edellisessä oppaassa luokat esiteltiin melko yksinkertaisella tasolla. Seuraavaksi tutkitaan luokkien erikoisempia jäseniä ja ominaisuuksia.

Muodostin ja tuhoaja

Luokilla voi olla kahdenlaisia erityisfunktioita: yksi tai useampia muodostimia (engl. constructor, "konstruktori") sekä yksi tuhoaja (engl. destructor; "destruktori"). Nämä poikkeavat tavallisista funktioista sekä ulkonäöltään että toiminnaltaan.

Muodostin on funktio, joka suoritetaan, kun uusi olio luodaan. Sen nimi on aina sama kuin luokan nimi eikä sille merkitä paluuarvon tyyppiä. Muodostimelle annetaan usein parametreina alkuarvoja olion jäsenille, ja muodostimen alussa voi olla alustuslista, jossa arvot sijoitetaan jäsenmuuttujiin jo ennen varsinaisen muodostinfunktion ajamista. Funktiossa voi tehdä myös muita välttämättömiä toimenpiteitä, jotka täytyy tehdä ennen olion käyttämistä; esimerkiksi kuvankäsittelyyn liittyvän luokan muodostin voisi ladata kuvan tiedostosta. Luokalla voi olla useita erilaisia muodostimia. Jos luokalle kirjoitetaan yksikin muodostin, kaikki luokan edustajat täytyy luoda muodostinten avulla, eli aiempi alkuarvojen antaminen aaltosuluissa ei enää toimi.

Kun olion elinaika päättyy, kutsutaan automaattisesti sen tuhoajaa. Tällä funktiolla ei ole parametreja eikä paluuarvoa. Tuhoajan nimenä on aina ~-merkki ja luokan nimi, esimerkiksi ~testi.

Aiemmin esitellyillä luokilla piste ja jana voisi olla vaikkapa seuraavanlaiset muodostimet ja tuhoajat:

#include <iostream>
#include <cmath>

struct piste {
	float x, y;

	// Muodostimella ei ole omaa nimeä, vaan sitä merkitään aina luokan
	// nimellä. Jos jäsenet alustetaan alustuslistalla, lista kirjoitetaan
	// ennen funktion varsinaista koodia. Lista alkaa kaksoispisteestä,
	// jäsenen alkuarvo annetaan suluissa jäsenen nimen perässä ja jäsenet
	// erotetaan toisistaan pilkulla. Tässä alustetaan jäsenet nolliksi.
	piste(): x(0), y(0) {
		std::cout << "piste() eli (0, 0)." << std::endl;
	}

	// Muodostimella voi olla myös parametreja.
	// Tämän toisen muodostimen parametrit ovat jäsenten x ja y alkuarvot.
	// Parametreja voi käyttää jo alustuslistassa jäsenten alustamiseen.
	piste(float x_alku, float y_alku): x(x_alku), y(y_alku) {
		std::cout << "piste(" << x << ", " << y << ")." << std::endl;
	}

	// Jokaiselle luokalle määritellään automaattisesti kopiomuodostin,
	// jonka parametrina on olio, josta kaikki tiedot kopioidaan uuteen.
	// Kopiomuodostimen voi määritellä myös itse, jos siinä pitää tehdä
	// jotain erikoista. Tämän luokan oletuskopiomuodostin toimisi näin:
	piste(const piste& t): x(t.x), y(t.y) {
		// Tyhjä funktio.
	}

	// Tuhoajallakaan ei ole omaa nimeä, vaan merkintä on ~ ja luokan nimi.
	// Tuhoajalla ei koskaan ole parametreja.
	~piste() {
		std::cout
			<< "~piste() eli tuhoaja pisteelle "
			<< "(" << x << ", " << y << ")." << std::endl;
	}
};

struct jana {
	piste a, b;

	// Janan muodostimelle voi antaa viittaukset kahteen pisteeseen.
	// Jos toinen piste jätetään antamatta, se luodaan piste-luokan
	// oletusmuodostimella. Alustuslistassa kopioidaan parametrit
	// jäseniin eli käytetään piste-luokan kopiomuodostinta.
	jana(piste const& a0, piste const& b0 = piste()): a(a0), b(b0) {
		std::cout << "jana(a, b): " << pituus() << std::endl;
	}

	// Janan toinen muodostin voisi ottaa suoraan pisteiden koordinaatit.
	// Alustuslistassa muodostetaan koordinaateista pisteitä piste-luokan
	// toisella muodostimella.
	jana(float x0, float y0, float x1, float y1): a(x0, y0), b(x1, y1) {
		std::cout << "jana(x0, y0, x1, y1): " << pituus() << std::endl;
	}

	// Janalle tulee automaattisesti kopiomuodostin.
	// jana(jana const& t): a(t.a), b(t.b) { }

	// Pituusfunktio...
	float pituus() const {
		float dx = (b.x - a.x), dy = (b.y - a.y);
		return std::sqrt(dx * dx + dy * dy);
	}
};

int main() {
	// Lohkot määräävät elinajan, kuten aiemmin opittiin.
	// Tämän if-lauseen ehto on aina tosi.
	if (true) {
		// Nyt pisteiden alustamiseen täytyy käyttää muodostimia.
		// Oletusmuodostinta kutsutaan, jos ei erikseen käytetä muuta.
		std::cout << "Luodaan piste oletusmuodostimella." << std::endl;
		piste a;
		std::cout << "Tämän pisteen elinaika loppuu pian." << std::endl;
	}

	// Toinen muodostin; argumentit ovat suluissa olion nimen perässä.
	std::cout << std::endl;
	std::cout << "Luodaan piste a koordinaateista." << std::endl;
	piste a(2.5, 3.1);

	// Muodostin, jonka parametrina on toinen saman luokan olio.
	// Kääntäjä luo tällaisen automaattisesti, jos ohjelmoija ei
	// itse kirjoita vastaavaa.
	std::cout << std::endl;
	std::cout << "Luodaan piste b oletusmuodostimella." << std::endl;
	piste b(a);
	b.x = 100;

	// Janalle ei määritelty oletusmuodostinta mutta määriteltiin kuitenkin
	// muita muodostimia, joten sitä ei voi luoda ilman parametreja!
	// Janaa varten tarvitaan pisteitä, koordinaatteja tai vanha jana.
	std::cout << std::endl;
	std::cout << "Luodaan jana j1 koordinaateista." << std::endl;
	std::cout << "Janalle luodaan samalla omat pisteet." << std::endl;
	jana j1(2, 2, 5, 6);

	std::cout << std::endl;
	std::cout << "Luodaan jana j2 vanhoista pisteistä." << std::endl;
	std::cout << "Uudet pisteet luodaan kopiomuodostimella." << std::endl;
	std::cout << "Janankin pisteet tuhotaan ohjelman lopussa!" << std::endl;
	jana j2(a, b);

	std::cout << std::endl;
	std::cout << "Luodaan jana j3 kopioimalla jana j2." << std::endl;
	jana j3(j2);

	// Sijoitetaan pisteisiin erikoisia arvoja, jotta nähdään helpommin
	// lopussa tulosteesta, missä järjestyksessä pisteet tuhotaan.
	a.x = a.y = 123;
	b.x = b.y = 456;
	j3.a.x = j3.a.y = 135;
	j3.b.x = j3.b.y = 246;

	// Funktion lopussa pisteiden ja janojen elinaika päättyy ja ne
	// tuhoutuvat. Tuhoajaa kutsutaan automaattisesti. Tällä kertaa
	// tuhotaan seuraavat oliot:
	// j3.b,  j3.a,  j3;   j2.b,  j2.a,  j2;   j1.b,  j1.a,  j1;   b,  a.
	std::cout << std::endl;
	std::cout << "Nyt tulee loppu." << std::endl;
}

Dynaaminen muistinhallinta

Olioiden luominen dynaamisesti ajon aikana toimii samalla tavalla kuin muukin dynaaminen muistinhallinta. Kun oliot luodaan new-operaattorilla, jokaiselle niistä kutsutaan muodostinta, ja vastaavasti delete-operaattorilla tuhottaessa kutsutaan jokaiselle tuhoajaa. Jos olio sisältää muita olioita (kuten jana-olio sisälsi kaksi piste-oliota), näidenkin elinaika päättyy samalla kertaa.

#include <iostream>

struct sisa {
	// Oletusmuodostin.
	sisa() {
		std::cout << "Muodostin sisa()." << std::endl;
	}
	// Muodostin kokonaislukuparametrilla.
	sisa(int x) {
		std::cout << "Muodostin sisa(" << x << ")." << std::endl;
	}
	~sisa() {
		std::cout << "Tuhoaja ~sisa()." << std::endl;
	}
};

struct ulko {
	sisa a, b;
	// Muodostin kokonaislukuparametrilla; parametri välitetään
	// jäsenen a muodostimelle. Jäsen b muodostetaan automaattisesti
	// oletusmuodostimella.
	ulko(int x): a(x) {
		std::cout << "Muodostin ulko(" << x << ")." << std::endl;
	}
	~ulko() {
		std::cout << "Tuhoaja ~ulko()." << std::endl;
	}
	void koe() {
		std::cout << "Funktio ulko::koe()." << std::endl;
	}
};

int main() {
	// Luodaan uusi olio, kutsutaan yhtä sen jäsenfunktiota ja tuhotaan se.
	ulko* u = new ulko(123);
	u->koe();
	delete u;

	// Luodaan toinenkin olio ja tuhotaan se saman tien.
	delete new ulko(456);
}

Luokan yhteiset jäsenet

Luokka voi sisältää jäseniä, jotka ovat kaikkien olioiden yhteisiä. Nämä staattiset eli pysyvät jäsenet merkitään sanalla static. Staattiset jäsenmuuttujat täytyy esittelyn lisäksi määritellä erikseen luokan ulkopuolella, ja ne ovat olemassa ohjelman alusta loppuun riippumatta siitä, luodaanko luokasta yhtään oliota. Vakiot ovat poikkeus sääntöön: niiden kohdalla erillinen määrittely ei ole välttämätön, vaan kaiken voi kirjoittaa luokan sisään.

#include <iostream>
#include <stdexcept>

struct piste {
	// Kaikki staattiset jäsenet esitellään static-sanan kanssa.
	// Staattiset muuttujat esitellään luokan sisällä, mutta
	// määrittely ja alustus ovat luokan ulkopuolella.
	static char vapaa_kirjain;

	// Staattinen vakio voidaan kirjoittaa suoraan luokan sisään:
	static const char viimeinen_kirjain = 'Z';

	// Staattinen funktio ei juuri eroa näöltään muista jäsenfunktioista.
	static bool kirjaimia_vapaana() {
		// Staattisessa jäsenfunktiossa voi käyttää vain
		// staattisia jäsenmuuttujia, koska funktiota ei
		// kutsuta minkään olion kautta.

		// Ilmoitetaan, onko kirjaimia vapaana.
		// Nyt käytössä ovat kirjaimet A-Z.
		return vapaa_kirjain <= viimeinen_kirjain;
	}
	// Tämän funktion määrittely on luokan ulkopuolella.
	static char varaa_kirjain();

	// Muut jäsenet: pisteen paikka, kirjain, muodostin ja tuhoaja.
	int x, y;
	char kirjain;

	// Muodostin asettaa pisteen annettuun kohtaan ja varaa sille kirjaimen.
	piste(int _x, int _y): x(_x), y(_y), kirjain(varaa_kirjain()) {
		std::cout
			<< "Piste " << kirjain << " sijaitsee kohdassa ("
			<< x << ", " << y << ")." << std::endl;
	}

	// Tuhoaja kertoo vain, mitä tuhotaan.
	~piste() {
		std::cout << "Piste " << kirjain << " tuhoutuu." << std::endl;
	}
};

// Staattinen jäsenmuuttuja pitää määritellä erikseen.
// Muuttujan nimen edessä on luokan nimi ja kaksi kaksoispistettä.
char piste::vapaa_kirjain = 'A';

// Staattisenkin funktion voi määritellä luokan ulkopuolella.
char piste::varaa_kirjain() {
	// Staattinen funktio voi käyttää luokan muita staattisia funktioita.
	if (!kirjaimia_vapaana()) {
		// Jos kirjaimet loppuivat, heitetään asianmukainen poikkeus.
		// std::runtime_error on eräs standardikirjaston virheluokka,
		// jonka muodostin ottaa parametrinaan virheilmoituksen.
		throw std::runtime_error("Kirjaimet loppuivat!");
	}

	// Korotetaan vapaata kirjainta mutta palautetaan vanha arvo.
	// (Muistele merkintöjen  ++x  ja  x++  ero 2. osasta!)
	return vapaa_kirjain++;
}

int main() {
	piste p1(1, 1);
	piste p2(2, 3);
	piste p3(5, 8);
}

Vakiot ja viittaukset jäseninä

Luokan jäseninä voi olla myös vakioita ja viittauksia. Niihin pätevät silti samat säännöt kuin ennenkin: vakioiden ja viittauksien arvot täytyy asettaa heti eikä niitä voi muuttaa. Luokkien kanssa tämä tarkoittaa, että nämä jäsenet on alustettava muodostimen alustuslistassa (tai aaltosulkumerkinnällä, jos muodostinta ei ole). Koska vakioita ja viittauksia ei voi jälkikäteen muuttaa, sellaisia sisältävää oliota ei voi myöskään kopioida toiseen tavallisella =-operaattorilla.

#include <iostream>

struct luokka {
	const int vakio;
	int &x, &y, &z;
	int z1, z2;

	// Vakiot ja viittaukset on alustettava alustuslistassa. Vakion arvoksi
	// voi asettaa mitä tahansa. Viittauksen arvo taas täytyy käytännössä
	// välittää muodostimeen viittauksen tai osoittimen avulla, ellei sitä
	// alusteta viittaamaan luokan omaan jäseneen.
	luokka(int& _x, int* _y, bool _kumpi):
		vakio(_x * 123),     // Vakion alustus
		x(_x),               // Viittaus toisesta viittauksesta
		y(*_y),              // Viittaus osoittimen osoittamaan muuttujaan
		z(_kumpi ? z1 : z2), // Viittaus omaan jäseneen, parametrista riippuen z1 tai z2.
		z1(5), z2(7) {
	}

	void muuta() {
		x *= 11;
		y *= 16;
		z *= 64;
	}

	void tulosta() {
		// Tulostetaan jäsenten muistiosoitteet ja arvot. Viittauksen
		// osoitteeksi tulostuu viitatun muuttujan osoite, joten
		// osoitteita vertailemalla nähdään, mihin muuttujiin ne
		// viittaavat.
		std::cout << "&x  = " << &x  << ", x  = " << x  << std::endl;
		std::cout << "&y  = " << &y  << ", y  = " << y  << std::endl;
		std::cout << "&z  = " << &z  << ", z  = " << z  << std::endl;
		std::cout << "&z1 = " << &z1 << ", z1 = " << z1 << std::endl;
		std::cout << "&z2 = " << &z2 << ", z2 = " << z2 << std::endl;
	}
};

int main() {
	int i = 1, j = 2;

	std::cout << "Tulostetaan a:" << std::endl;
	luokka a(i, &j, true);
	a.tulosta();

	std::cout << "Muutetaan a:ta..." << std::endl;
	a.muuta();
	a.tulosta();

	// Luodaan a:sta kopio b. Myös viittaukset kopioidaan; erityisesti
	// kannattaa muistaa, että b.z viittaa samaan kuin a.z eli edelleen
	// olion a jäseneen z1 tai z2!
	std::cout << "Luodaan kopio b:" << std::endl;
	luokka b(a);
	b.tulosta();

	std::cout << "Muutetaan b:tä..." << std::endl;
	b.muuta();
	b.tulosta();

	std::cout << "Tulostetaan a:" << std::endl;
	a.tulosta();

	std::cout << "Mitä tapahtui?" << std::endl;

	// Lopuksi voit kokeilla, millaisia virheilmoituksia seuraava rivi tuottaisi:
	// a = b;
}

Operaattorit

Tavallisten jäsenfunktioiden lisäksi luokkaan voi määritellä joitakin operaattoreita kuten yhteenlaskun ja yhtäsuuruusvertailun. Operaattorien avulla luokat saadaan istumaan entistä paremmin tavallisten muuttujien joukkoon: esimerkiksi standardikirjaston tekstiluokka std::string sisältää yhteenlasku- ja vertailuoperaattorit, joiden ansiosta tekstejä voi käsitellä paljon näppärämmin kuin pitkillä funktiokutsuilla. Erityisen hyvin operaattorit esiintyvät edukseen matematiikan kuten vektori- ja matriisilaskujen yhteydessä, kuten tässä koodivinkissä.

Operaattorit eivät toiminnaltaan eroa tavallisista funktioista; vain merkintätapa on erilainen. Kun normaalisti kirjoitettaisiin a.funktio(b), esimerkiksi +-operaattorin kohdalla kirjoitetaankin a + b. Operaattorin esittely ja määrittely toimivat tavallisen funktion tapaan, mutta funktion nimenä on esimerkiksi operator +. Erikoisempia operaattoreita ovat taulukon tapaan toimiva hakasulkuoperaattori (kuten olio[i]) ja funktiokutsulta näyttävä sulkuoperaattori (kuten olio(parametri)) sekä operaattorit, jotka mahdollistavat muunnoksen toiseen tietotyyppiin (kuten (int)olio). Seuraava luokka sisältää malliksi muutaman erilaisen operaattorin:

#include <iostream>
#include <string>

struct esimerkki {
	int numero;

	// Muodostin alustaa ainoan jäsenen.
	esimerkki(int n = 0): numero(n) {
	}

	// Yhteenlasku ym. tutut perusoperaattorit palauttavat yleensä uuden olion,
	// mutta muutakin saa palauttaa. Operaattorista voi määritellä myös eri versioita
	// erilaisille parametreille, kuten muistakin funktioista.
	// a + b  on sama kuin  a.operator+(b)
	esimerkki operator + (esimerkki const& toinen) const {
		esimerkki uusi;
		uusi.numero = numero + toinen.numero;
		std::cout << numero << " ynnä " << toinen.numero << " on " << uusi.numero << "." << std::endl;
		return uusi;
	}

	// Hakasulkuoperaattorin parametri on hakasuluissa oleva asia.
	bool operator [] (int x) const {
		int bitti = (numero >> x) & 1;
		std::cout << "Luvun " << numero << " nollannesta laskettuna " << x << ". bitti on " << bitti << "." << std::endl;
		return bitti == 1;
	}

	// Sijoitusoperaattorin paluuarvona on yleensä const-viittaus olioon itseensä.
	// a = b  on sama kuin  a.operator=(b)
	esimerkki const& operator = (esimerkki const& toinen) {
		std::cout << numero << " korvataan numerolla " << toinen.numero << "." << std::endl;
		numero = toinen.numero;

		// this on osoitin, joten *this viittaa tähän olioon.
		return *this;
	}

	// Operaattorit ! ja ~ kohdistuvat vain olioon itseensä.
	bool operator ! () const {
		std::cout << "Operaation !" << numero << " totuusarvo on " << (!numero ? "true" : "false") << "." << std::endl;
		return !numero;
	}

	// Funktiokutsun kaltainen ()-operaattori kelpuuttaa montakin parametria.
	int operator () (int a, int b, int c) const {
		int tulos;
		std::string apu;
		switch (numero) {
			case 1: tulos = a; apu = "ensimmäisenä"; break;
			case 2: tulos = b; apu = "toisena"; break;
			case 3: tulos = c; apu = "kolmantena"; break;
			default: return 0;
		}
		std::cout << "Luvuista " << a << ", " << b << " ja " << c << " on tuossa " << apu << " " << tulos << "." << std::endl;
		return tulos;
	}

	// Tyypinmuunnosoperaattorin määrittely poikkeaa hieman muista:
	// paluuarvon tyyppi tulee vasta operator-sanan jälkeen.
	operator bool () const {
		std::cout << "Muunnos (bool) " << numero << " tuottaa arvon " << ((bool) numero ? "true" : "false") << "." << std::endl;
		return (bool) numero;
	}
};

int main() {
	esimerkki a(1), b(10), c(100);

	// Operaattorit toimivat aivan samalla tavalla kuin muillakin muuttujilla.
	c = a + b;
	if (c[3] && !b == false && (bool) a) {
		a(3, 5, 7);
	}

	// Kaiken voi kirjoittaa myös vaikeammin käyttämällä operaattoria tavallisen jäsenfunktion tapaan.
	std::cout << "Uudestaan:" << std::endl;
	c.operator=(a.operator+(b));
	if (c.operator[](3) && b.operator!() == false && a.operator bool()) {
		a.operator()(3, 5, 7);
	}
}

Operaattorit luokkien ulkopuolella

Operaattoreita voi kirjoittaa myös luokkien ulkopuolelle. Silloin operaattori ei ole minkään luokan jäsen, joten esimerkiksi äsken yhdellä parametrilla selvinnyt +-operaattori tarvitsee kaksi parametria, vasemman ja oikean puolen. Joskus operaattorit on selvempi kirjoittaa näin, ja joskus luokan jäseneksi kirjoittaminen ei ole edes mahdollista, esimerkiksi siinä tapauksessa, että luokka ei ole itse tehty. Seuraavassa esimerkissä on kaksi näin määriteltyä operaattoria.

#include <iostream>
#include <string>

struct esimerkki {
	int numero;

	esimerkki(int n = 0): numero(n) {
	}

	// Tehdään funktio, joka tulostaa olion tiedot. Parametrina on viittaus
	// tulostevirtaan (esim. cout), ja sama viittaus myös palautetaan.
	std::ostream& tulosta(std::ostream& virta) const {
		virta << "esimerkki(" << numero << ")";
		return virta;
	}
};

// Määritellään operaattori <<, jonka vasemmalla puolella on virta ja oikealla
// luokkaa esimerkki edustava olio. Operaattori palauttaa viittauksen virtaan,
// jolloin voidaan tulostaa monta asiaa peräkkäin (std::cout << a << b << c).
// Jos tämä operaattori määriteltäisiin jonkin luokan jäseneksi, sen täytyisi
// olla std::ostream-luokan jäsen, mikä ei ole mahdollista.
std::ostream& operator << (std::ostream& virta, esimerkki const& esim) {
	return esim.tulosta(virta);
}

// Määritellään operaattori +, joka lisää kokonaislukuun olion ja palauttaa
// tuloksen kokonaislukuna. Jos tämä operaattori määriteltäisiin jonkin luokan
// jäseneksi, sen täytyisi olla int-luokan operaattori, mikä ei ole mahdollista,
// koska int ei ole luokka.
int operator + (int luku, esimerkki const& esim) {
	return luku + esim.numero;
}

int main() {
	esimerkki a(1), b(12), c(123);

	// Nyt oliot voi tulostaa aivan normaaliin tapaan.
	std::cout << a << ", " << b << ", " << c << std::endl;

	// Luvun ja olion yhteenlaskukin onnistuu.
	std::cout << 0 << " + " << b << " = " << (0 + b) << std::endl;
}

Kommentit

Weggo [13.01.2011 15:23:21]

#

mite ois virtuaalifunktiot opassarjaan?

Metabolix [13.01.2011 16:24:02]

#

Weggo, älä hätäile: opassarja ei ole vielä päättynyt. Virtuaalifunktiot kerrotaan perinnän yhteydessä eli luultavasti seuraavassa osassa.

ItEliasPro [03.02.2011 21:14:44]

#

Ai tuleeko seuraava osa?
Jes jos tulee :)

Mahi [12.04.2013 21:37:07]

#

En käsitä tätä struct-sanan käyttöä, class-sanaa käytetään maailmanlaajuisesti ja structia ei todellakaan ole tarkoitettu yleisesti tavallisten luokkien luontiin.

Metabolix [12.04.2013 21:55:05]

#

Mahi kirjoitti:

structia ei todellakaan ole tarkoitettu

Mistä tiedät, mihin se on tarkoitettu? Jos kielen kehittäjien mielestä sanoilla struct ja class pitäisi olla jokin erityinen ero, varmasti niin olisi kirjoitettu C++-standardiin. Kuitenkin standardissa sanotaan täysin yksiselitteisesti, että sanoilla ei ole olennaista eroa, ja standardin esimerkeissäkin käytetään huomattavassa määrin struct-sanaa – jopa periytymistä ja polymorfismia käsittelevissä esimerkeissä.

Hengilö [16.05.2013 22:19:05]

#

Vihdoinkin tajusin luokat tämän avulla. Hyvin on asiat kerrottu.

Kirjoita kommentti

Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.

Muista lukea kirjoitusohjeet.
Tietoa sivustosta