Klase, objekti i članovi klase

Pojam i deklaracija klase

Klasa je realizacija apstrakcije koja ima

  • svoju internu predstavu (svoje atribute) i
  • operacije koje se mogu vršiti nad njom (javne funkcije članice).

Klasa definiše novi tip.
Jedan primerak takvog tipa (instanca klase) naziva se OBJEKTOM te klase (engl. class object).
PODACI koji su deo klase nazivaju se podaci članovi klase (engl. data members).
FUNKCIJE koje su deo klase nazivaju se funkcije članice klase (engl. member functions).

Članovi (podaci ili funkcije) :

  • klase iza ključne reči PRIVATE: zaštićeni su od pristupa spolja (enkapsulirani su). Ovim članovima mogu pristupati samo funkcije članice klase. Ovi članovi nazivaju se PRIVATNIM članovima klase(engl. private class members).
  • Članovi iza ključne reči PUBLIC: dostupni su spolja i nazivaju se JAVNIM članovima klase (engl. public class members).
  • Članovi iza ključne reči PROTECTED: dostupni su funkcijama članicama date klase, kao i klasa izvedenih iz te klase, ali ne i korisnicima spolja, i nazivaju se ZAŠTIĆENIM članovima klase (engl. protected class members).

Redosled sekcija public, protected i private je proizvoljan, ali se preporučuje baš navedeni redosled.
Podrazumevano (ako se ne navede specifikator ispred) su članovi privatni.


Kaže se još da klasa ima svoje unutrašnje stanje, predstavljeno atributima, koje menja pomoću operacija.
Javne funkcije llanice nazivaju se još i metodima klase, a poziv ovih funkcija - upućivanje poruke objektu klase.

Objekat klase menja svoje stanje kada se pozove njegov metod, odnosno kada mu se uputi poruka.

Objekat unutar svoje funkcije članice može pozivati funkciju članicu neke druge ili iste klase, odnosno uputiti poruku drugom objektu. Objekat koji šalje poruku (poziva funkciju) naziva se objekat-klijent, a onaj koji je prima (čija je funkcija članica pozvana) je objekat-server.


Preporuka je da se klase projektuju tako da nemaju javne podatke članove.
Unutar funkcije članice klase, članovima objekta čija je funkcija pozvana pristupa se direktno, samo navođenjem njihovog imena.

Kontrola pristupa članovima nije stvar objekta, nego klase: jedan objekat neke klase iz svoje funkcije članice može da pristupi privatnim članovima drugog objekta iste klase. Takođe, kontrola pristupa članovima je potpuno odvojena od koncepta oblasti važenja: najpre se, na osnovu oblasti važenja, određuje entitet na koga se odnosi dato ime na mestu obraćanja u programu, a zatim se određuje da li se tom entitetu može pristupiti.

Moguće je preklopiti (engl. overload) funkcije članice, ukljuèujući i konstruktore.

Deklaracijom klase smatra se deo kojim se specifikuje ono što korisnici klase treba da vide. To su uvek javni članovi. Međutim, da bi prevodilac korektno zauzimao prostor za objekte klase, mora da zna njegovu veličinu, pa u deklaraciju klase ulaze i deklaracije privatnih podataka članova:

class Point{
    private:
        double x,y;
    public:
        void SetPoint(double xx, double yy) {x=xx; y=yy;}
        double GetX() {return x;}
        double GetY() {return y;}
        double Distance();
};
class Ime_Klase{
    podaci_clanovi
    objekti_clanovi
    funkcije_clanice
};

Navedena deklaracija je zapravo definicija klase, ali se iz istorijskih razloga naziva deklaracijom.
Pravu deklaraciju klase predstavlja samo deklaracija class Ime_Klase;. Pre potpune deklaracije (zapravo definicije) mogu samo da se definišu pokazivači i reference na tu klasu, ali ne i objekti te klase, jer se njihova velièina ne zna.

Pokazivač this

Unutar svake funkcije članice postoji implicitni (podrazumevani, ugrađeni) lokalni objekat this.
Tip ovog objekta je "konstantni pokazivač na klasu čija je funkcija članica" (ako je klasa X, this je tipa X*const).
Ovaj pokazivač ukazuje na objekat čija je funkcija članica pozvana:

// definicija funkcije Add članice klase complex
complex complex::Add (complex c) {
    complex temp=*this;
    // u temp se prepisuje objekat koji je prozvan
    temp.real+=c.real;
    temp.imag+=c.imag;
    return temp;
}
//deklaracija klase complex:
class complex {
public:
    void Add(complex);
    void Sub(complex);
    float Re();
    float Im();
    //...
private:
    float real,imag;
};

Pristup članovima objekta čija je funkcija članica pozvana obavlja se neposredno; implicitno je to pristup
preko pokazivača this i operatora ->. Može se i eksplicitno pristupati članovima preko ovog pokazivača unutar funkcije članice:

// nova definicija funkcije Add članice klase complex
complex complex::Add (complex c) {
    complex temp;
    temp.real=this->real+c.real;
    temp.imag=this->imag+c.imag;
    return temp;
}

Pokazivač this je, u stvari, jedan skriveni argument funkcije članice.
Poziv objekat.f() prevodilac prevodi u kôd koji ima semantiku kao f(&objekat).


Podrazumevano se sa objektima klase može raditi sledeće:

  • definisati primerci (OBJEKTI) te klase i nizovi objekata klase;
  • definisati POKAZIVAČI na objekte i REFERENCE na objekte;
  • dodeljivati vrednosti (OPERATOR=) jednog objekta drugom;
  • uzimati adrese objekata (OPERATOR&) i posredno pristupati objektima preko pokazivača (OPERATOR*);
  • pristupati članovima i pozivati funkcije članice neposredno (OPERATOR .) ili posredno (OPERATOR ->);
  • prenositi objekti kao argumenti funkcija i to po vrednosti ili referenci, ili prenositi pokazivači na objekte;
  • vraćati objekte iz funkcija po vrednosti ili referenci, ili vraćati pokazivače na objekte.

Neke od ovih operacija korisnik može redefinisati preklapanjem operatora.
Ostale, ovde nenavedene operacije korisnik mora definisati posebno ako su potrebne (ne podrazumevaju se).


Konstantne funkcije članice

Dobra programerska praksa je da se korisnicima klase specifikuje da li neka funkcija članica menja unutrašnje stanje objekta ili ga samo "čita" i vraća informaciju korisniku klase.

Funkcije članice koje ne menjaju unutrašnje stanje objekta nazivaju se inspektori ili selektori (engl. inspector, selector).
Da je funkcija članica inspektor, korisniku klase govori reč const iza zaglavlja funkcije. Ovakve funkcije članice nazivaju se u jeziku C++ konstantnim funkcijama članicama (engl. constant member functions).

Funkcija članica koja menja stanje objekta naziva se mutator ili modifikator (engl. mutator, modifier) i posebno se ne označava:

class X {
    public:
        int read () const  { return i; }
        int write (int j=0) { int temp=i; i=j; return temp; }
    private:
        int i;
    };

Deklarisanje funkcije članice kao inspektora je samo notaciona pogodnost i "stvar lepog ponašanja prema
korisniku". To je "obećanje" projektanta klase korisnicima da funkcija ne menja stanje objekta, onako kako je projektant klase definisao stanje objekta. Prevodilac nema načina da u potpunosti proveri da li inspektor menja neke podatke članove klase preko nekog posrednog obraćanja.

Inspektor može da menja podatke članove, uz pomoć eksplicitne konverzije, koja "probija" kontrolu konstantnosti. To je ponekad slučaj kada inspektor treba da izračuna podatak koji vraća (npr. dužinu liste), pa ga onda sačuva u nekom članu da bi sledeći put brže vratio odgovor.

U konstantnoj funkciji članici tip pokazivača this je const X*const, tako da pokazuje na konstantni objekat, pa nije moguće menjati objekat preko ovog pokazivača (svaki neposredni pristup članu je implicitni pristup preko ovog pokazivaèa). Takođe, za konstantne objekte klase nije dozvoljeno pozivati nekonstantnu funkciju članicu (korektnost konstantnosti). Za prethodni primer:

X x;
const X cx;
x.read();    // u redu: konstantna funkcija nekonstantnog objekta;
x.write();     // u redu: nekonstantna funkcija nekonstantnog objekta;
cx.read();     // u redu: konstantna funkcija konstantnog objekta;
cx.write();    // greška: nekonstantna funkcija konstantnog objekta;

Ugnježđivanje klasa

Klase mogu da se deklarišu i unutar deklaracije druge klase (ugnežđivanje deklaracija klasa).
Na ovaj način se ugnežđena klasa nalazi u oblasti važenja okružujuće klase, pa se njenom imenu može pristupiti samo preko operatora razrešavanja oblasti važenja :: .

Okružujuća klasa nema nikakva posebna prava pristupa članovima ugnežđene klase, niti ugnežđena klasa ima posebna prava pristupa članovima okružujuće klase. Ugnežđivanje je samo stvar oblasti važenja, a ne i kontrole pristupa članovima.

