9.4.7 Yliluokan muodostajan kutsuminen ennen muodostajaa
Joskus kirjoittamalla vähän enemmän voi saada aikaan nopeampaa
ja turvallisempaa koodia. Niin tässäkin tapauksessa. Nimittäin
säätäminen siinä, ettemme vaivautuneet kirjoittamaan
kumpaankin luokkaan erikseen oletusmuodostajaa, johtaa siihen että
esimerkiksi alustus
cAikaSek a4(12,55,45);
kutsuu kaikkiaan seuraavia metodeja:
cAikaSek::cAikaSek(12,55,45);
cAika::cAika(0,0); // Oletusalustus yliluokalle
cAika::aseta(0,0,0);
cAika::lisaa(0,0); // Tässä cAika, koska ei vielä "kasvanut" cAikaSek
cAikaSek::aseta(12,55,45);
cAika::aseta(12,55);
cAikaSek::lisaa(0,0); // HUOM! Todellakin cAikaSek::lisaa virt.takia
cAika::lisaa(0,0);
cAikaSek::lisaa(0,0);
cAika::lisaa(0,0);
Olisitko arvannut! Enää ei tarvitse ihmetellä miksi olio-
ohjelmat ovat isoja ja hitaita. Totta kai voitaisiin sanoa, että
hyvän kääntäjän olisi pitänyt huomata tuosta
optimoida päällekkäisyydet pois. Mutta tämä on
vielä tänä päivänä kova vaatimus
kääntäjälle. Mutta ehkäpä voisimme ohjelmoijina
vähän auttaa:
olioalk\aikaclaB.cpp - oletusmuodostajat
...
class cAika {
int h,m;
public:
virtual void aseta(int ih=0,int im=0, int is=0) { ... }
cAika() { h = 0; m = 0; } // tai cAika() : h(0), m(0) {} // Oletusmuodostaja
cAika(int ih, int im=0) { aseta(ih,im); } // ih=0 oletus pois
virtual void lisaa(int lisa_min, int lisa_sek=0) { ... }
virtual void tulosta(int lf=1) const { ... }
};
class cAikaSek : public cAika {
int s;
public:
virtual void aseta(int ih=0, int im=0, int is=0) {...}
cAikaSek() : s(0), cAika() {} // Oletusmuodostaja
cAikaSek(int ih, int im=0, int is=0) : cAika(ih,im), s(is) { lisaa(0); }
};
...
Nyt on saatu ainakin seuraavat edut: oletustapauksessa kumpikin luokka alustuu
pelkillä 0:ien sijoituksilla, ilman yhtään
ylimääräistä kutsua. Oletusmuodostaja luokalla
cAika voidaan esitellä esim. seuraavilla tavoilla, joista
keskimmäistä voidaan pitää parhaana:
cAika() { h = 0; m = 0; } // Normaalit sijoitukset
cAika() : h(0), m(0) {} // h:n muodostaja arvolla 0, eli h=0 kokonaisluvulle
// m:n muodostaja arvolla 0, eli m=0
cAika() : h(0) { m=0; }
Vastaavasti luokalle cAikaSek pitää oletusmuodostaja
tehdä seuraavanlaiseksi:
cAikaSek() : s(0), cAika() { } // s alusetetaan 0:ksi ja peritty yliluokka
// alustetaan muodostajalla cAika()
cAikaSek() { s = 0; } // TÄMÄ on TÄSSÄ tapauksessa sama kuin
// edellinen, koska jos yliluokan muodostajaa
// ei itse kutsuta, kutsutaan
// oletusmuodostajaa
ELI! Perityn yliluokan muodostajaa kutsutaan automaattisesti, jollei
sitä itse tehdä. Nyt parametrillisessa muodostajassa kutsutaan
yliluokan muodostajaa, ja näin voidaan välttää ainakin
oletusmuodostajan kutsu:
cAikaSek(int ih, int im=0, int is=0) : cAika(ih,im), s(is) { lisaa(0); }
Nyt alustuksesta
cAikaSek a4(12,55,45);
seuraa seuraavat metodikutsut
cAikaSek::cAikaSek(12,55,45);
cAika::cAika(12,55); // NYT EI oletusalustusta yliluokalle
cAika::aseta(12,55,0);
cAika::lisaa(0,0);
cAikaSek::lisaa(0,0);
cAika::lisaa(0,0);
Turhaahan tuossa on vieläkin, mutta tippuihan kuitenkin noin puolet pois!
Joka tapauksessa periminen tuottaa jonkin verran koodia, aina kun yliluokan
metodeja käytetään hyväksi. Ja jollei
käytettäisi, kirjoitettaisiin samaa koodi uudelleen, eli palattaisiin
70- luvulle. Nykyisin kasvanut koneteho kompensoi kyllä tehottomamman
oliokoodin ja olio- ohjelmoinnin ansiosta pystytään kirjoittamaan
luotettavampia (?) ja monimutkaisempia ohjelmia.
- Pöytätestaa sekä aikaclaA.cpp että aikaclaB.cpp
alustuksella cAikaSek a4(1,95,70);