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
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)
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); }
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)