Kurz C++ (13.)

 V dalÜφm pokraΦovßnφ kurzu o C++ dokonΦφme povφdßnφ o p°et∞₧ovßnφ operßtor∙.

13.1. P°et∞₧ovßnφ operßtor∙ - pokraΦovßnφ

    Minule jsme probrali unßrnφ operßtory a dnes se budeme  zab²vat operßtory binßrnφmi. Nejprve si p°etφ₧φme voln∞ p°et∞₧ovateln² operßtor, kter²m je nap°φklad operace sΦφtßnφ a odΦφtßnφ. V minulΘm dφle se vyskytla malß chybiΦka, za kterou se omlouvßm. V poslednφ ukßzce k≤du musφte vÜechny identifikßtory cplx nahradit identifikßtorem complex, jinak dostanete varovßnφ o nedefinovanΘm typu cplx.

13.1.1. Binßrnφ operßtory

    Tyto operßtory lze p°et∞₧ovat jako obyΦejnou funkci se dv∞ma parametry, alespo≥ jeden z nich musφ b²t objektovΘho nebo v²ΦtovΘho typu, nebo jako metodu s jednφm parametrem. Pro ukßzku pou₧ijeme op∞t t°φdu pro prßci s komplexnφmi Φφsly z minulΘho dφlu:

class complex {
private: double re, im;

public:
    complex(double _re = 0, double _im = 0):re(_re), im(_im) { }
    double Re() { return re; }
    double Im() { return im; }
    complex operator-(complex b) { return complex(re-b.re, im-b.im); }   
// Odecte dve komplexni cisla
};

Te∩ si m∙₧eme ukßzat pou₧itφ operßtoru:

void main(void)
{
    complex c1, c2, c;
    c = c1 - c2;
    c = c2 - 5;
    c = 5 - c1;
};

    Pokud p°elo₧φte takov²to k≤d, dostanete varovßnφ, ₧e neexistuje globßlnφ operßtor, kter² p°ijφmß typ complex za sv∙j parametr nebo neexistuje p°φsluÜnß konverze. Je to zp∙sobeno poslednφm °ßdkem, proto₧e Φφslo 5 nenφ objektov² typ a nelze pro n∞j tedy zavolat verzi operßtoru p°etφ₧enΘho jako metodu. Pro sprßvnou funkci je tedy t°eba vytvo°it globßlnφ operßtor, kter² bude normßlnφ funkcφ se dv∞ma parametry typu complex:

complex operator-(complex a, complex b) { return complex(a.Re()-b.Re(), a.Im()-b.Im()); }

    Po p°idßnφ tΘto verze operßtoru vÜak dostaneme op∞t chybu, p°ekladaΦ se nem∙₧e rozhodnout, kterou verzi pou₧φt. Je tedy nutnΘ pou₧φvat v₧dy jen jednu z verzφ. Zavolßnφ operßtorovΘ funkce je mo₧nΘ provΘst i alternativnφm zßpisem:

c = c1.operator-(c2);    // Pro pretizeni jako metoda
c = operator-(5, c1);   
// Pro obecnou funkci

    Zdrojov² k≤d naleznete v sekci Downloads (projekt PretezMinus).

13.1.2. Operßtory =, (), [], -> a (typ)

    Tyto operßtory lze p°et∞₧ovat pouze jako nestatickΘ metody objektov²ch typ∙. Ukß₧eme si, jak p°etφ₧it operßtory p°i°azenφ =, indexovßnφ [] a operßtor p°etypovßnφ (typ). Operßtor volßnφ funkce () se p°etypovßvß jako metoda s hlaviΦkou: operator()(parametry).

