Konstruktori

Pojam konstruktora

  • Funkcija članica koja nosi isto ime kao i klasa naziva se konstruktor (engl. constructor).
  • Ova funkcija poziva se prilikom kreiranja objekta te klase.
  • Konstruktor nema tip koji vraća (čak ni VOID).
  • Konstruktor može da ima argumente proizvoljnog tipa.
  • Unutar konstruktora, članovima objekta pristupa se kao i u bilo kojoj drugoj funkciji članici.
  • Konstruktor se uvek implicitno poziva pri kreiranju objekta klase, odnosno na početku životnog veka svakog objekta date klase.
  • Konstruktor, kao i svaka funkcija članica, može biti preklopljen (engl. overloaded).
  • Konstruktor koji se može pozvati bez stvarnih argumenata (nema formalne argumente ili ima sve argumente sa podrazumevanim vrednostima) naziva se podrazumevanim konstruktorom.
  • Način poziva je različit od poziva ostalih metoda ( nije z.Complex; , već Complex z;).
  • Svaka klasa ima barem jedan konstruktor (čak i ako nije naveden), ali ih moze imati i viče. Svi konstruktori će imati isto ime, pa će se njihovo prepoznavanje vrsiti po ulaznim parametrima (Ovo znači da ne smeju postojati dva konstruktora sa jednakim ulaznim parametrima).
  • Odredjene podgrupe konstruktora:
    • konstruktor kopije
    • podrazumevani konstruktor, konstruktor bez parametara
    • ugradjeni konstruktor , spada u podrazumevane konstruktore, aktivira se kada za klasu nije naveden ni jedan konstruktor. Razlika podrazumevanog i ugrađenog konstruktora je u tome što podrazumevani mi mozemo napisati. Ako klasu snabdemo barem jednim konstruktorom, tada ugrađeni konstruktor vise ne vazi.

Kada se poziva konstruktor

Konstruktor je funkcija koja pretvara alocirane memorijske lokacije koje je sistem odvojio za novi objekat (i sve njegove podatke članove) u objekat koji ima svoje članove i koji može da prima poruke, odnosno ima sva svojstva svoje klase i konzistentno početno stanje.
Pre nego što se pozove konstruktor, objekat je u trenutku definisanja samo "gomila praznih bita" u memoriji računara.
Konstruktor ima zadatak da od ovih bita napravi objekat tako što će inicijalizovati članove.

Konstruktor se poziva uvek kada se kreira objekat klase, a to je u sledećim slučajevima:

  • kada se izvršava definicija statièkog objekta;
  • kada se izvršava definicija automatskog (lokalnog nestatičkog) objekta unutar bloka; formalni argumenti se, pri pozivu funkcije, kreiraju kao lokalni automatski objekti;
  • kada se kreira objekat, pozivaju se konstruktori njegovih podataka članova;
  • kada se kreira dinamički objekat operatorom new;
  • kada se kreira privremeni objekat, pri povratku iz funkcije, koji se inicijalizuje vraćenom vrednošću funkcije.

Načini pozivanja konstruktora

Konstruktor se poziva kada se kreira objekat klase. Na tom mestu je moguće navesti inicijalizatore, tj. stvarne argumente konstruktora.
Poziva se onaj konstruktor koji se najbolje slaže po broju i tipovima argumenata (pravila su ista kao i kod preklapanja funkcija):

class X {
    public:
        X ();
        X (double);
        X (char*);
        ...
};
void 
main () {
    double d=3.4;
    char *p="Niz znakova";
    X a(d),     // poziva se X(double)
    b(p),          // poziva se X(char*)
    c;           // poziva se X()
    ...    
}

Pri definisanju objekta c sa zahtevom da se poziva podrazumevani konstruktor klase X, ne treba navesti
X c(); (jer je to deklaracija funkcije),
već samo X d;.

Pre izvršavanja samog tela konstruktora klase pozivaju se konstruktori članova. Argumenti ovih poziva mogu da se navedu iza zaglavlja definicije (ne deklaracije) konstruktora klase, iza znaka : (dvotačka):

class YY {
    public:
        YY (int j) {...}
        ...
};
class XX {
        YY y;
        int i;
    public:
        XX (int);
};
 
XX::XX (int k) : y(k+1) , i(k-1) {
    // y je inicijalizovan sa k+1, a i sa k-1
    ... ostatak konstruktora
}

Prvo se pozivaju konstruktori članova, po redosledu deklarisanja u deklaraciji klase, pa se onda izvršava telo konstruktora klase.

Ovaj način ne samo da je moguć, već je i jedino ispravan: navođenje inicijalizatora u zaglavlju konstruktora predstavlja specifikaciju inicijalizacije članova (koji su ugrađenog tipa ili objekti klase), što je različito od operacije dodele koja se može jedino vršiti unutar tela konstruktora.

Osim toga, kada za člana korisničkog tipa ne postoji podrazumevani konstruktor, ili kada je član konstanta ili referenca, ovaj način je i jedini način inicijalizacije člana.

Konstruktor se može pozvati i eksplicitno u nekom izrazu. Tada se kreira privremeni objekat klase pozivom odgovarajućeg konstruktora sa navedenim argumentima.

Isto se dešava ako se u inicijalizatoru eksplicitno navede poziv konstruktora:

void 
main () {
    complex c1(1,2.4),c2;
    c2=c1+complex(3.4,-1.5);     // privremeni objekat
    complex c3=complex(0.1,5);  // opet privremeni objekat koji se kopira u c3
}

