Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

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
esim1
esim1

Esimerkki 1. Parametriton muodostin.

Code Block
class CElain
{
public:
    CElain();   // Parametriton muodostin
};

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
esim2

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
    char[] PalautaNimi();             // Palautusfunktio

    bool AsetaMassa(float Massa);     // 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(m_Nimi, Nimi);
     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;
Note

Jos parametritonta muodostinta halutaan käyttää, tulee myös se lisätä luokkaan:

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.