====== C++ ====== ===== Skrývání dat ===== Data označená klíčovým slovem **private** jsou dostupná jen metodám z téže třídy. Ukážeme si, že to je ochrana proti neúmyslnému porušení přístupových práv. Pokud si někdo připraví strukturu s podobnými proměnnými, která jsou označena jako **public** \\ (a případně s podobnými virtuálními metodami, aby překladač použil podobné umístění proměnných uvnitř třídy), \\ lze se po přetypování ukazatele dostat k privátním datům. class Private { private: int x, y, z; public: Private ( ) : x (1), y (2), z (3) { } }; class Spy { public: int x, y, z; }; void privateFields () { Private * p = new Private; Spy * s = (Spy *) p; // ukazatel na tridu Private pretypujeme na ukazatel na tridy Spy cout << "x = " << s->x << endl; cout << "y = " << s->y << endl; cout << "z = " << s->z << endl; } x = 1 y = 2 z = 3 ===== Virtuální metody ===== Mějme třídu **Base** s vituální metodou **info** class Base { protected: string name; public: Base (string name0) : name (name0) { } virtual void info () { cout << "Base (name=" << name << ")" << endl; } }; Z ní odvozenou třídu **Derived** class Derived : public Base { private: int number; public: Derived (string name0, int number0) : Base (name0), number (number0) { } void info () { cout << "Derived (name=" << name << ", number=" << number << ")" << endl; } }; Vytvoříme instance obou tříd a oba ukazatele uložíme do proměnné typu ukazatel na **Base**. void baseAndDerived () { Base * a = new Base ("alpha"); Base * b = new Derived ("beta", 7); a->info (); b->info (); } Jelikož je metoda //info// virtuální je jedou zavolána metoda info ze třídy Base (**Base::info**) a podruhé **Derived::info** Base (name=alpha) Derived (name=beta, number=7) ===== Nahrazení metod obyčejnými funkcemi ===== Zkusme si představit jak probíhá překlad předchozího příkladu. \\ Třídy a metody přepíšeme na neobjektové struktury a funkce. \\ (Přepis jakoby do jazyka C, ale stále budu používat //new//, //string// a //cout// ) Prozatím nezvládneme virtuální metody. Vytvoříme si strukturu **BaseStruct**. Metodu Base::info bez parametrů předěláme na funkci **base_info** s parametrem ukazatel na BaseStruct. \\ Parametr pojmenujem **self** a bude náhradou za **this** v C++. \\ K členským datům se musíme dostávat přes self (např. **self->name**) \\ Volání metody **a->info()** nahradíme **base_info (a)** struct BaseStruct { string name; }; void base_info (BaseStruct * self) { cout << "Base (name=" << self->name << ")" << endl; } Konstruktor nahradí funkce **base_create**. \\ Inicializaci proměnných budu potřebovat i v konstruktoru odvozené třídy, tak si připravím funkci **base_init** void base_init (BaseStruct * self, string name0) { self->name = name0; } BaseStruct * base_create (string name0) { BaseStruct * self = new BaseStruct; base_init (self, name0); return self; } Pro odvozenou třídu naprogramujeme **DerivedStruct**. \\ Třída z které jsme naši třídu odvodili se skrývá v položce **base** typu BaseStruct. struct DerivedStruct { BaseStruct base; int number; }; Inicializace proměnných **derived_init** zavolá **base_init**. \\ Parametrem je adresa vnořené struktury **base**. void derived_init (DerivedStruct * self, string name0, int number0) { base_init (&(self->base), name0); self->number = number0; } Konstruktor přejde na **derived_create** DerivedStruct * derived_create (string name0, int number0) { DerivedStruct * self = new DerivedStruct; derived_init (self, name0, number0); return self; } Metoda info přistupuje k proměnným pomocí **self->number** nebo **self->base.name** void derived_info (DerivedStruct * self) { cout << "Derived (name=" << self->base.name << ", number=" << self->number << ")" << endl; } A zde je ekvivalent původní funkce baseAndDerived void withoutVirtual () { BaseStruct * b = base_create ("first"); DerivedStruct * t = derived_create ("second", 2); base_info (b); derived_info (t); } [[https://gitlab.fjfi.cvut.cz/culikzde/uop/-/blob/5f6f7e90/CPlusPlus/main.cc|program na gitlabu odpovídající právě popsané situaci]] ===== Virtuální metody pomocí ukazatelů na funkce ===== Přípravíme si předběžné deklarace struktury a funkce //base_info// struct BaseStruct; void base_info (BaseStruct * self); Zavedeme typ **BaseMethod** - ukazatel na funkci s jedním parameterem a to ukazatelem na //BaseStruct//. \\ Tedy ukazatel na funkci stejného typu jako //base_info//. (Pokud je virtuálních metod více, připravíme několik typů) typedef void (*BaseMethod) (BaseStruct *); Do struktury //BaseStruct// přidáme ukazatel na původně virtuélní metodu. struct BaseStruct { string name; BaseMethod info_method; }; Při inicializaci proměnných nastavíme ukazatel **info_method** na **base_info**. void base_init (BaseStruct * self, string name0) { self->name = name0; self->info_method = base_info; } BaseStruct * base_create (string name0) { BaseStruct * self = new BaseStruct; base_init (self, name0); return self; } void base_info (BaseStruct * self) { cout << "Base (name=" << self->name << ")" << endl; } Odvozená třída se nezmění. struct DerivedStruct { BaseStruct base; int number; }; Jen při inicializace přepíšeme **info_method** na derived_info void derived_info (DerivedStruct * self); void derived_init (DerivedStruct * self, string name0, int number0) { base_init (& (self->base), name0); self->base.info_method = (BaseMethod) derived_info; // přetypujeme - parametr je jiného typu self->number = number0; } Další funkce se nezmění DerivedStruct * derived_create (string name0, int number0) { DerivedStruct * self = new DerivedStruct; derived_init (self, name0, number0); return self; } void derived_info (DerivedStruct * self) { cout << "Derived (name=" << self->base.name << ", number=" << self->number << ")" << endl; } Volání **b->info()** nahradíme **b->info_method(b)** Do proměnné **t** uložíme novou instanci odvozené třídy. \\ Při volání metody info se musíme dostat k vnořenámu objektu základní třídy a také musíme přetypovat parametr funce. Pokud **t** uložíme do proměnné **v** typu ukazatel na základní třídu, \\ můžeme metodu info zavolat pomocí **v->info_method(v)** a přitom se zavolá //info// z odvozené třídy. void withVirtual () { BaseStruct * b = base_create ("first"); b->info_method (b); DerivedStruct * t = derived_create ("second", 2); t->base.info_method (& t->base); BaseStruct * v = & t->base; v->info_method (v); } Base (name=first) Derived (name=second, number=2) Derived (name=second, number=2) [[https://gitlab.fjfi.cvut.cz/culikzde/uop/-/blob/def3aaf1/CPlusPlus/main.cc|gitlab]]