Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Wiki Markup
h2. 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. !Muodostimet.png|border=1!

{anchor:esim1}

h3. Esimerkki 1. Parametriton muodostin.

{code}
class CElain
{
public:
    CElain();   // Parametriton muodostin
};
{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:esim2}

h3. Esimerkki 2. Luokkaan CElain on lisätty muodostin, joka antaa luokan  oliolle heti nimen ja massan.

{code}
class CElain
{
private:
    char m_Nimi[100];
    float m_Massa;

public:
    CElain(char Nimi[], float Massa); // Parametrillinen muodostin

    bool AsetaNimi(char Nimi[]);      // Asetusfunktio
    char[] PalautaNimi();             // Palautusfunktio

    bool AsetaMassa(float Massa);     // Asetusfunktio
    float PalautaMassa();             // Palautusfunktio
};
{code}
Muodostimelle täytyy tehdä vielä toteutus, joka voi olla esim. seuraavanlainen:

{code}
CElain::CElain(char Nimi[], float Massa) // Parametrillinen muodostin
{
     strcpy(m_Nimi, Nimi);
     m_Massa = Massa;
}
{code}
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}
CElain kissa("misu", 5.2);
{code}
Mutta koska oletusmuodostinta ei voida enää käyttää, seuraava ei ole  validia koodia:
{code}
CElain kissa;
{code}
{note}Jos parametritonta muodostinta halutaan käyttää, tulee myös se lisätä luokkaan:{note}
{code}
class CElain {
...
public:
    CElain();                         // Parametriton muodostin
    CElain(char Nimi[], float Massa); // Parametrillinen muodostin
...
{code}

h2. 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.

h3. Esimerkki 3. Kopiomuodostimen esittely.

{code}
class CElain
{
private:
    float m_Massa;

public:
    CElain() {}              // Tyhjä oletusmuodostin.
    CElain(const CElain&);   // Kopiomuodostin
};
{code}
Yllä olevassa esimerkissä on esitelty kopiomuodostin (ilman toteutusta). Toteutus voisi olla seuraavanlainen:

{code}
CElain::CElain(const CElain& Instanssi) {
    this->m_Massa = Instanssi.m_Massa;   // Kopioidaan annetun olion muuttujan m_Massa arvo omaamme.
}
{code}
Yllä annettu toteutus on itse asiassa sama kuin oletuskopiomuodostin. Se kopioi kaikkien jäsenmuuttujien arvot.

h3. Esimerkki 4. Kopiomuodostin, kun luokassa on osoittimia.

{code}
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!
    }
};
{code}
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}
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.
    }
};
{code}
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.