13.1.2.1. Operßtor p°i°azenφ

    O tomto operßtoru jsem se ji₧ n∞kolikrßt zmi≥oval, p°edevÜφm ve spojenφ s kopφrovacφm konstruktorem a problΘmem s m∞lkou kopiφ. P°ipomeneme si, ₧e pokud t°φda obsahuje dynamickou pam∞¥ a po₧adujeme, aby ka₧dß instance m∞la svoje vlastnφ, nesdφlenß data v tΘto pam∞ti, pak je nutnΘ vytvo°it kopφrovacφ konstruktor. Nynφ se podφvßme, co se stane pokud v programu pou₧ijeme operßtor p°i°azenφ nßsledujφcφm zp∙sobem:

void main(void)
{
    Buffer prvni(5);
    prvni.NastavNa('a');

    Buffer druhy(10);

    druhy.NastavNa('b');

    druhy = prvni;
}

    Buffer je nßm ji₧ znßmß t°φda z lekce Φφslo 11. K≤d vytvo°φ dv∞ instance t°φdy Buffer, kterΘ obsahujφ dynamicky alokovanΘ pole znak∙. Po provedenφ p°i°azenφ vÜak nastane problΘm, proto₧e pokud nemß t°φda explicitn∞ definovan² operßtor p°i°azenφ, dojde k vytvo°enφ implicitnφho. Implicitnφ p°i°azovacφ operßtor se chovß podobn∞ jako se implicitnφ kopφrovacφ konstruktor, tedy p°enese obsah Φlensk²ch prom∞nn²ch z instance prvni do instance druhy. To mß ale v tomto p°φpad∞ horÜφ nßsledky, ne₧li pou₧itφ implicitnφho kopφrovacφho konstruktoru. Instance druhy mß ji₧ alokovanou pam∞¥, na kterou ukazuje Φlenskß prom∞nnß t°φdy Buffer m_Buffer. P°i°azenφm pak tuto prom∞nnou p°epφÜeme, Φφm₧ ztratφme ukazatel na tuto pam∞¥, a dojde ke ztrßt∞ pam∞ti. V nßsledujφcφ ukßzce je tedy p°etφ₧en² operßtor p°i°azenφ:

Buffer& Buffer::operator=(Buffer& Buf)
{
    if(m_Buffer)
    {
        delete(m_Buffer);
        s_PocetByte -= m_Velikost;
        m_Velikost = 0;
    }

    m_Buffer = new char[Buf.m_Velikost];

    if(m_Buffer)
    {
        m_Velikost = Buf.m_Velikost;
        s_PocetByte += m_Velikost;

        for(int i = 0; i < m_Velikost; i++)
        {
            m_Buffer[i] = Buf.m_Buffer[i];
        }
    }

    return *this;
}

    Nejprve uvolnφme dynamickou pam∞¥ v objektu na levΘ stran∞ p°i°azenφ, potom vytvo°φme pole o velikosti pole objektu, kter² stojφ na pravΘ stran∞ operßtoru p°i°azenφ. Ov∞°φme jestli alokace pam∞ti prob∞hla sprßvn∞, pokud ano, pak p°ekopφrujeme obsah pole. Jazyk C++ nep°edepisuje, co mß operßtorovß funkce p°i°azenφ vracet, tato implementace vracφ ukazatel na objekt na levΘ stran∞ p°i°azovacφho operßtoru, tedy jako standardn∞ definovan² operßtor =. O ukazateli this se jeÜt∞ zmφnφm.

    Pro optimalizaci by jeÜt∞ bylo lepÜφ, pokud bychom nejprve zkontrolovali, jestli nßhodou nemajφ oba objekty stejnou velikost dynamicky alokovanΘ pam∞ti. Potom by odpadla nutnost uvol≥ovat a znovu alokovat pam∞¥, kterß ve srovnßnφ s jednφm porovnßnφm zabere mnohem vφce Φasu:

