[[uop:impl]]
 

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

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)

gitlab

 
uop/impl.txt · Last modified: 2020/11/06 09:13 by 88.103.111.44
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki