Třídou se v C++ rozumí programová datová struktura. Uvnitř třídy můžete seskupit:
Přístup k seskupeným prvkům třídy mohou upravovat specifikátory přístupu. Třídy mezi sebou mohou vytvářet hierarchické struktury. Vhodným příkladem jsou například třídy v objektovám modelu AutoCADu. Například třída Application obsahuje data - objekty aplikace (Document, Collection ..), metody pro práci s těmito daty (ActiveDocument ..).
Při psaní aplikací v C++ pro ovládání AutoCADu se často setkáte s vytvářením objektů tříd. Objekt se vytvoří podle schématu.
klíč identifikátor_třídy [:seznam_předků] { deklarace_prvků } [instance1, instance2, ...];
klíč | |||
Povinná. Jedno z klíčových slov class, struct, union (strukt a union jsou považovány za speciální typy tříd). | |||
identifikátor_třídy | |||
Povinná. Identifikátor (jméno) deklarované třídy. | |||
:seznam_předků | |||
Seznam identifikátorů tříd, jejichž vlastnosti má nová třída zdědit. Přístup odvozené třídy k zděděným prvkům je možné upravit pomocí specifikátorů přístupu: public, protected a private. | |||
Položky v :seznamu_předků mají následující syntaxi: [virtual][specifikátor_přístupových_práv] jméno_bázové_třídy Překladač musí znát definice všech bázových tříd, aby dokázal vytvořit strukturu instance odvozené třídy. |
|||
deklarace_prvků | |||
Deklarace jednotlivých prvků třídy. | |||
instance | |||
Nepovinné, definice proměnných, ukazatelů na ně atd. tohoto typu (vytvářeného typu třída). Lze je definovat i později. |
Každá instance třídy dostane k dispozici paměť pro své datové složky. Instance se po vytvoření zároveň inicializují - datovým položkám se přiřadí hodnoty. Přiřazení hodnoty má za úkol speciální metoda tzv. KONSTRUKTOR. Třídy tvoří zároveň oblast platnosti datových prvků. Datové prvky zanikají spolu se svou instancí. O správný zánik se stará speciální metoda DESTRUKTOR.
Charakteristické vlastnosti datových složek:
jméno_třídy::statický_prvek;
Členské funkce by se daly přirovnat k "způsobu chování" objektů reálného světa, tzn. členské funkce dotváří chování objektu třídy.
Charakteristické vlastnosti členských funkcí:
virtual void jméno_funkce();
Výhodou virtuálních funkcí je, že objekty sdílející společnou základní třídu se mohou používat stejným způsobem. Například je možné definovat třídu s názvem Krivka s virtuální členskou funkcí Kresli a poté z ní odvodit třídy Kruznice a Obdelnik, každý s vlastní členskou funkcí Kresli. Každá instance objektu z těchto tříd volá členskou funkci Kresli. Kompilátor zajistí volání správné funkce Kresli.
class Zakladni { public: virtual void Zobraz() {cout<<100<<"\n";} }; class Odvozena { public: virtual void Zobraz() {cout<<200<<"\n";} // přetížení funkce definované v třídě Zakladni }; void Tiskni(Zakladni* zt) // vytisknutí jednotlivých hodnot { zt->Zobraz(); } void main() { Zakladni* pZakladni = new Zakladni; // vytvoření objektu Zakladni třída Odvozena* pOdvozena = new Odvozena; // vytvoření objektu Odvozena třída Tisk(pZakladni); // vytisknutí hodnoty z funkce Zobraz Zakladni třídy Tisk(pOdvozena); // vytisknutí hodnoty z funkce Zobraz Odvozené třídy }
Programovací jazyk C++ umožňuje uvést definici metody třídy mimo vlastní třídu. Třída musí obsahovat deklaraci metody. Hlavička definice funkce má tvar:
datový_typ_metody jméno_třídy::jméno_metody([parametry]) { tělo_metody }
Datový typ metody musí být stejný jako typ uvedený v deklaraci metody ve třídě.
class auto { public: auto(char* vyr, char* t, int rych, int c) { vyrobce = vyr; typ = t; rychlost = rych; cena = c; } auto() { vyrobce = "neznamy"; typ = "neznamy"; rychlost = 0; cena = 0; } ~auto(){}; char* vyrobce; char* typ; int rychlost; int cena; void tisk_informaci(); }; void auto::tisk_informaci() { cout << "Vyrobce: " << vyrobce << ", typ: " << typ << ", rychlost: " << rychlost << ", cena: " << cena << "\n"; }
Konstruktor je metoda, která inicializuje datové prvky instance, volá se automaticky. Konstruktorů může být ve třídě definováno libovolné množství (musí mít smysl!).
jméno_třídy ([seznam_parametrů])[:inicializační_část] tělo_konstruktoru
Základní pravidla při vytváření konstruktorů:
identifikátor_složky(hodnota) identifikátor_složky(seznam_parametrů_konstruktoru) identifikátor_třídy(seznam_parametrů_konstruktoru)
Kopírovací konstruktor slouží ke kopírování objektů (inicializuje instanci třídy pomocí jiné instance této třídy). Kopírovací konstruktor je volán s jedním parametrem typu reference na danou třídu.
identifikátor_třídy(identifikátor_třídy&)
Pokud není ve třídě deklarován vlastní kopírovací konstruktor, překladač vytvoří standardní kopírovací konstruktor, který překopíruje jednotlivé složky a použije k tomu jejich kopírovací konstruktory (prostě přenese složky základních typů). Při tomto postupu však může dojít k chybám, proto je lepší vytvořit kopírovací konstruktor vlastní.
Příklad vytvoření vlastního konstruktoru
#include <iostream.h> class pole { int delka; int* p; public: pole(int _delka): delka(_delka) {p = new int[delka];} pole(pole&); // deklarace kopírovacího konstruktoru ~pole() {delete p;} // další metody } pole::pole(pole& P):delka(P.delka) { // definice kopírovacího konstruktoru p = new int[delka]; for(int i = 0; i < delka; i++) p[i] = P.p[i]; } void main() { pole a(7); pole b(a); // použije se kopírovací konstruktor }
Kopírovací konstruktor zjistí,že ukazatel p bude v různých instancích ukazovat na různá pole.
Destruktor je metoda, volaná automaticky při zániku instance. Destruktor se stará o správný zánik instance.
~jméno_třídy () tělo_destruktoru
Základní pravidla při vytváření destruktorů
Způsob přístupu k jednotlivým prvkům třídy určují následující specifikátory:
Aby bylo možné s třídou v programu pracovat je nutné vytvořit tzv. proměnnou nebo konstantu instance třídy. Při vytváření instance vznikne konkrétní objekt dané třídy, vyhradí se mu paměť pro jeho datové složky a pak se jim přiřadí hodnoty. Při zřizování instance třídy se vždy volá konstruktor.
class auto { public: auto(char* vyr, char* t, int rych, int c) { vyrobce = vyr; typ = t; rychlost = rych; cena = c; } auto() { vyrobce = "neznamy"; typ = "neznamy"; rychlost = 0; cena = 0; } ~auto(){}; char* vyrobce; char* typ; int rychlost; int cena; }; auto Octavie; // vytvoření proměnné typu třída auto konstruktorem bez parametrů auto Felicie("Felicie", "Škoda", 156, 240000); // vytvoření proměnné typu třída auto konstruktorem s parametry
Každá instance třídy má vlastní kopii nestatických datových složek třídy. Naproti tomu metody třídy zůstávají uloženy v paměti počítače pouze jednou a používají je všechny instance společně. Aby mohla metoda pracovat s daty aktuální instance, předá ji překladač jako dodatečný parametr ukazatel na tuto (aktuální) instanci - ukazatel this. Ukazatel this se předává jako skrytý parametr, tj. nedeklaruje se v programu.
V jazyce C++ je ukazatel this využíván členskými funkcemi tříd při volání jiných funkcí, kde jim tento parametr předávají jako jeden z argumentů. Volaná funkce potom použije takto předaný parametr při zpětném volání funkcí objektu.
class trida { .... public: int hodnota; int funkce(); .... } int trida::funkce() { return hodnota; }
Vrácení návratové hodnoty z metody funkce příkazem return je v podstatě volání následujícího příkazu:
return this->hodnota;
V těle členských funkcí manipulujeme s ostatními složkami třídy bez jakýchkoliv omezení. Používáme při tom přímo jména složek:
class ukazka { private: int operand1, operand2; int funkce1() return operand1+operand2;} // ukázka přístupu uvnitř třídy, všimněte si, že // není uvedena instance pouze jméno proměnných .... }
Při přístupu ke složkám třídy z vnějšku je nutné uvést jméno instance jejíž složky budeme používat. Způsob přístupu ke složkám třídy:
jméno_instance.jméno_složky; ukazatel_na_instanci->jméno_složky;
Složka třídy, s kterou budeme manipulovat může být jednak datová složka nebo členská proměnná.
Přístup z vnějšku je regulován přístupovými právy (public, protected a private). Prvky, které jsou deklarovány jako chráněné nebo soukromé jsou pomocí přístupu přes instanci přístupné pouze ve spřátelených funkcích.
class ukazka { private: int operand1; int funkce1() {...} public: int operand2; int funkce2() {...} }; void main() { ukazka u, *ptr_u; int x; x = u.operand1; // chyba operand1 je private - soukromá x = u.funkce1(); // chyba funkce1 je private - soukromá x = ptr_u->operand1; // chyba operand1 je private - soukromá x = ptr_u->funkce1(); // chyba funkce1 je private - soukromá x = u.operand2; x = u.funkce2(); x = ptr_u->operand2; x = ptr_u->funkce2(); }
Představuje jeden ze základních rysů objektově orientovaného programování. Dědění tříd umožňuje definovat nové třídy na základě tříd, které již existují.
Třídy, od kterých odvozujeme nové třídy jsou nazývány rodičovské třídy. Odvozené třídy jsou označovány jako potomci nebo dceřinné třídy. Skupiny provázaných tříd (vztahem předek-potomek) jsou nazývány dědické hierarchie.
Odvozená třída "zdědí" vlastnosti svých předků - bude obsahovat všechny nestatické datové složky svých předků a bude moci používat jejich metody, typy a statické složky (pokud jí to dovolí přístupová práva).
Dědění se využívá v OOP pro vyjádření specifikace: Předek popisuje abstraktnější pojem, zatím co potomek popisuje konkrétnější, přesněji vymezený pojem. Z toho plyne důležité pravidlo:
!Potomek může zastoupit předka, nikoli naopak!
klíč identifikátor_třídy : SEZNAM_RODIČOVSKÝCH_TŘÍD { Složky_třídy };
klíč | |||
Povinná. Jedno z klíčových slov class, struct (union nemohou být předkem ani potomkem žádné třídy). | |||
identifikátor_třídy | |||
Povinná. Identifikátor (jméno) deklarované třídy. | |||
:SEZNAM_RODIČOVSKÝCH_TŘÍD | |||
Seznam identifikátorů tříd, jejichž vlastnosti má nová třída zdědit. Přístup odvozené třídy k zděděným prvkům je možné upravit pomocí specifikátorů přístupu: public, protected a private. | |||
Položky v :seznamu_předků mají následující syntaxi: [virtual][specifikátor_přístupových_práv] jméno_bázové_třídy [specifikátor_přístupových_práv][virtual] jméno_bázové_třídy Překladač musí znát definice všech bázových tříd, aby dokázal vytvořit strukturu instance odvozené třídy. |
|||
Složky_třídy | |||
Deklarace jednotlivých prvků třídy. |
Rodičovská třída musí být deklarována před tím než ji použijeme k dědění.