Buffer& Buffer::operator=(Buffer& Buf)
{
    if(m_Velikost == Buf.m_Velikost)
    {
        cout << "- just copying\n";
        s_PocetByte -= m_Velikost;
    }
    else
    {
        cout << "- deleting memory\n";
        if(m_Buffer)
        {
            delete(m_Buffer);
            s_PocetByte -= m_Velikost;
            m_Velikost = 0;
        }

        m_Buffer = new char[Buf.m_Velikost];
    }

    if(m_Buffer)
    {
        s_PocetByte += m_Velikost;
        m_Velikost = Buf.m_Velikost;

        for(int i = 0; i < m_Velikost; i++)
        {
            m_Buffer[i] = Buf.m_Buffer[i];
        }
    }

    return *this;
}

    Zdrojov² k≤d naleznete v sekci Downloads (projekt PretezPrir).

13.1.2.2. Operßtor indexovßnφ

    Tento operßtor je nßm dob°e znßm ve spojenφ s poli. V p°φkladu tento operßtor p°etφ₧φme tak, aby pracoval stejn²m zp∙sobem, bude tedy mo₧nΘ p°istupovat k prvk∙m v dynamicky alokovanΘm poli prßv∞ p°es indexy. Operßtor p°et∞₧ujeme ve tvaru: operator[](parametr). Nßsleduje p°etφ₧enß verze:

char& Buffer::operator[](int _indx)
{
    if(m_Buffer)
    {
        if(_indx < m_Velikost)
// Osetrime preteceni mezi
        {
            return m_Buffer[_indx];
        }
        else
        {
            cout << "m_Velikost je mensi nez index : " << m_Velikost << endl;
            return m_Buffer[m_Velikost - 1];
        }
    }
    else
    {
       
// jinak vytvorime pole, kam se index vejde
        cout << "vytvarim nove pole" << endl;
        m_Buffer = new char[_indx+1];
// Pole se indexuje od 0
        if(m_Buffer)
        {
            m_Velikost = _indx + 1;
            s_PocetByte += m_Velikost;
            return m_Buffer[_indx];
        }
        else
        {
            cout << "vracim zacatek pole, ale dojde k chybe pristupu do pameti" << endl;
            return *m_Buffer;
        }
    }
}

    Jak vidφte, tak je mo₧nΘ pomocφ p°etφ₧enφ provßd∞t kontrolu mezφ, ale jeÜt∞ by bylo pot°eba oÜet°it poslednφ p°φpad, kdy stejn∞ musφme vrßtit pam∞¥, kterß nenφ alokovanß. Bylo by mo₧nΘ pou₧φt nap°φklad v²jimek, ale v ka₧dΘm p°φpad∞ je lepÜφ, kdy₧ dojde k chybnΘmu p°φstupu do pam∞ti poka₧dΘ, ne₧ aby aplikace n∞kdy pracovala sprßvn∞ a n∞kdy by p°epsala ₧ivotn∞ d∙le₧itß data jinΘ aplikace nebo dokonce operaΦnφho systΘmu. JeÜt∞ je vhodnΘ zmφnit, ₧e parametr operßtoru m∙₧e b²t jakΘhokoliv typu, tak₧e nenφ problΘm indexovat pomocφ znak∙ nebo dokonce °et∞zc∙.

    Zdrojov² k≤d p°φkladu je v sekci Downloads (projekt PretezIndx).

13.1.2.3. Operßtor p°etypovßnφ

    Pomocφ tohoto operßtoru m∙₧eme definovat funkce, kterΘ p°evedou dan² objektov² typ na jin² objektov² nebo i neobjektov² typ. P°ekladaΦ potom m∙₧e tento operßtor vyu₧φvat i k implicitnφm konverzφm. Pro p°φklad nßm op∞t poslou₧φ t°φda Buffer, pro kterou si p°etφ₧φme operßtor p°etypovßnφ na (char *):

Buffer::operator char*()
{
    if(m_Buffer) { return m_Buffer; }
    return NULL;
}

    Nynφ m∙₧eme pou₧φvat funkce jako je nap°. memcpy() pro kopφrovßnφ blok∙ pam∞ti, p°iΦem₧ jako parametr pou₧ijeme jmΘno instance. P°ekladaΦ toti₧ pou₧ije pro implicitnφ konverzi nßmi definovan² operßtor.

    Zdrojov² k≤d p°φkladu je v sekci Downloads (projekt PretezPret).