Kada se kreira niz objekata neke klase, poziva se podrazumevani konstruktor za svaku komponentu niza
ponaosob, po rastućem redosledu indeksa.


Podrazumevani konstruktor

Podrazumevani konstruktor je konstruktor koji nema argumenata.
Podrazumevani konstruktor za klasu X ima oblik X::X().
Konstruktor koji ima sve podrazumevane argumente, X::X(const int x=0), na primer, takođe je podrazumevani konstruktor, pošto se on može pozvati bez argumenata.

Podrazumevani konstruktori omogućavaju objektima da budu kreirani bez prosleđivanja bilo kakvih parametara konstruktoru.

Na primer, deklaracija

String s;

Za rezultat daje string s koji još uvek nema vrednost; to je jedan prazan string.

Podrazumevani konstruktor obično kreira objekat koji predstavlja null instancu konkretnog tipa koji klasa označava.
Podrazumevani konstruktor za kompleksni broj može rezultirati u objektu sa vrednošću nula, dok podrazumevani konstruktor za povezanu listu može rezultirati praznom listom.

Smatra se dobrom praksom :
Obezbedite podrazumevani konstruktor za svoju klasu.

Koristite podrazumevane argumente da izbegnete pojavljivanje posebnog podrazumevanog konstruktora koji nema argumente.

Često ćete omogućiti korisnicima vaših klasa da prosleđuju argumente konstruktoru. Bolje je da obezbedite konstruktor sa podrazumevanim argumentima koji može da služi kao podrazumevani konstruktor ili kao konstruktor koji prihvata argumente koje specificira nego da obezbedite poseban podrazumevani konstruktor koji ne prihvata (nema) argumente. Na primer, dva konstruktora

String();
i
String(const char* str);

mogu da se kombinuju u jedan konstruktor koji ima podrazumevani argument:
String(const char* str=0);

Može da postoji samo jedan podrazumevani konstruktor, zato nemojte dodavati podrazumevane argumente svim argumentima svakog konstruktora – vaš kompajler će se verovatno žaliti.

Budite pažljivi kod implementiranja šablona. Ponekad ako ne vodite dovoljno računa kako implementirate kôd, može se desiti da primorate svoje korisnike da koriste svoje tipove koji imaju podrazumevani konstruktor. Ako stavite

"T element;

u neku od svojih šablon funkcija, primoravate svoje korisnike da koriste tipove koji imaju podrazumevani konstruktor.

Ugradjeni konstruktor

Ugradjeni konstruktor spada u podrazumevane konstruktore.
Aktivira se kada za klasu nije naveden ni jedan konstruktor.
Razlika podrazumevanog i ugrađenog konstruktora je u tome što podrazumevani mi mozemo napisati.
Ako klasu snabdemo barem jednim konstruktorom, tada ugrađeni konstruktor vise ne vazi.


Konstruktor kopije

Kada se objekat x1 klase XX inicijalizuje drugim objektom x2 iste klase, C++ će podrazumevano (ugrađeno)
izvršiti prostu inicijalizaciju redom članova objekta x1 članovima objekta x2.
To ponekad nije dobro (često ako objekti sadrže članove koji su pokazivači ili reference), pa programer treba da ima potpunu kontrolu nad inicijalizacijom objekta drugim objektom iste klase.

Za ovu svrhu služi tzv. konstruktor kopije (engl. copy constructor).
To je konstruktor klase XX koji se može pozvati sa samo jednim stvarnim argumentom tipa XX.

Taj konstruktor se poziva kada se objekat inicijalizuje objektom iste klase, a to je:

  • prilikom inicijalizacije objekta (pomoću znaka = ili sa zagradama);
  • prilikom prenosa argumenata u funkciju (kreira se lokalni automatski objekat);
  • prilikom vraćanja vrednosti iz funkcije (kreira se privremeni objekat).

Konstruktor kopije nikad ne sme imati formalni argument tipa XX, a može argument tipa XX& ili najčešće const XX&.
Primer:

class XX {
    public:
        XX (int);
        XX (const XX&); // konstruktor kopije
        ...
};
 
XX f(XX x1) {
    XX x2=x1;     // poziva se konstruktor kopije XX(XX&) za x2
    //...
    return x2;     // poziva se konstruktor kopije za 
               // privremeni objekat u koji se smešta rezultat
}
 
void 
g() {
    XX xa=3, xb=1;
    ...
    xa=f(xb);     // poziva se konstruktor kopije samo za
               // formalni argument x1,
               // a u xa se samo prepisuje privremeni objekat,
                 // ili se poziva XX::operator= ako je definisan
}

Prepoznaje se po tome sto ima jedan parametar koji je referenca:

 Complex (Complex &z) {....}

Pojavljuje se u dvostrukoj ulozi:

  • kao normalan konstruktor
  • sa specifičnom ulogom

Njegova specifična namena je kod prenosa objekta kao parametra (ali ne kod prenosa po adresi, već po vrednosti, kada se ceo objekat smešta na stek).

Konstruktor kopije je zadužen da ceo objekat kopira na stek. Pošto je ovo često neizbežno, svaka klasa u sebi sadrzi ugradjeni konstruktor kopije (što je u stvari običan ugrađeni konstruktor).
I kada se prosleđuje i kada se vraća objekat kao parametar, poziva se konstruktor kopije kao posrednik.

Konstruktor kopije se mora programirati kada klasa sadrži elemente u dinamičkoj memoriji.


Add a New Comment
or Sign in as Wikidot user
(will not be published)
- +

Unless otherwise stated, the content of this page is licensed under GNU Free Documentation License.