...
Muodostimet
...
(Constructor)
...
Aina,
...
kun
...
luodaan
...
uusi
...
olio
...
suoritetaan
...
automaattisesti
...
muodostin-funktio.
...
Lisäksi
...
kutsutaan
...
mahdollisen
...
vanhemman
...
muodostinta,
...
sekä
...
kaikkien
...
olioiden
...
muodostimia,
...
mistä
...
luokka
...
koostuu.
...
Muodostimen
...
nimi
...
on
...
sama
...
kuin
...
luokan
...
nimi.
Anchor | ||||
---|---|---|---|---|
|
Esimerkki 1. Parametriton muodostin.
Code Block |
---|
Esimerkki 1: Muodostin. {code} class CElain { public: CElain(); // Parametriton Muodostinmuodostin }; {code} |
Yllä
...
olevassa
...
esimerkissä
...
on
...
esitelty
...
parametriton
...
muodostin
...
(ilman
...
toteutusta).
...
Muodostimessa
...
voidaan
...
antaa
...
oliolle
...
alkuarvoja,
...
varata
...
muistia
...
jne.
...
Jos
...
muodostimelle
...
halutaan
...
antaa
...
parametreja,
...
laitetaan
...
ne
...
sulkuihin,
...
kuten
...
mihin
...
tahansa
...
funktioon.
...
Muodostin
...
ei
...
palauta
...
mitään,
...
joten
...
nimen
...
eteen
...
ei
...
saa
...
laittaa
...
mitään
...
tyyppiä,
...
ei
...
edes
...
void-määrittelyä.
Anchor | ||||
---|---|---|---|---|
|
Esimerkki 2. Luokkaan CElain on lisätty muodostin, joka antaa luokan oliolle heti nimen ja massan.
Code Block |
---|
Esimerkki 2: Luokkaan CElain on lisätty muodostin, joka antaa luokan oliolle heti nimen ja massan. class CElain { private: char char m_Nimi\[100\]; float float m_Massa; public: CElain(char Nimi\[\], float float Massa); // MuodostinParametrillinen muodostin bool bool AsetaNimi(char Nimi\[\]); bool KysyNimi(char Nimi\[\]); // Asetusfunktio char[] PalautaNimi(); // Palautusfunktio bool AsetaMassa(float Massa); bool KysyMassa(); // Asetusfunktio float PalautaMassa(); // Palautusfunktio }; |
Muodostimelle
...
täytyy
...
tehdä
...
vielä
...
toteutus,
...
joka
...
voi
...
olla
...
esim.
...
seuraavanlainen:
Code Block |
---|
CElain::CElain(char Nimi\[\], float Massa) // Parametrillinen muodostin { strcpy strcpy(m_Nimi, Nimi); m m_Massa = Massa; } |
Luokalla
...
on
...
aina
...
oletusmuodostin,
...
esim
...
CElain::CElain(),
...
mutta
...
jos
...
ohjelmoija
...
tekee
...
yhdenkin
...
oman
...
muodostimen
...
ei
...
oletusmuodostinta
...
voida
...
enää
...
käyttää.
...
Yllä
...
olevassa
...
esimerkissä
...
tulisi
...
kaikki
...
CElain-luokan
...
oliot
...
muodostaa
...
antamalla
...
niille
...
nimi
...
ja
...
massa.
...
Esimerkiksi:
Code Block |
---|
CElain kissa("misu", 5.2);
|
Mutta
...
koska
...
oletusmuodostinta
...
ei
...
voida
...
enää
...
käyttää,
...
seuraava
...
ei
...
ole
...
validia
...
koodia:
Code Block |
---|
CElain kissa; Huom\! Jos parametritonta muodostinta halutaan käyttää, tulee myös se |
Note |
---|
Jos parametritonta muodostinta halutaan käyttää, tulee myös se lisätä luokkaan: |
Code Block |
---|
lisätä luokkaan: class CElain { ... public: CElain(); CElain(char Nimi\[\], float Massa); ... \\ h3 CElain(); // Parametriton muodostin CElain(char Nimi[], float Massa); // Parametrillinen muodostin ... |
Kopiomuodostimet (Copy Constructor)
Kopiomuodostin on muodostin, jolle annetaan parametrina referenssi itsensä tyyppiseen olioon. Kopiomuodostimen idea on mahdollistaa olion monistaminen eli kopioiminen. C++ tarjoaa oletuskopiomuodostimen joka osaa tehdä matalan kopioinnin (shallow copy), tarkoittaen että se kopioi kaikkien jäsenmuuttujien arvot, myös osoittimien, mikä on ongelma, kts. Esimerkki 2.
Esimerkki 3. Kopiomuodostimen esittely.
Code Block |
---|
class CElain
{
private:
float m_Massa;
public:
CElain() {} // Tyhjä oletusmuodostin.
CElain(const CElain&); // Kopiomuodostin
};
|
Yllä olevassa esimerkissä on esitelty kopiomuodostin (ilman toteutusta). Toteutus voisi olla seuraavanlainen:
Code Block |
---|
CElain::CElain(const CElain& Instanssi) {
this->m_Massa = Instanssi.m_Massa; // Kopioidaan annetun olion muuttujan m_Massa arvo omaamme.
}
|
Yllä annettu toteutus on itse asiassa sama kuin oletuskopiomuodostin. Se kopioi kaikkien jäsenmuuttujien arvot.
Esimerkki 4. Kopiomuodostin, kun luokassa on osoittimia.
Code Block |
---|
class CElain
{
private:
float m_Massa;
char* m_Nimi; // Osoitin.
public:
CElain(char Nimi[])
{
m_Nimi = new char[strlen(Nimi) + 1]; // Varataan muistia, annetun merkkijonon verran.
}
~CElain()
{
delete [] m_Nimi; // Vapautetaan varattu muisti.
}
CElain() { }
CElain(const CElain& Instanssi)
{
this->m_Massa = Instanssi.m_Massa;
this->m_Nimi = Instanssi.m_Nimi; // Tässä kopioidaan ainoastaan osoittimen arvo, ei itse muistia!
}
};
|
Yllä oleva kopiomuodostin (joka siis on kuten oletuskopiomuodostin) aiheuttaa ongelmia. Ajatellaan seuraavaa tilannetta:
- Luodaan ensimmäinen olio, sille annetaan nimi ja varataan muodostimessa tarvittavan määrän muistia nimeä varten.
- Luodaan toinen olio, käyttämällä kopiomuodostinta, antamalla ensimmäinen olio parametrina.
- Nyt molemmat oliot omaavat saman massan ARVON ja saman osoittimen ARVON muistiin, jossa sijaitsee nimi. Siis molemmat oliot osoittavat samaan muistialueeseen.
- Tuhotaan ensimmäinen olio, jolloin kutsutaan sen hajoitin, joka vapauttaa varatun muistin.
- Nyt meillä on enään toisena luoto olio, jonka m_Nimi osoittaa muistiin, joka juuri vapautettiin!
- --> Meillä on katastrofi käsissä!
Yllä kuvatun ongelman välttämiseksi voimme kirjoittaa oman kopiomuodostimen, jossa suoritetaan syvä kopiointi (deep copy), eli varataan tarvittava muisti ja kopioidaan merkkijono sinne:
Code Block |
---|
class CElain
{
...
CElain(CElain& Instanssi)
{
this->m_Massa = Instanssi.m_Massa;
this->m_Nimi = new char[strlen(Instanssi.m_Nimi) + 1]; // Varataan tarvittava muisti merkkijonolle.
strcpy(this->m_Nimi, Instanssi.m_Nimi); // Tässä kopioidaan merkkijono.
}
};
|
Tämä kopiomuodostin kopioi osoittimen sisältämän muistin oikein. Nyt voidaan muodostaa haluttu määrä kopioita ja ne käyttäytyvät kuten pitääkin.