13.2. Ukazatel this

    V t∞le ka₧dΘ nestatickΘ metody je k dispozici ukazatel se jmΘnem this, kter² ukazuje v₧dy na instanci, pro kterou byla ta danß metoda volßna. Tento ukazatel se p°edßvß jako skryt² parametr ka₧dΘ metod∞. Tuto hodnotu nesmφme v t∞le funkce m∞nit a p°ekladaΦ nßm to ani neumo₧nφ, proto₧e tento ukazatel nenφ l-hodnotou - nem∙₧e stßt na levΘ stran∞ p°i°azovacφho p°φkazu. V t∞le metody complex::Re()im stejn² v²znam jako this->im.

13.3. KlφΦovΘ slovo friend

    Toto klφΦovΘ slovo umo₧≥uje poruÜit p°φstupovß prßva k prvk∙m t°φdy, ve kterΘ je uvedeno. V odstavci pro p°etφ₧enφ binßrnφho operßtoru jsme vytvo°ili globßlnφ funkci operator-(), v jejφm₧ t∞le nelze p°istupovat k soukrom²m (private) nebo chrßn∞n²m (protected) prvk∙m t°φdy complex. Pomocφ friend:

class complex {
friend complex operator-(complex a, complex b);
private: double re, im;
public:
    complex(double _re = 0, double _im = 0):re(_re), im(_im) { }
    double Re() { return re; }
    double Im() { return im; }
};

complex operator-(complex a, complex b) { return complex(a.re + b.re, a.im + b.im); }

    Zdrojov² k≤d p°φkladu je v sekci Downloads (projekt Friends1).

    Takto lze zp°φstupnit prvky i jinΘ t°φd∞:  friend class JinaTrida;
    nebo jenom urΦitΘ metod∞ jinΘ t°φdy:  friend double JinaTrida::Fce1(complex&);.

    V obou p°φpadech m∙₧e Φlenskß funkce t°φdy JinaTrida::Fce1(complex&) p°istupovat k prvk∙m re a im t°φdy complex.

    Zdrojov² k≤d p°φkladu je v sekci Downloads (projekt Friends2).

13.4. Operace s cel²mi objekty

   V jazyce C se parametry p°enßÜφ hodnotou, kdy p°ekladaΦ vytvo°φ lokßlnφ kopii objektu, se kterou se pak v t∞le funkce pracuje. Dßle nabφzφ p°enos parametr∙ pomocφ ukazatele, kdy se lokßlnφ prom∞nnß nevytvß°φ. Druh² zp∙sob se pou₧φvß pro rozm∞rnß pole a struktury. Vzhledem k tomu, ₧e v C++ m∙₧e b²t objektov² typ velmi rozsßhl² (co se t²Φe dat) a vytvo°enφ kopie tak m∙₧e zabrat velmi mnoho Φasu a pam∞ti, nenφ vhodnΘ p°edßvat parametry hodnotou. Lze pou₧φt op∞t bu∩ ukazatele na typ p°φsluÜnΘ t°φdy (a k prvk∙m objektu p°istupovat pomocφ operßtoru ->) nebo jazyk C++ zavßdφ mo₧nost pou₧φt p°enos parametr∙ odkazem. Pro p°enos odkazem staΦφ parametr definovat jako referenci na typ p°φsluÜnΘ t°φdy (nap°. Buf& ref_buf) a v t∞le funkce nebo metody pak p°istupovat k objektu pomocφ standardnφ teΦkovΘ notace (nap°. ref_buf.NastavNa('a')). Tento zp∙sob takΘ umo₧≥uje m∞nit data vn∞ funkce, podobn∞ jako kdybychom p°edßvali parametr pomocφ ukazatele.

