====== 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]]