Programování v C++ 1, 5. cvičení konstruktory, nevirtuální dědění 1 1 Fakulta jaderná a fyzikálně inženýrská České vysoké učení technické v Praze Zimní semestr 2018/2019
Přehled 1 2 3
Shrnutí minule procvičené látky spřátelené funkce statické metody a atributy konstruktory význam inicializační část neobjektová implementace zásobníku uložení dat do dynamického pole základní operace vložení na vrchol zásobníku získání hodnoty z vrcholu zásobníku odstranění vrcholu test prázdnosti nevýhody neobjektové implementace
Konstruktor metoda sloužící k inicializaci instance konstruktor nelze volat přímo, volán automaticky při: vytváření instancí v deklaraci nebo pomocí operátoru new předávání parametrů objektového typu hodnotou konverzích jméno konstruktoru stejné jako identifikátor třídy deklarace neobsahuje typ vrácené hodnoty konstruktory se nedědí konstruktor nesmí být virtuální ani statický konstruktor může mít parametry libovolného typu s výjimkou typu své třídy (ale reference na svou třídu parametrem být může => kopírovací konstruktor)
Inicializační část konstruktoru inicializace nestatických atributů předání parametrů konstruktorům předků syntaxe: inicializační_část identifikátor(seznam_výrazů nep ) identifikátor(seznam_výrazů nep ), inicializační_část inicializační část proběhne před vstupem do těla konstruktoru před vstupem do těla také zavolány konstruktory předků inicializace v inicializační části v pořadí, ve kterém jsou složky uvedeny v deklaraci třídy konstantní složky lze nastavit jen v inicializační části
Kopírovací konstruktor jeden z jeho parametrů je reference na danou třídu použije se při předávání parametrů objektového typu hodnotou v deklaracích typu T x = y; (y je typu T) a T x(y); pokud třída kopírovací konstruktor neobsahuje, překladač vytvoří vlastní: neobjektové složky přenese objektové složky okopíruje pomocí jejich kopírovacích konstruktorů veřejně přístupný a vložený takovýto konstruktor vytváří mělkou kopii často potřeba hluboká kopie => vlastní kopírovací konstruktor
Použití kopírovacího konstruktoru 1 class TStack{ 2 int size; 3 int sp; 4 int *d; 5 public: 6 TStack(int); 7 TStack(TStack &); 8 ~TStack(); 9 void push(int value); 10 int top() const; 11 void pop(); 12 bool empty() const; 13 bool full() const; 14 };
Hluboká kopie 1 TStack::TStack(int items) 2 : size(items), sp(-1), d(new int[size]) 3 {} 4 //kopirovaci konstruktor 5 TStack::TStack(TStack &s) 6 :size(s.size), sp(s.sp), d[new int[size]] 7 { 8 //vlastni kopirovani dat 9 for(int i = 0; i <= sp; i++) 10 this->d[i] = s.d[i]; 11 } podobný problém s operátorem =
Implementace dalších metod 1 void TStack::push(int value){ 2 sp++; 3 d[sp] = value; 4 } 5 int TStack::top() const{ 6 return d[sp]; 7 } 8 void TStack::pop(){ 9 sp--; 10 } 11 bool empty() const{ 12 return (sp == -1); 13 } 14 bool full() const{ 15 return (sp == size -1); 16 } opět chybí kontrola chybových situací (vkládání do plného zásobníku, vyjímání z prázdného zásobníku)
Použití zásobníku pro převod do šestnáctkové soustavy 1 void convert(int i, TStack &s){ 2 char digits[16] = {'0', '1', '2', '3', '4', '5', '6', 3 '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 4 //char digits[17] = "0123456789abcdef"; 5 while(i!= 0){ 6 s.push(digits[i % 16]); 7 i = i / 16; 8 } 9 } 10 void print(int i, TStack &s){ 11 cout << "Cislo " << i << " ma v sestnactkove soustave tvar: "; 12 while(!s.empty()){ 13 cout << s.top(); 14 s.pop(); 15 } 16 cout << endl; 17 } zásobník předáváme do funkcí odkazem!
Dědičnost v C++ vícenásobná dědičnost (potomek má více předků) nevirtuální dědění: předci neobsahují specifikace virtual class identifikátor : seznam_předků{tělo_třídy}; seznam_předků: identifikátor_přístupu nep identifikátor identifikátor_přístupu nep identifikátor, seznam_předků identifikátor přístupu: public, protected, private private: zděděné složky budou v potomkovi soukromé protected: zděděné složky budou v potomkovi chráněné public: zděděné veřejné a chráněné složky budou v potomkovi veřejné a chráněné ve všech případech budou soukromé složky předka v potomkovi nedostupné potomek při nevirtuálním dědění zdědí: všechny atributy svých předků všechny metody s výjimkou konstruktorů, destruktorů (a přetíženého operátoru =)
Poznámka: dědění a skládání Odvodit úsečku od bodu zděděním? Příklad: ne úsečka není speciální případ bodu úsečka má dva body relace is a relace has a 1 class Usecka{ 2 public: 3 //seznam verejnych polozek 4 private: 5 A, B: Bod; 6 };
Příklad: studenti a zaměstnanci 1 class Osoba{ 2 public: 3 Osoba(std::string, std::string, int); 4 void tisk(); 5 private: 6 std::string jmeno, prijmeni; int id; 7 }; 8 class Student : public Osoba{ 9 public: 10 Student(std::string, std::string, int, int); 11 int pocetkreditu() const{return kredity;} 12 private: 13 int kredity; 14 };
Implementace metod 1 Osoba::Osoba(string jm, string prij, int i) 2 : jmeno(jm), prijmeni(prij), id(i) 3 {} 4 5 void Osoba::tisk(){ 6 cout << jmeno << " " << prijmeni << endl; 7 } 8 9 Student::Student(string jm, string prij, 10 int i, int kr) 11 : Osoba(jm, prij, i), kredity(kr) 12 {}
Třída std::string hlavičkový soubor string pro práci s posloupností znaků přístup ke znaku pomocí operátoru [] některé metody: length: vrátí délku řetězce empty: testuje, zda je řetězec prázdný substr: vytvoří podřetězec append: připojí řetězec na konec řetězce lze použít přetížený operátor += compare: porovná řetězce find: vyhledává v podřetězci c_str: vrátí data jako const char* lze použít s proudy std::cin a std::cout
Základní pravidlo dědičnosti Potomek může zastoupit předka 1 void poslizpravu(osoba o, string zprava){ 2 cout << "Posilam zpravu " << zprava << endl; 3 cout << "Prijemce: "; o.tisk(); 4 } 5 int main(){ 6 string jmeno, prijmeni; 7 cout << "Zadejte jmeno a prijmeni: " << endl; 8 cin >> jmeno >> prijmeni; 9 Student s(jmeno, prijmeni, 42, 10); 10 poslizpravu(s, "Test"); 11 return 0; 12 }
Závěr konstruktory význam inicializační část kopírovací konstruktor příklad: objektová implementace zásobníku využití pro převod do šestnáctkové soustavy nevirtuální dědění přístup ke zděděným složkám dědění a skládání potomek může zastoupit předka třída string