Retezec krok 5 - Zlepšení třídy Retezec
V tomto kroku se dozvíte něco o kopírovacím konstruktoru, přetěžování operátorů a něco málo i o friend funkcích a o ukazateli this.
Nejprve přidáme ještě jeden konstruktor. Vzpomeňte si, že konstruktor je funkce, která má stejné jméno jako její třída a je volána pokaždé když vzniká nový objekt dané třídy. Naše třída řetězec má zatím 2 konstruktory. Jeden nebere žádné parametry a slouží pro vytvoření prázdného řetězce. Druhý konstruktor bere jako parametr ukazatel na char, tedy textový řetězec a umožňuje vytvořit objekt třídy Retezec, který už obsahuje nějaký text. Teď přidáme třetí konstuktor, který bude jako parametr požadovat referenci na již existující objekt třídy Retezec. Tuším, že takovému konstruktoru se říká kopírovací a hodí se pro kopírování jednoho řetězce do druhého.
Otevřete soubor RETEZ.H a pod deklaraci druhého konsturktoru Retezec(char* text); přidejte následující řádek:
Retezec(const Retezec& retez); //deklarace třetího konstruktoru
Do souboru RETEZ.CPP přidejte definici těla třetího konstruktoru
Retezec::Retezec(const Retezec& ret)
{
strcpy(m_text, ret.m_text);
m_delka = ret.m_delka;
}
Dále budeme často potřebovat
přistupovat přímo k jednotlivým znakům řetězce, proto
přidáme členské funkce GetAt a SetAt. GetAt(int pos) bude
vracet znak na pozici určené parametrem pos a SetAt(int
pos,char zn) naopak nastaví znak zn na pozici pos.
Otevřete soubor RETEZ.H a do public části pod deklarace
konstruktorů přidejte toto:
char GetAt(int pos) { assert(pos < m_delka); return
m_text[pos]; };
void SetAt(int pos,char zn) { assert(pos < m_delka);
m_text[pos] = zn; };
Obě funkce jsou jednoduché a
proto jsme je definovaly jako inline přímo v souboru RETEZ.H.
Všimněte si makra assert, Toto makro vyhodnocuje předaný
výraz a pokud není pravdivý, zastaví program a vypíše na
monitor podobné hlášení: "Assertion failed [soubor]
[řádek] ". Zde slouží k ověření, že žádaný znak
skutečně patří do řetězce u funkce GetAt() a k ověření
že vkládáme znak na platnou pozici řetězce u SetAt(). Makro
může být z výsledného programu po odladění odstraněno vložením
direktivy #define NDEBUG před #include <assert.h>.
Vložte řádek
#include <assert.h>
na začátek souboru RETEZ.H. Inklůdování souborů do *.H sice není nejšťastnější, ale momentálně se mi nechce vymýšlet nějaké finty.
Další užitečnou věcí bude,
když půjde spojovat řetězce pomocí operátoru +, takže
budete moci napsat např.
retez3 = retez1 + " Ahoj " + retez2; kde retez1 až 3
jsou objekty naší třídy Retezec.
K tomu stačí přetížíme-li pro třídu
Retezec operátor +.
Přetěžování operátorů znamená, že definujete vlastní
funkci, která bude volána při použití daného operátoru a
provede pak potřebné operace. Jak je možné, že překladač
ví, kterou funkci má zavolat? K tomu slouží klíčové slovo operator před deklarací funkce; dalším
vodítkem je typ proměnných použitých s operátorem ve
výrazu.
V zásadě můžete funkce pro přetížení operátorů
definovat dvěma způsoby:
1.) jako členskou funkci dané třídy
2.) jako friend funkci.
Poznámka: friend neboli přátelská funkce je další
vymožeností jazyka C++. Umožňuje deklarovat ve třídě
nečlenskou funkci s klíčovým slovem friend. A tato funkce má pak přístup k datům
třídy jako by byla členskou funkcí.
Pro třídu Retezec použijeme oba typy funkcí -jednu členskou pro přetížení operátoru += a druhou friend pro operátor +.
Otevřete soubor TESTIK.H a
vložte do public pod řádek
void SetAt(int pos,char zn) { assert(pos < m_delka);
m_text[pos] = zn; };
deklaraci členské funkce pro operátor += a pod ni deklaraci
friend funkce pro operátor + :
const Retezec& operator+=(const Retezec& ret);
friend Retezec operator+(const Retezec& A, const Retezec&
B);
Členská funkce pro operator +=
přičte předaný řetězec ret ke svému řetězci uloženému
v členské proměnné m_text a vratí referenci na výsledný
řetězec, tj. vlastně sama na sebe.
Friend funkce sečte řetězce A a B a vrátí přímo výsledný
objekt třídy Retezec. Tato funkce vytváří lokální objekt
třídy Retezec pro součet a nemůže vrácet pouze referenci na
něj, protože tento objekt zanikne po skončení funkce a reference by tedy
odkazovala kdoví kam.
Při pohledu na deklarace obou
funkcí operátoru si snadno představíte, jak překladač
interpretuje řádek vašeho zdrojového kódu s danými
operátory:
Např. ret1 += ret2; se přeloží jako ret1.operator+=(ret2);
a ret1 + ret2 jako operator+(ret1,ret2);
Zbývá definovat funkce. Otevřete proto soubor RETEZ.CPP a přidejte na konec následující řádky:
//členská funkce pro
přetížení operátoru +=
const Retezec& Retezec::operator+=(const Retezec& ret)
{
strcat(m_text, ret.m_text);
m_delka = strlen(m_text);
return (*this);
}
//definice přátelské funkce
pro operátor +
Retezec operator+(const Retezec& A, const Retezec& B)
{
Retezec pom(A);
pom += B;
return pom;
}
U členské funkce operator+= jsme narazili na další nový
pojem -ukazatel this. Bez zbytečných keců this je
ukazatel na vlastní objekt - ukazatel sama na sebe. Funkce má
vracet referenci sama na sebe, což je realizováno příkazem
return (*this);
Druhá - friend - funkce využívá nejprve třetího
konstruktoru, který jsme právě vytvořili, ke konstrukci
pomocného objektu třídy Retezec jménem pom inicializovaného
přímo řetězcem A. Pak použije čerstvě napsanou funkci
operator+= k přičtení řetězce B.
S dosud vytvořenými funkcemi můžeme psát pouze výrazi, kde jsou na obou stranách objekty třídy Retezec. Abychom mohli k objektu Retezec přičítat také textové konstanty typu "ahoj", musíme definovat další verze funkcí pro operátory += a +, které budou jako parametr brát ukazatel na char.
V kroku 6 definujeme další funkce pro operátory a vyzkoušíme třídu Retezec.