int x,y;
class Spoljna {
    public:
        int x;
        class Unutrasnja {
            void f(int i, Spoljna *ps) {
            x=i;         // greška: pristup Spoljna::x nije korektan!
            ::x=i;        // u redu: pristup globalnom x;
            y=i;         // u redu: pristup globalnom y;
            ps->x=i;     // u redu: pristup Spoljna::x objekta *ps;
}
};
};
    Unutrasnja u;           // greška: Unutrasnja nije u oblasti važenja!
    Spoljna::Unutrasnja u; // u redu;

Unutar deklaracije klase se mogu navesti i deklaracije nabrajanja (enum), i typedef deklaracije.
Ugnežđivanje se koristi kada neki tip (nabrajanje ili klasa npr.) semantički pripada samo datoj klasi, a nije globalno važan i za druge klase.
Ovakvo korišćenje povećava čitljivost programa i smanjuje potrebu za globalnim tipovima.


Strukture

Struktura je klasa kod koje su svi članovi podrazumevano javni.
Može se to promeniti eksplicitnim umetanjem public: i private:

struct A {
 
        //...
    private:
        //...
};
class A {
    public:
        //...
    private:
        //...
};

Struktura se tipično koristi za definisanje slogova podataka koji ne predstavljaju apstrakciju, odnosno nemaju ponašanje (nemaju značajnije operacije). Strukture tipično poseduju samo konstruktore i eventualno destruktore kao funkcije članice


Zajednički članovi klasa

Zajednički podaci članovi

Pri kreiranju objekata klase, za svaki objekat se kreira poseban komplet podataka članova.
Ipak, moguće je definisati podatke članove za koje postoji samo jedan primerak za celu klasu, tj. za sve objekte klase.
Ovakvi članovi nazivaju se statièkim članovima, i deklarišu se pomoću reči static:

class X {
    public:
        //...
    private:
        static int i; // postoji samo jedan i za celu klasu
        int j; // svaki objekat ima svoj j
        //...
};

Svaki pristup statièkom članu iz bilo kog objeka klase znači pristup istom zajednièkom članu-objektu.
Statièki član klase ima životni vek kao i globalni statički objekat: nastaje na početku programa i traje do kraja programa.
Uopšte, statièki član klase ima sva svojstva globalnog statičkog objekta, osim oblasti važenja klase i kontrole pristupa.
Statički član mora da se inicijalizuje posebnom deklaracijom van deklaracije klase.
Obraćanje ovakvom članu van klase vrši se preko operatora :: .
Za prethodni primer:
int X::i=5;

Statičkom članu može da se pristupi iz funkcije članice, ali i van funkcija članica, čak i pre formiranja ijednog objekta klase klase (jer statički član nastaje kao i globalni objekat), naravno uz poštovanje prava pristupa.
Tada mu se pristupa preko operatora :: (X::j).

Zajednički članovi se uglavnom koriste kada svi primerci jedne klase treba da dele neku zajedničku informaciju, npr. kada predstavljaju neku kolekciju, odnosno kada je potrebno imati ih "sve na okupu i pod kontrolom".
Na primer, svi objekti neke klase se uvezuju u listu, a glava liste je zajednički član klase.

Zajednički članovi smanjuju potrebu za globalnim objektima i tako povećavaju čitljivost programa, jer je
moguče ograničiti pristup njima, za razliku od globalnih objekata.
Zajednički članovi logički pripadaju klasi i "upakovani" su u nju.

Zajedničke funkcije članice

I funkcije članice mogu da se deklarišu kao zajedničke za celu klasu, dodavanjem reči static ispred deklaracije funkcije članice.
Statičke funkcije članice imaju sva svojstva globalnih funkcija, osim oblasti važenja i kontrole pristupa. One ne poseduju pokazivač this i ne mogu neposredno (bez pominjanja konkretnog objekta klase) koristiti nestatičke članove klase. Mogu neposredno koristiti samo statičke članove te klase.

Statičke funkcije članice se mogu pozivati za konkretan objekat (što nema posebno značenje), ali i pre
formiranja ijednog objekta klase, preko operatora :: .

class X {
        static int x;         // staticki podatak clan;
        int y;
    public:
        static int f(X,X&);     // statièka funkcija èlanica;
        int g();
};
 
int X::x=5;                // definicija statièkog podatka èlana;
 
int 
X::f(X x1, X& x2){          // definicija statièke funkcije èlanice;
    int i=x;                   // pristup statièkom èlanu X::x;
    int j=y;                   // greška: X::y nije statièki,
                    // pa mu se ne može pristupiti neposredno!
    int k=x1.y;                 // ovo može;
    return x2.x;                // i ovo može,
                    // ali se izraz "x2" ne izraèunava;
}
 