13.5. D∞diΦnost a kompozice

    Jak jsme si uvedli tak t°φda v objektov∞ orientovan²ch programovacφch jazycφch slou₧φ p°edevÜφm ke spojenφ dat a operacφ nad t∞mito atributy. Jednou z vlastnostφ konstrukce t°φdy je, ₧e k ji₧ stßvajφcφm prvk∙m m∙₧eme jednoduÜe p°idat dalÜφ metodu nebo datov² prvek. D∞diΦnost nßm prßv∞ nabφzφ tyto mo₧nosti s n∞kter²mi v²hodn²mi vlastnostmi. T°φdu, od kterΘ d∞dφme prvky (metody i data) naz²vßme t°φdou zßkladnφ, bßzovou nebo tΘ₧ rodiΦovskou, t°φda kterß vznikne se pak naz²vß t°φdou odvozenou, podt°φdou nebo potomkem. D∞diΦnost se vyu₧φvß zpravidla tak, ₧e od obecn∞jÜφ t°φdy odvodφme t°φdu specializovan∞jÜφ. Jin²m mo₧n²m zp∙sobem vytvß°enφ slo₧it∞jÜφch objekt∙ z jednoduch²ch je metoda kompozice, kdy jeden objekt obsahuje dalÜφ objektovΘ typy.

    Rozhodovßnφ o tom, jestli novou specializovan∞jÜφ t°φdu vytvo°it metodou d∞d∞nφ nebo kompozice nßm m∙₧e usnadnit takzvan² "je test". Vezm∞me si jako p°φklad dvojici CMotor a CMotoroveVozidlo. M∙₧eme si polo₧it otßzku: "Je motorove vozidlo motor?". Odpov∞dφ na tuto otßzku samoz°ejm∞ je, ₧e nenφ, motorovΘ vozidlo ale urΦit∞ obsahuje motor. NaÜe rozhodnutφ tedy bude pro metodu kompozice, kdy ve t°φd∞ CMotoroveVozidlo vytvo°φme Φlenskou prom∞nnou m_motor typu CMotor. Na kompozici nßs takΘ m∙₧e p°ivΘst "mß test", kdy se zeptßme: "Mß motorovΘ vozidlo motor?".

    Pro dvojici CZivocich a CPes budeme postupovat ·pln∞ stejn∞, polo₧φme si otßzku: "Je pes zivocichem?". Pokud se nepletu, tak odpov∞∩ na tuto otßzku bude ano a tedy pou₧ijeme mo₧nosti vytvo°it specializovan∞jÜφ t°φdu CPes od t°φdy CZivocich zd∞d∞nφm. T°φd∞ CPes pak p°idßme n∞kterΘ specißlnφ metody a data specifickß pro psy, mohla by to b²t t°eba prom∞nnß m_hlasitoststekotu a z metod nap°φklad Stekej().

    D∞diΦnost je tedy vlastn∞ o hledßnφ spoleΦn²ch vlastnostφ skupiny souvisejφcφch objekt∙. Pokud bychom programovali nap°φklad aplikaci pro sprßvu zoologickΘ zahrady mohli bychom jeÜt∞ nalΘzt spoleΦnΘ vlastnosti pro savce, ptßky apod. Od t∞chto specializovan∞jÜφch t°φd bychom pak odvozovali jednotlivß zvφ°ata. Pokud bychom hledali peΦliv∞, a s nejv∞tÜφ pravd∞podobnostφ s pomocφ zkuÜenΘho zoologa, dopracovali bychom se k rozΦlen∞nφ ₧ivoΦich∙ do systΘmu ₧ivoΦiÜnΘ °φÜe, se kterou se ka₧d² urΦit∞ ve Ükole setkal.

13.6. Co bude p°φÜt∞

    P°φÜt∞ si jeÜt∞ povφme n∞co o p°et∞₧ovßnφ a p°edefinovßnφ standardnφch operßtor∙ new a delete a dßle se budeme v∞novat d∞diΦnosti. TakΘ si ukß₧eme n∞jakΘ diagramy hierarchie t°φd. V nßsledujφcφch dφlech se pak budeme v∞novat obsluze v²jimek.

PřφÜtě nashledanou.

Ond°ej BuriÜin