...
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 |
---|
class CElain { public: CElain(); // Parametriton muodostin }; |
...
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 |
---|
class CElain { private: char m_Nimi[100]; float m_Massa; public: CElain(char Nimi[], float Massa); // Parametrillinen muodostin bool AsetaNimi(char Nimi[]); // Asetusfunktio bool KysyNimi(char Nimi[] 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 |
---|
class CElain {
...
public:
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.