int 
X::g () {
    int i=x;     // nestatièka funkcija èlanica može da
    int j=y;     // koristi i pojedinaène i zajednièke
    return j;     // èlanove; y je ovde this->y;
}
 
void 
main () {
    X xx;
    int p=X::f(xx,xx);     // X::f može neposredno, bez objekta;
    int q=X::g();         // greška: za X::g mora konkretan objekat!
    xx.g();               // ovako može;
    p=xx.f(xx,xx);         // i ovako može,
                    // ali se izraz "xx" ne izraèunava;
}

Statičke funkcije predstavljaju operacije klase, a ne svakog posebnog objekta.
Pomoću njih se definišu neke opšte usluge klase, npr. tipično kreiranje novih, dinamičkih objekata te klase (operator new je implicitno definisan kao statička funkcija klase).
Na primer, na sledeći način može se obezbediti da se za datu klasu mogu kreirati samo dinamički objekti:
class X {
    public:
        static X* create () { return new X; }
    private:
        X(); // konstruktor je privatan
};

Prijatelji klasa

Često je dobro da se klasa projektuje tako da ima i "povlašćene" korisnike, odnosno funkcije ili druge klase
koje imaju pravo pristupa njenim privatnim članovima.
Takve funkcije i klase nazivaju se prijateljima (enlgl. friends).

Prijateljske funkcije

Prijateljske funkcije (engl. friend functions) su funkcije koje nisu članice klase, ali imaju pristup do privatnih članova klase.
Te funkcije mogu da budu globalne funkcije ili članice drugih klasa.

Da bi se neka funkcija proglasila prijateljem klase, potrebno je u deklaraciji te klase navesti deklaraciju te
funkcije sa ključnom reči friend ispred.
Prijateljska funkcija se definiše na uobičajen način:

class X {
        friend void g (int,X&); // prijateljska globalna funkcija
        friend void Y::h ();     // prijateljska clanica druge klase
        int i;
    public:
        void f(int ip) {i=ip;}
};
 
void
g (int k, X &x) {
    x.i=k;         // prijateljska funkcija može da pristupa
}               // privatnim èlanovima klase
 
void 
main () {
    X x;
    x.f(5);       // postavljanje preko èlanice
    g(6,x);     // postavljanje preko prijatelja
}

Globalne funkcije koje predstavljaju usluge neke klase ili operacije nad tom klasom (najčešće su prijatelji te klase) nazivaju se klasnim uslugama (engl. class utilities).

Nema formalnih razloga da se koristi globalna (najčešće prijateljska) funkcija umesto funkcije članice.
Postoje prilike kada su globalne (prijateljske) funkcije pogodnije:

  • funkcija članica mora da se pozove za objekat date klase, dok globalnoj funkciji može da se dostavi i objekat drugog tipa, koji će se konvertovati u potrebni tip;
  • kada funkcija treba da pristupa članovima više klasa, efikasnija je prijateljska globalna funkcija;
  • ponekad je notaciono pogodnije da se koriste globalne funkcije (poziv je f(x)) nego članice (poziv je x.f()); na primer, max(a,b) je čitljivije od a.max(b);
  • kada se preklapaju operatori, često je jednostavnije definisati globalne (operatorske) funkcije nego članice.

"Prijateljstvo" se ne nasleđuje: ako je funkcija f prijatelj klasi X, a klasa Y izvedena (naslednik) iz klase X,
funkcija f nije prijatelj klasi Y.

Prijateljske klase

Ako je potrebno da sve funkcije članice klase Y budu prijateljske funkcije klasi X, onda se klasa Y deklariše
kao prijateljska klasa (friend class) klasi X.
Tada sve funkcije članice klase Y mogu da pristupaju privatnim članovima klase X, ali obratno ne važi ("prijateljstvo" nije simetrièna relacija):

class X {
    friend class Y;
    //...
};

Prijateljstvo" nije ni tranzitivna relacija: ako je klasa Y prijatelj klasi X, a klasa Z prijatelj klasi Y, klasa Z nije automatski prijatelj klasi X, već to mora eksplicitno da se naglasi (ako je potrebno).

Prijateljske klase se tipično koriste kada neke dve klase imaju tešnje međusobne veze. Pri tome je nepotrebno (i loše) "otkrivati" delove neke klase da bi oni bili dostupni drugoj prijateljskoj klasi, jer će na taj način oni biti dostupni i ostalima (ruši se enkapsulacija). Tada se ove dve klase proglašavaju prijateljskim.
Na primer, na sledeći način može se obezbediti da samo klasa Creator može da kreira objekte klase X:

class X {
    public:
        //...
    private:
        friend class Creator;
        X(); // konstruktor je dostupan samo klasi Creator
        //...
};

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.