h2. Hajoittimet (Destructor)
Hajoittimen nimi on muotoa mato(~) ja sen perään luokan nimi, esim. \~CElain().
Hajoittimen ominaisuuksia:
* Hajoittimessa tulee vapauttaa muodostimessa varatut muistit.
* Kutsutaan automaattisesti, kuten muodostintakin.
* Hajoittimelle ei voi antaa parametreja.
* Vaikka muodostin on ylikirjoitettu, voidaan silti käyttää oletushajoitinta.
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 5.
Esimerkki 4: Kopiomuodostimen esittely.
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:
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 5: Kopiomuodostin, kun luokassa on osoittimia.
class CElain
\{
private:
float m_Massa;
char\* m_Nimi; // Osoitin.
public:
CElain(char Nimi\[\])
{
m
\{ m_Nimi = new char\[strlen(Nimi) + 1\]; // Varataan muistia, annetun merkkijonon verran.
}
~CElain()
{
delete
\{ delete \[\] m_Nimi; // Vapautetaan varattu muisti.
}
CElain() { }
CElain(const CElain& Instanssi)
{
\{ this->m_Massa = Instanssi.m_Massa;
this->m_Nimi = Instanssi 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:
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. |