C++ za 21 dní


MENU:
 1 - C++, objektové programovaní, zápis, kompilace a sestavení fungujícího programu
 2 - části programu a jejich spolupráce, funkce
 3 - PROMĚNNÉ A KONSTANTY - definice a deklarace, přiřazení hodnot, výpis hodnot
 4 - VÝRAZY A PŘÍKAZY - větvení, podmínky, bloky...
 5 - FUNKCE - deklarace, definice, lokální a globální proměnné...
 6 - Objektově orientované programovnání - třídy a objekty, členské funkce, konstruktory...
 7 - Více o toku programu - SMYČKY - while, do while, for, Větvení - switch...
 8 - UKAZATELE
 9 - ODKAZY - předávání hodnot funkcím hodnotou, odlišnosti od ukazatelů
 10 - Pokročilé funkce - přetížení členských funkcí, operátorů, funkce s třídami s dynamicky alokovanými proměnnými
 11 - Objektově orientovaná analýza a návrh - jazyk UML
 12 - Dědičnost - jak odvodit jednu třídu z jiné, chráněný přistup, virtuální funkce
 13 - Pole a propojené seznamy - deklarace polí, řetezce, práce s poli znaků, pole a ukazatele, aritmetika ukazatelů ve spojení s poli
 14 - xx

1 - Začínáme

C++

Je vyšší verze jazyka C, upravená pro OBJEKTOVĚ ORIENTOVANÉ PROGRAMOVÁNÍ (upravil Bjarne Stroustrup). Je plně kompatibilní s jazykem C.
CASE-SENSITIVE! rozlišuje velikost písmen!! Proto AHOJ je jiné než Ahoj a jiné než ahoj!!!

Objektově orientováné programování - vymodelování objektů spíše nežli dat. Objekty mají charakteristiky a schopnosti. Díky OOP je možné vytvářet znovpoužitelné komponenty tak, aby se dali jednoduše použít pro další vytváření programů. Nemusí být známá podstata fungování, ale jen způsob použití dané komponenty. Příkladem reálného objektu může být auto - má charakteristiky jako barvu, max. rychlost...a schopnosti. K tomu, abychom jej mohli používat nepotřebujeme vědět jak konkrétně funguje daný motor, převodovka atd.. Stačí umět "nastartovat a řídit"...
Základní vlastnosti OOP jsou:
ZAPOUZDŘENÍ - "skrývání dat", existence samostatné fungující jednotky. Objekt bereme jako fungující celek, který používáme aniž bychom znali jeho vnitřní datovou strukturu - vytváření tříd.
DĚDIČNOST A OPAKOVANÉ POUŽIVÁNÍ - možnost deklarovat nový typ, který je rozšířením jiného typu, má všechny jeho vlastnosti, ale může některé rozšířit nebo modifikovat.
MNOHOTVÁRNOST - polymorfismus - "stejné jméno funkce, může mít mnoho forem"

PSANÍ PROGRAMU

Programátor by měl nejdříve vypracovat návrhm než se pustí do psaní samotného programu.
Analýza problému, jeho úplné pochopení a tvorba návrhu jsou nezbytnými předpoklady pro úspěšně napsané aplikace.
K napsání zdrojového kódu stačí jednoduchý textový editor. obvyklé přípony jsou .c .cp .cpp Zdrojový kód se přeloží kompilátorem (překladačem) a pokud je kód bezchybný, je vytvořen objektový soubor obvykle .obj.
Pro vytvoření spustitelného souboru je nutné spustit linker, který tento soubor spojí s potřebnými knihovnami.

1. program

#include <iostream>

int main()
{
	std::cout << "Nazdar lidi!\n";
	return 0;
}
Program nedělá nic jiného než výpis na obrazovku.
Pokud je kompilátor starý, nezná nové standardní knihovny ANSI. Program se bude muset upravit.
#include <iostream.h>

int main()
{
	cout << "Nazdar lidi!\n";
	return 0;
}

2 - Anatomie programu C++

Analýza programu

# - značka pro preprocesor
Preprocesor - spouští se při každém spuštění kompilátoru a zpracovává řádky začínající znakem # před spuštěním samotného kompilátoru
include - instrukce pro preprocesor, která mu říká, že následuje název souboru, jehož obsah má vložit na toto místo programu
< > - soubor se má hledat na obvyklých místech (kam si kompilátor ukládá soubory)
Soubor iostream obsahuje objekty jako cout, který zajišťuje výpis na obrazovku
main () - hlavička funkce, tělo je uzavřeno mezi závorky {}
Funkce main() je speciální funkcí, protože je to první volaná funkce, volá ji systém. Proto každý program musí obsahovat tuto funkci. Tato funkce má být typu int, tudíž po ukončení vrací celočíselnou hodnotu. Jelikož žádnou hodnotu nechceme, vracíme 0 return 0;

Protože objekty různých tříd se můžou jmenovat stejně, používáme specifikátor oboru názvů! Řádek std::cout << "Nazdar lidi!\n"; tedy znamená, že má být použit objekt cout ze standardního oboru názvů pojmenovanéjho std
Syntaxe určení oboru je názevOboru::názevObjektu

<< v objektu cout značí přesměrování na obrazovku, tedy text uvedený za touto značkou bude vypsán na obrazovku. Řetezce se uzavírají do uvozovek, hodnoty bez uvozovek viz. dále.
\n je speciální znak, pro nový řádek

Objekt cout - stručně

Podrobněji v 17. dnu.
Vypis ruznych hodnot je uveden v nasledujicim programu:

#include <iostream>
int main()
{
	std::cout << "Dobry den.\n";
	std::cout << "Zde je cislo 5: " << 5 << "\n";
	std::cout << "Manipulacni objekt std::endl ";
	std::cout << "zpusobi zalomeni radku na obrazovce.";
	std::cout << std::endl;
	std::cout << "Zde je velke cislo:\t\t" << 70000;
	std::cout << std::endl;
	std::cout << "Tady je soucet 8 a 5:\t\t";
	std::cout << 8+5 << std::endl;
	std::cout << "A tady mame zlomek:\t\t";
	std::cout << (float) 5/8 << std::endl;
	std::cout << "Zde je velmi velke cislo:\t";
	std::cout << (double) 7000 * 7000 << std::endl;
	std::cout << "Nezapomente zmenit Jesse Liberty ";
	std::cout << "na vase vlastni jmeno...\n";
	std::cout << "Jesse Liberty je programator C++!\n";
	return 0;
}
Pro výpis výsledku po nějaké operaci, je někdy nutné uvést výpočet do závorek, tedy např.: std::cout << (8+5) << std::endl;
\t - vloží znak tabulátoru, pro formátování výstupu
endl - součást standtardní knihovny, napíše nový řádek.

Stálé opisování std::cout je pracné a může se stát zdrojem překlepů...lze tedy tento výpis vypustit, pokud kompilátoru předem sdělíme, odkud chceme dané objekty používat.

// Používání klíčového slova "using"
#include <iostream>
int main()
{
	using std::cout;
	using std::endl;

	cout << "Dobry den.\n";
	cout << "Zde je cislo 5: " << 5 << "\n";
	cout << "Manipulacni objekt endl ";
	cout << "zpusobi zalomeni radku na obrazovce.";
	cout << endl;
	cout << "Zde je velke cislo:\t\t" << 70000;
	cout << endl;
	cout << "Tady je soucet 8 a 5:\t\t";
	cout << 8+5 << endl;
	cout << "A tady mame zlomek:\t\t";
	cout << (float) 5/8 << endl;
	cout << "Zde je velmi velke cislo:\t";
	cout << (double) 7000 * 7000 << endl;
	cout << "Nezapomente zmenit Jesse Liberty ";
	cout << "na vase vlastni jmeno...\n";
	cout << "Jesse Liberty je programator C++!\n";
	return 0;
}

Použitím using std::cout jsme kompilátoru sdělili, že má jako objekt cout použít objekt standardní knihovny std...

Další možností je použití using namespace std;
tím kompilátoru sdělíme, že používáme všechny objekty ze třídy std...takže cout i endl budou použity správně ze std.

// Používání klíčového slova "namespace"
#include <iostream>
int main()
{
	using namespace std;

	cout << "Dobry den.\n";
	cout << "Zde je cislo 5: " << 5 << "\n";
	cout << "Manipulacni objekt endl ";
	cout << "zpusobi zalomeni radku na obrazovce.";
	cout << endl;
	cout << "Zde je velke cislo:\t\t" << 70000;
	cout << endl;
	cout << "Tady je soucet 8 a 5:\t\t";
	cout << 8+5 << endl;
	cout << "A tady mame zlomek:\t\t";
	cout << (float) 5/8 << endl;
	cout << "Zde je velmi velke cislo:\t";
	cout << (double) 7000 * 7000 << endl;
	cout << "Nezapomente zmenit Jesse Liberty ";
	cout << "na vase vlastni jmeno...\n";
	cout << "Jesse Liberty je programator C++!\n";
	return 0;
}

Komentáře

Lze použít komentáře dvojího druhu:
// - jsou řádkové komentáře stylu C++, vše za touto značkou, až do konce řádku je kompilátorem ignorováno
/* */ - párový komentář stylu C, vše mezi těmito značkami je ignorováno

#include <iostream>

int main()
{
	using std::cout;

	/* toto je komentář
	and sahá až po ukončující
	značku hvězdičky a lomítka */
	cout << "Nazdar lidi!\n";
	// tento komentář končí s koncem řádku
	cout << "Komentar skoncil!\n";

	// komentáře s dvojitým lomítkem mohou být na samostatném řádku
	/* stejně jako komentáře s lomítkem a hvězdičkou */
	return 0;
}

Komentáře by neměly říkat co se děje, ale PROČ se to děje!

FUNKCE

Každá užitečná funkce musí být v programu zavolána. Funkce main() je volána systémem, ale ostatní funkce musí být zavolány uživatelem.
Postupně se provádí řádky kódu ve funkci main(), a pokud je zavolána funkce, program se rozvětví, prochází tělem funkce a po jejím dokončení se vrací za místo, odkud byla funkce vyvolána.
Ilustrace volání funkce:
#include <iostream>

// funkce IlustracniFunkce
// vytiskne velmi užitečnou zprávu
void IlustracniFunkce()
{
	std::cout << "Uvnitr Ilustracni funkce\n";
}

// funkce main - vytiskne zprávu, pak zavolá
// ilustrační funkci a nakonec vytiskne
// druhou zprávu
int main()
{
	std::cout << "Ve funkci main\n";
	IlustracniFunkce();
	std::cout << "Zpet ve funkci main\n";
	return 0;
}

Použití funkcí
Funkce bud vrací skutečnou hodnotu, nebo nic=prázdná hodnota typu void. Funkce, která jen vypíše zprávu by měla být typu void (vyjímka je main())

Funkce se skládá z hlavičky a těla
Hlavička: obsahuje návratový typ, název funkce, parametry
Pomocí parametrů je možné předávat hodnoty funkci. Návratová hodnota je uvedena v příkazu return hodnota;

Příklad pro součet dvou čísel s použitím funkcí:

#include <iostream>
int Soucet(int x, int y)
{
	std::cout << "Ve funkci Soucet(), prijato " << x << " a " << y << "\n";
	return (x+y);
}

int main()
{
	using std::cout;
	using std::cin;

	cout << "Ve funkci main()!\n";
	int a, b, c;
	cout << "Vlozte dve cisla: ";
	cin >> a;
	cin >> b;
	cout << "\nVolani funkce Soucet()\n";
	c=Soucet(a,b);
	cout << "\nZpet ve funkci main().\n";
	cout << "c bylo nastaveno na " << c;
	cout << "\nKonec...\n\n";
	return 0;
}


cin >> slouží k získání vstupních hodnot od uživatele.
Výstupem může tedy být:
Ve funkci main()!
Vlozte dve cisla: 2 7

Volani funkce Soucet()
Ve funkci soucet(), prijato 2 a 7

Zpet ve funkci main().
c bylo nastaveno na 8
Konec...

3 - proměnné a konstanty

Proměnné

Při definici proměnné, musíme udat typ proměnné. Podle typu si kompilátor vyhradí místo v paměti. Typ udává také rozsah přípustných hodnot. Na různých počítačích se můžo lišit velikosti typů. Obvyklé velikosti a rozsahy jsou:

TYPY PROMĚNNÝCH
TYP VELIKOST (Bajtů)  HODNOTY
bool 1 true, false
unsigned short int 2 0..65 535
short int 2 -32 768..32 767
unsigned long int 4 0..4 294 967 295
long int 4 -2 147 483 648..2 147 483 647
int (16b) 2 -32 768..32 767
int (32b) 4 -2 147 483 648..2 147 483 647
unsigned int (16b) 2 0..65 535
unsigned int (32b) 4 0..4 294 967 295
char 1 256 znakových hodnot
float 4 1,2e-38..3,4e38
double 8 2,2e-308..1,8e308

Pro zjištění hodnot na vačem počítači je možné použít tento program:

#include <iostream>

int main()
{
	using std::cout;

	cout << "Velikost typu int je:\t\t"
		<< sizeof(int) << " bajtu.\n";
	cout << "Velikost typu short je:\t\t"
		<< sizeof(short) << " bajtu.\n";
	cout << "Velikost typu long je:\t\t"
		<< sizeof(long) << " bajtu.\n";
	cout << "Velikost typu char je:\t\t"
		<< sizeof(char) << " bajtu.\n";
	cout << "Velikost typu float je:\t\t"
		<< sizeof(float) << " bajtu.\n";
	cout << "Velikost typu double je:\t"
		<< sizeof(double) << " bajtu.\n";
	cout << "Velikost typu bool je:\t\t"
		<< sizeof(bool) << " bajtu.\n";

	return 0;
}

Kde sizeof(argument) vrací velikost argumentu v bajtech.

SIGNED a UNSIGNED
Pokud nejsou proměnné označeny unsigned, tak jsou považovány za signed = se znaménkem. Rozsah (podle počtu bajtů) se dělí na půl (záporné, kladné hodnoty). Pokud nepotřebujeme záporné hodnoty, označíme proměnnou jako unsigned, čímš posuneme její horní hranici. PŘETEČENÍ HODNOT Pokud je do proměnné přiřazena větší hodnota, než je maximum, pak se hodnota počítá znovu od minima!!!!
Př.: Kdybychom měli proměnnou a typu o rozsahu -5 až 5 a provedli přiřazení a=7, pak by hodnoto a byla -4. 5+1->-5+1->-4

Definice proměnné

typ jménoProměnné;
kde jméno proměnné je libovolná kombinace písmen a číslic, NESMÍ OBSAHOVAT MEZERY! Lze definovat více proměnných současně.
Př.:

int main()
{
	unsigned short int promenna;
	long a,b,treti_promenna;

	...	
	return promenna;
}

Proměnné lze definovat a rovnou inicializovat: unsigned short a=5;
a bude typu int a bude mít hodnotu 5.

long a short je zkraceny zapis pro long int a short int
Přiřazení hodnoty: promenna = hodnota

Př.:

// Ukázka použití proměnných
#include <iostream>

int main()
{
	using std::cout;
	using std::endl;

	unsigned short int Sirka = 5, Delka;
	Delka = 10;

	// vytvoření proměnné typu unsigned short a její inicializace
	// na výsledek násobení šířky délkou
	unsigned short int Plocha = (Sirka * Delka);

	cout << "Sirka: " << Sirka << "\n";
	cout << "Delka: " << Delka << endl;
	cout << "Plocha: " << Plocha << endl;
	return 0;
}

TYPEDEF

Slouží k vytváření aliasů, pro často psané výrazy. Např.:

// Ukázka použití klíčového slova typedef
#include <iostream>

typedef unsigned short int USHORT;    // definice USHORT

int main()
{

	using std::cout;
	using std::endl;

	USHORT Sirka = 5;
	USHORT Delka;
	Delka = 10;
	USHORT Plocha = Sirka * Delka;
	cout << "Sirka: " << Sirka << "\n";
	cout << "Delka: " << Delka << endl;
	cout << "Plocha: " << Plocha << endl;
	return 0;
}

kdy místo opisování "unsigned short int" napíšeme kratší USHORT

Program na výpis znaků podle ordinálních hodnot:
#include <iostream>
int main()
{
	for (int i = 32; i<128; i++)
		std::cout << (char) i;
	return 0;
}

SPECIÁLNÍ ZNAKY

Slouží k formátování výstupu.
Přiřazení spec. znaku do proměnné: char znakTab='\t';
Speciální znaky
ZNAK Význam
\a pípnutí
\b smazání předchozího znaku
\f posun o stránku
\n nový řádek
\r návrat vozíku
\t tabulátor
\v vertikální tabulátor
\' apostrof
\" uvozovky
\? otazník
\\ zpětné lomítko
\000 oktalový zápis
\xhhh šestnáctkový zápis

KONSTANTY

Konstanty uchovávají hodnotu, jež se v programu nedá měnit. Literární (doslovná) konstanty - např 5, 22...
Symbolická konstanta - reprezentovaná názvem

DEFINICE KONSTANTY
#define konstanta 20 - vytvoří konstantu konstanta s hodnotou 20
const unsigned short int konstanta=20; - definuje konstantu s udaným typem, takže kompilátor může kontrolovat správné použití.

VÝČTOVÉ KONSTANTY

enum Barva { CERVENA, MODRA, ZELENA}; - výčtový typ Barva, může mít hodnoty CERVENA=0, MODRA=1 atd..
Hodnoty lze uvést přímo: enum Barva { CERVENA=100, MODRA, ZELENA=200}; kde MODRA=101

Příklad použití výčtu:

#include <iostream>
int main()
{
	enum Dny { Pondeli, Utery, Streda,
		Ctvrtek, Patek, Sobota, Nedele };

	Dny dnes;
	dnes = Pondeli;

	if ( dnes == Sobota || dnes == Nedele )
		std::cout << "\nMiluji vikendove dny!\n";
	else
		std::cout << "\nVzhuru do prace.\n";

	return 0;
}

4 - výrazy a příkazy

Příkazy

Všechny příkazy, i prázdný příkaz jsou ukončeny STŘEDNÍKEM. Prázdné znaky v příkazu jsou ignorovány a slouží jen k úpravě čitelnosti kódu. Takže:
x=y+z;
je totéž jako
x = y + z;

Bloky je část kódu ve složených závorkách {}. Všude, kde může být jednoduchý příkaz, může být použit složený příkaz (blok)
{
	pom=a;
	a=b;
	b=pom;
}
je blok, který se provede jako jeden příkaz (dojde k výměně obsahu proměnných a,b)

Výrazy

Výraz je vše, co se vyhodnotí na nějakou hodnotu. tedy např.: 5, PI, x=a+b; ...
x=y=a+b;
bude vyhodnoceno a+b a výsledek součtu bude přiřazen do y a následně do x.
Příklad kódu:
#include <iostream>
int main()
{
	using std::cout;
	using std::endl;

	int a=0, b=0, x=0, y=35;
	cout << "a: " << a << " b: " << b;
	cout << " x: " << x << " y: " << y << endl;
	a = 9;
	b = 7;
	y = x = a+b;
	cout << "a: " << a << " b: " << b;
	cout << " x: " << x << " y: " << y << endl;
	return 0;
}

OPERÁTORY

Symboly, díky nimž kompilátor provádí akce nad operandy..
Operátor přiřazení je symbol = , takže lze např. zapsat x=5; kdy je proměnné x přiřazena hodnota 5. To co je vpravo se přiřadí nalevo. NELZE 5=x !!!

Matematické operátory


Matematické operátory
Symbol Význam
+ Sčítání
- Odčítání
* Násobení
/ Dělení
% Zbytek po
celočíselném dělení


Příklad:
// demonstruje odečítání a
// přetečení celočíselné proměnné
#include <iostream>

int main()
{
	using std::cout;
	using std::endl;

	unsigned int rozdil;
	unsigned int velkeCislo = 100;
	unsigned int maleCislo = 50;
	rozdil = velkeCislo - maleCislo;
	cout << "Rozdil je: " << rozdil;
	rozdil = maleCislo - velkeCislo;
	cout << "\nRozdil nyni: " << rozdil << endl;
	return 0;
}

Výstup bude:
Rozdíl je: 50
Rozdíl nyní: 4294968246

Druhý, možná překvapivý výsledek je důsledkem přetečení rozsahu - dostáváme záporné číslo pro neznaménkový typ, tudíž je hodnota odečtena od maximální hodnoty daného typu.

Inkrementace a dekrementace

Zmena hodnoty proměnné lze provést několika způsoby:
Mějme definovánu proměnnou: int zvys=2;
zvys = zvys + 2;
zvys: 4
zvys = zvys - 3;
zvys: 1
zvys += 5;
zvys: 6
zvys -= 2;
zvys: 4
zvys /= 2;
zvys: 2
atd... kdy operátor += přičte k hodnotě vlevo hodnotu vpravo

Zvýšení/snížení o 1
K tomuto účelu slouží kratší operátor ++ nebo --

POZOR NA PREFIXOVÝ A POSTFIXOVÝ ZÁPIS

Máme-li proměnnou a=3 a provedeme přiřazení:
b = ++a; pak b má hodnotu 4 stejně jako a.
Pokud bychom ale napsali:
b = a++; pak by b mělo hodnotu 3, ale a by se již zvýšilo na 4!!!!


Příklad na použití inkrementace:
// používání operátoru inkrementace a dekrementace
// (zvýšení a snížení hodnoty o 1)
// s prefixovým a postfixovým zápisem
#include <iostream>
int main()
{
	using std::cout;

	int mujVek = 39;      // inicializace dvou celočíselných proměnných
	int tvujVek = 39;
	cout << "Mam " << mujVek << " let.\n";
	cout << "Mas " << tvujVek << " let.\n";
	mujVek++;         // postfixová inkrementace
	++tvujVek;        // prefixová inkrementace
	cout << "Uplynul jeden rok...\n";
	cout << "Mam " << mujVek << " let.\n";
	cout << "Mas " << tvujVek << " let.\n";
	cout << "Uplynul dalsi rok...\n";
	cout << "Mam " << mujVek++ << " let.\n";
	cout << "Mas " << ++tvujVek << " let.\n";
	cout << "Vytiskneme to znovu.\n";
	cout << "Mam " << mujVek << " let.\n";
	cout << "Mas " << tvujVek << " let.\n";
	return 0;
}

Mam 39 let.
Mas 39 let.
Uplynul jeden rok...
Mam 40 let.
Mas 40 let.
Uplynul dalsi rok...
Mam 40 let.
Mas 41 let.
Vytiskeme to znovu.
Mam 41 let.
Mas 41 let.

Priorita operátorů

Postup při vyhodnocování výrazů se řídí jejich prioritou. Pokud chceme prioritu zvýšit, uzavřeme výraz do kulatých závorek (i vnořených).
Např.: x = 5+3+8*9+6*4;
x: 104 

Je totéž jako x = 5+3+(8*9)+(6*4);
Uzávorkování ZVYŠUJE ČITELNOST A ORIENTACI VE VÝRAZU
Priorita operátorů
Postavení Název Operátor
1 stanovení oboru platnosti ::
2 výběr členů,dolní index,volání funkcí,
postfixová inkrementace a dekrementace
.--> () ++ --
3 sizeof, prefixová inkrementace a dekrementace
komplement, AND, NOT, unární minus a plus
adresa a zrušení odkazů, new, new[],delete,
delete[], typová konverze, sizeof()
++ -- ^ ! - + & * ()
4 výběr člena pro ukazatel .* .->*
5 násobení, dělení, dělení se zbytkem * / %
6 sčítání odečítání + -
7 bitový posun vlevo a vpravo << >>
8 relační operátory nerovnosti < <= > >=
9 rovnost, nerovnost == !=
10 bitový operátor AND (a zároveň) &
11 bitový operátor XOR (výhradně nebo) ^
12 bitový operátor OR (nebo) |
13 logický operátor AND (a zároveň) &&
14 logický operátor OR (nebo) ||
15 podmínkový ternární operátor ?:
16 operátory přiřazení = *= /= %= += -= <<= >>= &= |= ^=
17 operátor throw throw
18 operátor čárka ,

PRAVSPANOST

Pro vyhodnocování pravSPANosti výrazů byl zaveden typ bool, který může nabývat dvou hodnot: false, true
K zápisu takovýchto výrazů slouží relační operátory
Výraz je nepravSPANý, pokud je jeho hodnota 0, jinak je pravSPANý. Tedy if (x) je splněno pro každé x různé od nuly
Relační operátory
Název Operátor
Rovná se ==
Nerovná se !=
Vetší než >
Vetší nebo rovno>=
Menší než <
Menší nebo rovno<=

Příkaz if

If umožnuje větvení programu na základě pravSPANosti výrazu.

if (výraz) příkaz;
Příkaz se provede pokud je výraz pravSPANý.

Místo jednoduchého příkazu může následovat blok příkazů:
if (výraz) 
{
	příkaz;
příkaz2;
}

else

Tato klauzule umožnuje větvit program pro nesplnění podmínka.

if (podminka)
{
 	příkazy po splnění...
}
else
{
 	příkazy pokud není podminka pravSPANá
}

Příklad.:
//  složitý vnořený
// příkaz if
#include <iostream>
int main()
{
	// Požádá o zadaní dvou čísel
	// Přiřadí čísla do proměnných prvniCislo a druheCislo
	// Pokud je prvniCislo větší než druheCislo,
	// zjistí, zda jsou dělitelná beze zbytku
	// Pokud jsou, zjistí, zda se jedná o stejné číslo

	using namespace std;

	int prvniCislo, druheCislo;
	cout << "Zadejte dve cisla.\nPrvni: ";
	cin >> prvniCislo;
	cout << "\nDruhe: ";
	cin >> druheCislo;
	cout << "\n\n";

	if (prvniCislo >= druheCislo)
	{
		if ( (prvniCislo % druheCislo) == 0) // dělitelná beze zbytku?
		{
			if (prvniCislo == druheCislo)
				cout << "Jsou stejna!\n";
			else
				cout << "Jsou delitelna beze zbytku!\n";
		}
		else
			cout << "Nejsou delitelna beze zbytku!\n";
	}
	else
		cout << "Druhe cislo je vetsi!\n";
	return 0;
}

Logické operátory

Slouží k vytváření složitějších pravSPANostních výrazů.
Logické operátory
Operátor Symbol Příklad
AND && vyraz1 & vyraz2
OR || vyraz1 | vyraz2
NOT ! !vyraz1

AND Celý výraz je pravSPANý pokud jsou oba výrazy pravSPANé
if ((x==5) && (y==5)) je pravSPANý, pokud x i y mají hodnotu 5.

OR K pravSPANosti celého výrazu, stačí aby byl pravSPANý jen jeden výraz. if ((x==5) || (y==5)) je pravSPANý, pokud aspoň jedna proměnná má hodnotu 5.

NOT Logický výraz je pravSPANý pokud testovaný výraz je nepravSPANý.
if (!(x==5)) je pravSPANý, x je různé od 5.
Je to totéž jako if (x!=5)

Zkrácené vyhodnocování - znamená že výraz se vyhodnocuje, dokud není jisté že nemůže nabýt pravSPANé hodnoty...např. u spojky AND se vyhodnocení ukončí při prvním nesplnění podmínky, protože je jisté, že složený výraz bude mít hodnotu false.

Při vyhodnocování záleží na prioritě operátorů

Podmínkový operátor

Operátor pracující se třemi výrazy.
(výraz1) ? (výraz2) : (výraz3);
Pokud je výraz1 pravSPANý, vrátí hodnotu výrazu2, jinak vrátí výraz3!
Příklad:
// demonstruje operátor podmínky
//
#include <iostream>
int main()
{
	using namespace std;

	int x, y, z;
	cout << "Vlozte dve cisla.\n";
	cout << "Prvni: ";
	cin >> x;
	cout << "\nDruhe: ";
	cin >> y;
	cout << "\n";

	if (x > y)
		z = x;
	else
		z = y;

	cout << "z: " << z;
	cout << "\n";

	z =  (x > y) ? x : y;

	cout << "z: " << z;
	cout << "\n";
	return 0;
}

5 - Funkce

Deklarace funkce

Funkce se musí nejdřív deklarovat a pak definovat. Rovněž nelze volat funkci, která nebyla ještě deklarována. Deklarace se zve prototyp
Jsou tři možné způsoby deklarace funkce: Prototyp má tvar: návratový_typ jméno_funkce(seznam parametrů);
Středník je povinný!!!
Je možné vynechat názvy parametrů. MUSÍ SE SHODOVAT TYPY NA POZICÍCH!

Lze deklarovat funkci: lonf Plocha(int delka,int sirka); nebo taky
long Plocha(int, int);
V definici funkce se mohou parametry jmenovat jinak.

Definice funkce

Skládá se z hlavičky funkce - podobná jako deklarace, ale MUSÍ mít uvedeny názvy parametrů a není ukončena středníkem; a z těla - množina příkazů uzavřených v bloku {}.
// demonstruje používání prototypu funkcí

#include <iostream>
int Plocha(int delka, int sirka); //prototyp funkce

int main()
{
	using std::cout;
	using std::cin;

	int delkaDvorku;
	int sirkaDvorku;
	int plochaDvorku;

	cout << "\nJak siroky je Vas dvorek? ";
	cin >> sirkaDvorku;
	cout << "\nJak dlouhy je Vas dvorek? ";
	cin >> delkaDvorku;

	plochaDvorku= Plocha(delkaDvorku,sirkaDvorku);

	cout << "\nVas dvorek ma plochu ";
	cout << plochaDvorku;
	cout << " ctverecnich metru.\n\n";
	return 0;
}

int Plocha(int d, int s)
{
	return d * s;
}

Lokální proměnné

Mimo proměnných předaných funkci ve formě parametrů, je možné uvnitř funkce definovat lokální promněnné, které jsou platné jen v dané funkci. Lokální proměnné lze definovat pro libovolný BLOK, a jejích hodnota bude platit jen v tomto bloku. Pokud existují stejně pojmenované proměnné na širším oboru platnosti, jsou blokovány.
Např.: pokud je ve funkci definována proměnná int x=5 a vytvoříme ve funkci blok a znovu definujeme int x=7, pak po navratu z bloku do funkce má x hodnotu 5!
Př.:
// využití lokálních proměnných a parametrů
#include <iostream>

float Prevod(float);
int main()
{
	using namespace std;

	float TeplotaFer;
	float TeplotaCel;

	cout << "Vlozte prosim teplotu v jednotkach Fahrenheita: ";
	cin >> TeplotaFer;
	TeplotaCel = Prevod(TeplotaFer);
	cout << "\nA zde je teplota v jednotkach Celsia: ";
	cout << TeplotaCel << endl;
	return 0;
}

float Prevod(float TeplotaFer)
{
	float TeplotaCel;
	TeplotaCel = ((TeplotaFer - 32) * 5) / 9;
	return TeplotaCel;
}
// s oborem platnosti bloku

#include <iostream>

void mojeFunkce();

int main()
{
	int x = 5;
	std::cout << "\nV main je hodnota x: " << x;

	mojeFunkce();

	std::cout << "\nZpatky v main, hodnota x je: " << x;
	return 0;
}

void mojeFunkce()
{
	int x = 8;
	std::cout << "\nUvnitr mojeFunkce, lokalni promenna x: " << x << std::endl;

	{
		std::cout << "\nV bloku funkce mojeFunkce, hodnota x je: " << x;

		int x = 9;

		std::cout << "\nZcela lokalni promenna x: " << x;
	}

	std::cout << "\nMimo blok, uvnitr mojeFunkce, x: " << x << std::endl;
}

V main je hodnota x: 5
Uvnitr mojeFunkce , lokalni promenna x: 8

V bloku funkce mojeFunkce, hodnota x je: 8
Zcela lokalni promenna x: 9
Mimo blok, unvitr mojeFunkce, x: 8
Zpatky v main, hodnota x je: 5

Globální proměnné

Používají se pro předávání hodnot pro více funkcí, místo stálého předávání parametrů. Jejich hodnotu lze změnit uvnitř každé funkce! Proto může dojít k chybám!
Definují se vně funkcí
#include <iostream>
void mojeFunkce();           // prototyp

int x = 5, y = 7;            // globální proměnné
int main()
{
	using std::cout;

	cout << "x ve funkci main: " << x << "\n";
	cout << "y ve funkci main: " << y << "\n\n";
	mojeFunkce();
	cout << "Navrat z mojeFunkce!\n\n";
	cout << "x ve funkci main: " << x << "\n";
	cout << "y ve funkci main: " << y << "\n";
	return 0;
}

void mojeFunkce()
{
	using std::cout;

	int y = 10;

	cout << "x ve funkci mojeFunkce: " << x << "\n";
	cout << "y ve funkci mojeFunkce: " << y << "\n\n";
}

ARGUMENTY

Argumenty předávané funkci jsou LOKÁLNÍ. Jde o předávání hodnotou. Tedy změna hodnoty argumenty ve funkci se neprojeví ve zbytku programu. Nelze tedy vracet hodnotu jinak než pomocí return hodnota;
Jako argument lze použít i další funkci - musí mít návratovou hodnotu!
Jiné metody než předání hodnotou a možnost vrácení více hodnot v 8 a 10
Př.:
//demonstruje vícenasobný příkaz return

#include <iostream>

int Dvojnasobek(int MnozstviKeZdvojnasobeni);

int main()
{
	using std::cout;

	int vysledek = 0;
	int vstup;

	cout << "Vlozte cislo mezi 0 a 10 000 ke zdvojnasobeni: ";
	std::cin >> vstup;

	cout << "\nPred volanim dvojnasobku... ";
	cout << "\nvstup: " << vstup << " vysledek: " << vysledek << "\n";

	vysledek = Dvojnasobek(vstup);

	cout << "\nPo volani dvojnasobku...\n";
	cout << "\nvstup: " << vstup << " vysledek: " << vysledek << "\n";

	return 0;
}

int Dvojnasobek(int puvodni)
{
	if (puvodni <= 10000)
		return puvodni * 2;
	else
		return -1;
	std::cout << "Sem se neni mozne dostat!\n";
}

Výchozí parametry

Máme-li deklarovánu funkci long mojeFUnkce(int);, pak jí při předání je nutné předat parametr typu int.
Tuto funkci můžeme deklarovat: long mojeFunkce(int = 20); nebo long mojeFunkce(int x = 20); což znamená, že pokud funkci není předán parametr, pak si bere jako výchozí hodnotu 20.
// používání výchozích hodnot parametrů

#include <iostream>

int ObjemKrychle(int delka, int sirka = 25, int vyska = 1);

int main()
{
	int delka = 100;
	int sirka = 50;
	int vyska = 2;
	int objem;

	objem = ObjemKrychle(delka, sirka, vyska);
	std::cout << "Prvni objem je: " << objem << "\n";

	objem = ObjemKrychle(delka, sirka);
	std::cout << "Druhy objem je: " << objem << "\n";

	objem = ObjemKrychle(delka);
	std::cout << "Treti objem je: " << objem << "\n";
	return 0;
}

int ObjemKrychle(int delka, int sirka, int vyska)
{

	return (delka * sirka * vyska);
}

parametr delka musí být vždy zadán... Výsledkem programu je:
Prvni objem je: 10000
Druhy objem je: 5000
Treti objem je: 2500

Přetížení funkcí

Neboli také polymorfsmus - lze vytvořit více funkcí s jedním názvem.
int mojeFunkce(int,int);
int mojeFunkce(long);
Dvě funkce se stejným názvem a seznamem parametrů, ale různým návratovým typem způsobí chybu při kompilaci! Pokud je různý návratový typ, musí se lišit taky buď název nebo seznam parametrů, aby byl kompilátor schopen rozeznat, kterou funkci má použít.
// demonstruje funkční polymorfismus

#include <iostream>

int Dvojnasobek(int);
long Dvojnasobek(long);
float Dvojnasobek(float);
double Dvojnasobek(double);

using namespace std;

int main()
{
	int      mujInt = 6500;
	long     mujLong = 65000;
	float    mujFloat = 6.5F;
	double   mujDouble = 6.5e20;

	int      dvakratInt;
	long     dvakratLong;
	float    dvakratFloat;
	double   dvakratDouble;

	cout << "mujInt: " << mujInt << "\n";
	cout << "mujLong: " << mujLong << "\n";
	cout << "mujFloat: " << mujFloat << "\n";
	cout << "mujDouble: " << mujDouble << "\n";

	dvakratInt = Dvojnasobek(mujInt);
	dvakratLong = Dvojnasobek(mujLong);
	dvakratFloat = Dvojnasobek(mujFloat);
	dvakratDouble = Dvojnasobek(mujDouble);

	cout << "dvakratInt: " << dvakratInt << "\n";
	cout << "dvakratLong: " << dvakratLong << "\n";
	cout << "dvakratFloat: " << dvakratFloat << "\n";
	cout << "dvakratDouble: " << dvakratDouble << "\n";

	return 0;
}

int Dvojnasobek(int puvodni)
{
	cout << "Ve funkci Dvojnasobek(int)\n";
	return 2 * puvodni;
}

long Dvojnasobek(long puvodni)
{
	cout << "Ve funkci Dvojnasobek(long)\n";
	return 2 * puvodni;
}

float Dvojnasobek(float puvodni)
{
	cout << "Ve funkci Dvojnasobek(float)\n";
	return 2 * puvodni;
}

double Dvojnasobek(double puvodni)
{
	cout << "Ve funkci Dvojnasobek(double)\n";
	return 2 * puvodni;
}

Funkce řádkového typu

Služí k urychlení programu na úkor velikosti kódu. Používají se pro funkce s tělem o jednom až dvou řádcích a pokud se nebude funkce často volat.
Vytváří se tak, že se před delkaraci zadá klíčové slůvko inline. Kompilátor pak při na každé místo, kde se volá tato funkce, vloží definici funkce, místo odskoku do funkce a zpět. Tím se běh programu urychlí, uvčem na úkor velikosti zdrojového kódu. Proto se používá pro funkce s tělem o jednom až dvou řádcích a pokud se nebude funkce často volat.

Příklad:
// demonstruje inline funkce

#include <iostream>

inline int Dvojnasobek(int);

int main()
{
	int cil;
	using std::cout;
	using std::cin;
	using std::endl;

	cout << "Vlozte cislo, se kterym se bude pracovat: ";
	cin >> cil;
	cout << "\n";

	cil = Dvojnasobek(cil);
	cout << "Cil: " << cil << endl;

	cil = Dvojnasobek(cil);
	cout << "Cil: " << cil << endl;


	cil = Dvojnasobek(cil);
	cout << "Cil: " << cil << endl;
	return 0;
}

int Dvojnasobek(int cil)
{
	return 2*cil;
}

REKURZE

Neboli schopnost funkce volat sebe sama - přímá rekurze, nebo volat funkci a ta zase původní - nepřímá rekurze.
V C++ se často nepoužívá, ale může být efektivní nástroj pro řešení některých problémů.
PŘI REKURZI DOCHÁZÍ K ZANOŘOVÁNÍ FUNKCÍ, PROTO MUSÍ EXISTOVAT PODMÍNKA PRO UKONČENÍ. PŘI ROZSÁHLÉ REKURZI MŮŽE BÝT VYČERPÁNA PAMĚT!
Příklad:
// výpočet mocniny čísla rekurzí

#include <iostream>
using namespace std;
typedef unsigned short int USHORT;
typedef unsigned long int ULONG;
ULONG Umocni(USHORT n, USHORT mocnina);

int main()
{
	USHORT cislo,mocnina;
	ULONG vysl;
	cout << "Zadejte cislo: ";	cin >> cislo;
	cout << "Na kolikatou?: ";	cin >> mocnina;
	vysl = Umocni(cislo,mocnina);
	cout << cislo << "na" << mocnina <<"-tou je: " << vysl << "/n";
	return 0;
}

ULONG Umocni(USHORT n,USHORT mocnina)
{
	if (mocnina==1) return n;
	else 
		return (n * Umocni(n,mocnina-1));
}

6 - OOP - Objektově orientované programování

Tvorba nových typů

Typ proměnných nám říká   Nový typ je vytvořen deklarací třídy - sbírka proměnných v kombinaci se sadou souvisejících funkcí.
Třída se může skládat z libovolné kombinace typů, proměnných a jiných typů tříd.
Členské proměnné - datové členy - jsou proměnné třídy.

Deklarace třídy

class Jmeno_třídy { seznam datových členů. };
Př.:
class Kocka
{
     unsigned int jejiVek;
     unsigned int jejiVaha;
     void Mnau();
};
Deklarací se nevzhrazuje v paměti místo. Deklarace říká, co to vlastně je kočka - jaké datové typy používá a co umí (funkce).
Pamět se vyhrazuje pro každý definovaný objekt.

Definice objektu

Definuje se stejným způsobem jako ostatní proměnné.
int vaha;
Kocka Micka;

Definovali jsme proměnnou vaha typu int a proměnnou Micka typu Kocka.

Přístup ke členům třídy

Máme-li definovaný konkrétní objekt nějakého typu - tady je to Micka typu Kocka, pak je možný přístup ke členů tohoto objektu pomocí operátoru tečka . . Micka.jejiVaha = 50;
Micka.Mnau(); //zavol nˇ funkce Mnau()


Přiřazení hodnot objektům!
PŘIŘAZUJE SE PROMĚNNÝM! NIKOLIV TYPŮM!!! Proto není možné přiřadit: Kocka.jejiVek=5;
Ale je nutné přiřadit do PROMĚNNÉ:

Kocka Micka;
Micka.jejiVek=5;

Soukromé a veřejné členy třídy

Všem členům jsou přiřazeny atributy - public, private. Standardně jsou všechny členy považovány za soukromé private Tyto dva atributy značí přístupnost členu z venku třídy.
Příklad.:
// Demonstruje deklaraci trídy a definici objektu této trídy

#include <iostream>   

class Kocka             // deklarace trídy Kocka
{
public:                 // následující clenové jsou verejní (public)
	int jejiVek;		// clenská promenná
	int jejiVaha;		// clenská promenná
};						// všimnete si stredníku


int main()				
{
	Kocka Micka;
	Micka.jejiVek = 5;    // prírazení hodnoty do clenské promenné
	std::cout << "Micka je kocka, ktere je " ;
	std::cout << Micka.jejiVek << " let.\n";
	return 0;
}

Pokud by byly datové členy tridy Kocka privátní, tj. bylo by užito private nebo by nebylo uvedeno nic, pak by nebylo možné napsat pro objekt Micka:
Micka.jejiVek=5;
Protoze NENÍ MOŽNÝ PŘÍSTUP KE ČLENŮM TŘÍDY JINAK,NEŽ POMOCÍ PŘÍSTUPOVÝCH FUNKCÍ, KTERÉ JSOU DEFINOVÁNY V TŘÍDĚ JAKO VEŘEJNÉ!!!
Taková třída s přístupovými funkcemi je v příkladu:
// Deklarace trídy Kocka
// Clenské promenné jsou privátní, verejné prístupové metody
// zprostredkovávají nastavení a získaní hodnot privatních dat

class Kocka
{
public:
	// verejné prístupové metody
	unsigned int ZiskejVek();
	void NastavVek(unsigned int Vek);

	unsigned int ZiskejVahu();
	void NastavVahu(unsigned int Vaha);

	// verejné clenské funkce
	void Mnau();

	// privátní clenské promenné
private:
	unsigned int  jejiVek;
	unsigned int  jejiVaha;

};

V objektu této třídy je možné přiřadit a zpětně získat hodnotu privátní proměnné Micka.jejiVek pomocí přístupových funkcí Micka.NastavVek() a Micka.ZiskejVek(), které jsou PUBLIC.

ČLENSKÁ DATA BY MĚLA BÝT SOUKROMÁ
Pomocí přístupových funkcí se odděluje implementační detaily uložení dat od jejich používání. Pak například změna způsobu uložení dat se dá snadno provést, bez vlivu no ostatní funkce...

Implementace metod třídy

Všechny deklarované metody umusí mít svou implementaci neboli definici.
Syntaxe definice je: typ třída::metoda(parametry);{ tělo }
Např.:
// Demonstruje deklaraci trídy
// definici metod této trídy

#include <iostream>		// kvuli cout

class Kocka                    // zacátek deklarace trídy
{
public:						   // zacátek sekce public
	int ZiskejVek();           // funkce pro prístup k datum
	void NastavVek (int vek);  // funkce pro prístup k datum
	void Mnau();               // obecná funkce
private:					   // zacátek sekce private
	int jejiVek;               // clenská promenná
};

// ZiskejVek, verejná funkce pro prístup k datum
// vrací hodnotu clenské promenné jejiVek
int Kocka::ZiskejVek()
{
	return jejiVek;
}

// definice NastavVek, verejné
// funkce pro prístup k datum
// nastavuje clenskou promennou jejiVek
void Kocka::NastavVek(int vek)
{
	// nastavení clenské promenné jejiVek na
	// hodnotu predanou parametrem vek
	jejiVek = vek;
}

// definice metody Mnau
// vrací: void
// parametry: Žádne
// akce: Vytiskne na obrazovku "Mnau"
void Kocka::Mnau()
{
	std::cout << "Mnau.\n";
}

// založí objekt kocky, nastaví její vek, nechá ji zamnoukat,
// vytiskne její vek, pak zamnouká znovu.
int main()
{
	Kocka Micka;
	Micka.NastavVek(5);
	Micka.Mnau();
	std::cout << "Micka je kocka, ktere je " ;
	std::cout << Micka.ZiskejVek() << " let.\n";
	Micka.Mnau();
	return 0;
}

Mnau.
Micka je kocka, ktere je 5 let.
Mnau.

Konstruktory a destruktory

Konstruktory Konstruktor je metoda třídy se stejným názvem, jaký má třída.

Destruktor je metoda třídy se stejným názvem jako má třída, jemuž předchází znak vlnovka ~.
Úkolem destruktoru je uklidit po objetu a uvolnit veškerou alokovanou pamět.
Destruktory NEPŘIJÍMAJÍ ŽÁDNÉ ARGUMENTY A NEVRACÍ HODNOTU!

Například pro třídu Kocka, můžeme například vytvořit konstruktor Kocka() a destruktor bude ~Kocka()

Výchozí konstruktory a destruktory

Pokud konstruktor nebo destruktor není definován, vytvoří jej kompilátor. Kompilátor ovšem vytvoří výchozí konstruktor, který ani nic nedělá - prázdné tělo. Proto pro inicializaci hodnot je nutné si konstruktor definovat.
Výchozí konstruktory jsou ty, které nepřijímají žádný argument.


Stejně jako je možné definovat proměnnou a až potom jí přiřadit hodnotu: int Vaha;... vaha=7; nebo ji lze definovat a rovnou inicializovat int Vaha=7;, aby měla stále smysluplnou hodnotu, lze totéž provádět s objekty (s jejich datovými členy).

Konstruktory jsou formální záležitostí - proto se musí vytvořit i když nic nedělají. Každý objekt třídy se musí konstruovat a destruovat. Při definování objektu třídy se volá konstruktor.

Př.: Definujeme objekt bez parametrů:Kocka Micka;
Pak má konstruktor tvar: Kocka();

Pokud by konstruktor třídy přijímal dva parametry, pak by se objekt definoval třeba: Kocka Micka(5,7);

Příklad:
// Demonstruje deklaraci konstruktoru a
// destruktoru pro trídu Kocka
// Programatorem vytvorený výchozí konstruktor

#include <iostream>		// kvuli cout

class Kocka                    // zacátek deklarace trídy
{
public:						   // zacátek sekce public
	Kocka(int iniVek);	       // konstruktor
	~Kocka();				   // destruktor
	int ZiskejVek();           // funkce pro prístup k datum
	void NastavVek (int vek);  // funkce pro prístup k datum
	void Mnau();               // obecná funkce
private:					   // zacátek sekce private
	int jejiVek;               // clenská promenná
};

// konstruktor tridy Kocka
Kocka::Kocka(int iniVek)
{
   jejiVek = iniVek;
}

Kocka::~Kocka()                // destruktor, žádná akce
{
}

// ZiskejVek, verejná funkce pro prístup k datum
// vrací hodnotu clenské promenné jejiVek
int Kocka::ZiskejVek()
{
	return jejiVek;
}

// definice NastavVek, verejné
// funkce pro prístup k datum
// nastavuje clenskou promennou jejiVek
void Kocka::NastavVek(int vek)
{
	// nastavení clenské promenné jejiVek na
	// hodnotu predanou parametrem vek
	jejiVek = vek;
}

// definice metody Mnau
// vrací: void
// parametry: Žádné
// akce: Vytiskne na obrazovku "Mnau"
void Kocka::Mnau()
{
	std::cout << "Mnau.\n";
}

// založí objekt kocky, nastaví její vek, nechá ji zamnoukat,
// vytiskne její vek, pak zamnouká znovu.
int main()
{
	Kocka Micka(5);
	Micka.Mnau();
	std::cout << "Micka je kocka, ktere je " ;
	std::cout << Micka.ZiskejVek() << " let.\n";
	Micka.Mnau();
	Micka.NastavVek(7);
	std::cout << "Micce je nyni " ;
	std::cout << Micka.ZiskejVek() << " let.\n";
	return 0;
}

Mnau.
Micka je kocka, ktere je 5 let.
Mnau.
Micce je nyni 7 let.
Pomocí konstruktoru je po vytvoření objektu nastaven věk na 5 let, proto není nutné volat funkci NastavVek().
Destruktor nic nedělá, ale protože je uveden v deklaraci objektu, je nutné ho definovat (s prázdným tělem).

Konstantní členské funkce

Deklarují se pomocí klíčového slova const za závorkami.
void MojeFunkce() const;
Takto definované funkce NEZMĚNÍ HODNOTU ŽÁDNÉHO ČLENU Z TŘÍDY
Často se používá pro dekraci přístupových funkcí. Například u třídy Kocka je vhodné deklarovat funkce následně:
void NastavVek(int Vek);
int ZiskejVek() const;
Funkce ZiskejVek() má vrátit věk kočky, to jest zjistit číslo, ale nemá ho nijak měnit, proto je deklarována jako konstantní. Kdybychom naopak deklarovali jako konstantní funkci NastavVek(), tak by nám byla k ničemu, protože jakýkoliv pokus o přiřazení do členské proměnné by kompilátor hlásil jako chybu.

constby se mělo používat všude, kde je to možné.

Ukázka chyb v programu:
00 // Demonstruje chyby kompilátoru
01 // Tento program nelze zkompilovat!
02
03 #include <iostream>          // kvuli cout
04 
05 class Kocka
06 {
07 public:
08   	Kocka(int iniVek);
09	~Kocka();
10	int ZiskejVek() const;       // konstantní funkce pro prístup k datum
11	void NastavVek (int vek);
12	void Mnau();
13 private:
14	int jejiVek;
15 };
16
17 // konstruktor objektu Kocka
18 Kocka::Kocka(int iniVek)
19 {
20	jejiVek = iniVek;
21	std::cout << "Konstruktor Kocka\n";
22 }
23
24 Kocka::~Kocka()                  // destruktor, žádná akce
25{
26	std::cout << "Destruktor Kocka\n";
27 }
28 // ZiskejVek je konstantní funkce,
29 // ale tato vlastnost je porušená!
30 int Kocka::ZiskejVek() const
31 {
32	return (jejiVek++);         // porušuje klauzuli const!
33 }
34
35 // definice NastavVek, verejné
36 // funkce pro prístup k datum
37
38 void Kocka::NastavVek(int vek)
39 {
40	// nastavuje clenskou promennou vek na
41	// hodnotu predanou parametrem vek
42	jejiVek = vek;
43 }
44
45 // definice metody Mnau
46 // vrací: hodnotu void
47 // parametry: Žadné
48 // akce: Vytiskne na obrazovku "Mnau"
49 void Kocka::Mnau()
50 {
51	std::cout << "Mnau.\n";
52 }
53
54 // demonstruje nekolik porušeni rozhraní objektu
55 // a výsledné chyby kompilátoru
56 int main()
57 {
58	Kocka Micka;               // neodpovídá deklaraci
59	Micka.Mnau();
60	Micka.Haf();               // Kocky prece neštekají!
61	Micka.jejiVek = 7;         // jejiVek je privatní promenná
62	return 0;
63 }
Analýza:
řádek 10: deklarace funkce ZiskejVek() jako konstantní, ale
řádek 32: zvýšení hodnoty členské proměnné jejiVek uvntiř této funkce
řádek 12: Mnau() by měla být deklarována jako konstantní
řádek 58: Definice objektu Micka nesouhlasí s konstuktorem - je nutný parametr.
řádek 60: Metoda Haf() není deklarována.
řádek 61: Nelze přiřadit do soukromé členské proměnné jejiVek!

Umístění deklarace třídy a metod

Každá funkce deklarovaná ve třídě musí mít svoji definici, neboli implementaci.
Je dobrým zvykem tyto dvě části oddělovat a ukládat do jiných souborů. Deklaraci uložíme do hlavičkového souboru typu .hpp a definici do souboru typu .cpp kam vložíme připojení deklarace třídy pomoci include.

Implementace řádkových metod

inline int Kocka::ZiskatVahu()
{
     return jejiVaha;
}
je definována řádková funkce

Nebo lze definici umístit přímo do deklarace:
class Kocka
{
public:
     int ZiskatVahu()
     {
           return jejiVaha;
     }
     void NastavVahu(int vaha);
};
Příklad rozdělení programu:
// Ukázka hlavičkového souboru Kocka.hpp
#include <iostream>
class Kocka
{
public:
	Kocka (int iniVek);
	~Kocka();
	int ZiskejVek() const { return jejiVek;}		// inline!
	void NastavVek (int vek) { jejiVek = vek;}		// inline!
	void Mnau() const  { std::cout << "Mnau.\n";}	// inline!
private:
	int jejiVek;
};
//  Ukázka  implementace třídy Kocka ze souboru Kocka.hpp, souboru Kocka.cpp
// Demonstruje inline funkce
// a vložení hlavickového souboru

// vloženi hlavickového souboru!
#include "kocka.hpp"


Kocka::Kocka(int iniVek)   //konstruktor
{
	jejiVek = iniVek;
}

Kocka::~Kocka()             //destruktor, žádná akce
{
}

// založí objekt kocky, nastaví její vek, nechá ji zamnoukat,
// vytiskne její vek, pak zamnouká znovu.
int main()
{
	Kocka Micka(5);
	Micka.Mnau();
	std::cout << "Micka je kocka, ktere je " ;
	std::cout << Micka.ZiskejVek() << " let.\n";
	Micka.Mnau();
	Micka.NastavVek(7);
	std::cout << "Micce je nyni " ;
	std::cout << Micka.ZiskejVek() << " let.\n";
	return 0;
}

Třídy s jinými třídami jako členskými daty

Často se složitější třídy sestávají z jednoduššícht tříd. Např. třída auto se dá složit z třídy kolo, motor, převodovka...
Nebo např. obdelník se skládá z úseček. Úsečku definují dva body. Bod definuje x-ová a y-ová souřadnice.
V příkladu je uvedena deklarace třídy obdélník. Nejdříve se deklaruje třída bod, protože je nejmenší a základní objekt obdélníka.
// Zacátek Obdélník.hpp

#include <iostream>
class Bod     // udržuje souradnice x,y
{
// žádný konstruktor, používá se výchozí
public:
	void NastavX(int x) { hodnotaX = x; }
	void NastavY(int y) { hodnotaY = y; }
	int ZiskejX()const { return hodnotaX;}
	int ZiskejY()const { return hodnotaY;}
private:
	int hodnotaX;
	int hodnotaY;
};    // konec deklarace trídy Bod


class Obdelnik
{
public:
	Obdelnik (int horni, int levy, int spodni, int pravy);
	~Obdelnik () {}

	int ZiskejHorni() const { return hodnotaHorni; }
	int ZiskejLevy() const { return hodnotaLevy; }
	int ZiskejSpodni() const { return hodnotaSpodni; }
	int ZiskejPravy() const { return hodnotaPravy; }

	Bod  ZiskejHorniLevy() const { return hodnotaHorniLevy; }
	Bod  ZiskejSpodniLevy() const { return hodnotaSpodniLevy; }
	Bod  ZiskejHorniPravy() const { return hodnotaHorniPravy; }
	Bod  ZiskejSpodniPravy() const { return hodnotaSpodniPravy; }

	void NastavHorniLevy(Bod Umisteni)  {hodnotaHorniLevy = Umisteni;}
	void NastavSpodniLevy(Bod Umisteni)  {hodnotaSpodniLevy = Umisteni;}
	void NastavHorniPravy(Bod Umisteni)  {hodnotaHorniPravy = Umisteni;}
	void NastavSpodniPravy(Bod Umisteni)  {hodnotaSpodniPravy = Umisteni;}

	void NastavHorni(int horni) { hodnotaHorni = horni; }
	void NastavLevy (int levy) { hodnotaLevy = levy; }
	void NastavSpodni (int spodni) { hodnotaSpodni = spodni; }
	void NastavPravy (int pravy) { hodnotaPravy = pravy; }

	int ZiskejPlochu() const;

private:
	Bod  hodnotaHorniLevy;
	Bod  hodnotaHorniPravy;
	Bod  hodnotaSpodniLevy;
	Bod  hodnotaSpodniPravy;
	int    hodnotaHorni;
	int    hodnotaLevy;
	int    hodnotaSpodni;
	int    hodnotaPravy;
};
// konec Obdelnik.hpp

Zde je možná implementace třídy
// Zacátek Obdelnik.cpp

#include "obdelnik.hpp"
Obdelnik::Obdelnik(int horni, int levy, int spodni, int pravy)
{
	hodnotaHorni = horni;
	hodnotaLevy = levy;
	hodnotaSpodni = spodni;
	hodnotaPravy = pravy;

	hodnotaHorniLevy.NastavX(levy);
	hodnotaHorniLevy.NastavY(horni);

	hodnotaHorniPravy.NastavX(pravy);
	hodnotaHorniPravy.NastavY(horni);

	hodnotaSpodniLevy.NastavX(levy);
	hodnotaSpodniLevy.NastavY(spodni);

	hodnotaSpodniPravy.NastavX(pravy);
	hodnotaSpodniPravy.NastavY(spodni);
}


// spocítá plochu obdélníku tak, ze získa šírku a výšku jako
// rozdíly v souradnicích rohových bodu a pak je vynásobí
int Obdelnik::ZiskejPlochu() const
{
	int Sirka = hodnotaPravy-hodnotaLevy;
	int Vyska = hodnotaHorni - hodnotaSpodni;
	return (Sirka * Vyska);
}

int main()
{
	//inicializace lokální promenné Obdelnik
	Obdelnik MujObdelnik (100, 20, 50, 80 );

	int Plocha = MujObdelnik.ZiskejPlochu();

	std::cout << "Plocha: " << Plocha << "\n";
	std::cout << "X-ova souradnice horniho leveho bodu: ";
	std::cout << MujObdelnik.ZiskejHorniLevy().ZiskejX();
	return 0;
}

Struktury

struct - toto klíčové slovo je blízké class. Používá se k deklaraci struktury. Strukrura je v podstatě stejná jako třída, ale všechny členy jsou standardně veřejné! Toto klíčové slovo je z jazyka C.

7 - Více o toku programu

Smyčky

Opakované provádění akce nad stejnými daty.Jsou základem iterace.

goto - slouží ke skoku na návěští - jméno následované dvojtečkou

while

Opakovaně se provádí sekvence příkazů, dokud JE počáteční podmínka pravSPANá
Syntaxe:
while (podmínka)
příkaz;
Příklad:
// Smyčka s while

#include <iostream>

int main()
{
	int citac = 0;               // inicializace podmínky

	while(citac < 5)     // test, zda je podmínka stále pravSPANá
	{
		citac++;              // tělo smyčky
		std::cout << "citac: " << citac << "\n";
	}

	std::cout << "Hotovo, citac: " << citac << ".\n";
	return 0;
}

Podmínku lze vytvořit pomocí logických operátorů.
Příklad:
// Složitější výraz v podmínce smyčky while

#include <iostream>
using namespace std;

int main()
{
	unsigned short male;
	unsigned long  velke;
	const unsigned short MAXMALE=65535;

	cout << "Vlozte male cislo: ";
	cin >> male;
	cout << "Vlozte velke cislo: ";
	cin >> velke;

	cout << "male: " << male << "...";

	// při každé iteraci testujeme tři podmínky
	while (male < velke && velke > 0 && male < MAXMALE)
	{
		if (male % 5000 == 0)  // zapsání tečky za každých 5000 řádek
			cout << ".";

		male++;

		velke-=2;
	}

	cout << "\nMale: " << male << " Velke: " << velke << endl;
	return 0;
}

continue a break

continue - slouží ke skoku na začátek smyčky
break - slouží k ukončení smyčky (k jejímu opuštění)
while (podmínka)
{
     if (podmínka2)
           break;
     //příkazy;
}
Příklad:
// Demonstruje příkazy break a continue

#include <iostream>

int main()
{
	using namespace std;
	unsigned short male;
	unsigned long  velke;
	unsigned long  preskoc;
	unsigned long  cil;
	const unsigned short MAXMALE=65535;

	cout << "Vlozte male cislo: ";
	cin >> male;
	cout << "Vlozte velke cislo: ";
	cin >> velke;
	cout << "Vlozte cislo pro preskoceni: ";
	cin >> preskoc;
	cout << "Vlozte cilove cislo: ";
	cin >> cil;

	cout << "\n";

	// nastavení tří podmínek pro ukončení smyčky
	while (male < velke && velke > 0 && male < MAXMALE)

	{

		male++;

		if (male % preskoc == 0)  // přeskočit odečítání?
		{
			cout << "preskoceni na cisle " << male << endl;
			continue;
		}

		if (velke == cil)    // dosažení přesné cílové hodnoty?
		{
			cout << "Cíl dosažen!";
			break;
		}

		velke-=2;
	}                   // konec smyčky while

	cout << "\nMale: " << male << " Velke: " << velke << endl;
	return 0;
}

Tyto dva příkazy by se měli používat s rozvahou, protože způsobují nepřehlednost kódu.
Nekonečná smyčka - while (true) - pokud nebude obsahovat podmínku pro ukončení, dojde k zatuhnutí programu!!!

do...while

do
příkaz;
while (podmínka);
Funguje podobně jako smyčka while, ale podmínka se testuje na konci cyklu. Posloupnost příkazů tedy proběhne alespon jednou.
Příklad:
// Demonstruje smyčku do while

#include <iostream>

int main()
{
	using namespace std;
	int citac;
	cout << "Kolik pozdravu? ";
	cin >> citac;
	do
	{
		cout << "Ahoj\n";
		citac--;
	}  while (citac > 0);
	cout << "Hodnota citace: " << citac << endl;
	return 0;
}

for

for (inicializace;test;akce)
příkaz;
Inicializace slouží k inicializaci stavu čítače, podmínka se prověřuje při každém průchodu smyčkou, akce - obvykle zvýšení stavu čítače
Provádí se dokud je podmínka pravSPANá.
Příklad:
// Smyčka for

#include <iostream>

int main()
{

	int citac;
	for (citac = 0; citac < 5; citac++)
		std::cout << "Smycka! ";

	std::cout << "\nCitac: " << citac << ".\n";
	return 0;
}
Vícenásobná inicializace a zvýšení hodnoty
Pokud chceme inicializovat více proměnných najednou, nebo provést více akcí, oddělíme jednotlivé příkazy čárkami.
Příklad: Inicialiace dvou proměnných a zvýšení jejich hodnoty při každém průchodu.
// Demonstruje vícenásobný výraz
// ve smyčce for

#include <iostream>

int main()
{

	for (int i=0, j=0; i<3; i++, j++)
		std::cout << "i: " << i << " j: " << j << std::endl;
	return 0;

}
Prázdné příkazy ve smyčce for
Pokud chceme některý příkaz vynechat, vložíme jen středník.
Lze vynechat všechny příkazy, pak vznikne nekonečná smyčka.
Příklad:
// Smyčka for s prázdnými výrazy

#include <iostream>

int main()
{

	int citac = 0;

	for( ; citac < 5; )
	{
		citac++;
		std::cout << "Smycka!  ";
	}

	std::cout << "\nCitac: " << citac << ".\n";
	return 0;

}
Příklad: Prázdný příkaz for
// Ilustrace prázdného výrazu for

#include <iostream>

int main()
{

	int citac=0;       // inicializace
	int max;
	std::cout << "Kolik pozdravu?";
	std::cin >> max;
	for (;;)          // nekonečná smyčka for
	{
		if (citac < max)       // test
		{
			std::cout << "Ahoj!\n";
			citac++;          // inkrementace
		}
		else
			break;
	}
	return 0;
}
Pokud za hlavičku smyčky vložíme středník, nemá žádné tělo. Bere se jako prázdný příkaz. Budou se tedy vykonávat jenom příkazy v hlavičce.
Příklad:
// Demonstruje prázdný výraz
// v těle smyčky for

#include <iostream>
int main()
{

	for (int i = 0; i<5; std::cout << "i: " << i++ << std::endl)
		;
	return 0;

}
Smyčky lze do sebe vnářet!
Příklad:
// Ilustruje vnořené smyčky for

#include <iostream>

int main()
{
	using namespace std;
	int radky, sloupce;
	char znak;
	cout << "Kolik radku? ";
	cin >> radky;
	cout << "Kolik sloupcu? ";
	cin >> sloupce;
	cout << "Jaky znak? ";
	cin >> znak;
	for (int i = 0; i < radky; i++)
	{
		for (int j = 0; j<sloupce; j++)
			cout << znak;
		cout << "\n";
	}
	return 0;
}


Příklad: Fibonacciho posloupnost pomocí iterace:(každý člen je součtem dvou předchozích)
// Demonstruje spočítání n-tého
// Fibonacciho čísla s pomocí iterace

#include <iostream>

int fib(int pozice);
int main()
{
	using namespace std;
	int odpoved, pozice;
	cout << "Jaka pozice? ";
	cin >> pozice;
	cout << "\n";

	odpoved = fib(pozice);
	cout << pozice << ". Fibonacciho cislo je ";
	cout << odpoved << ".\n";
	return 0;
}

int fib(int n)
{
	int minusDva=1, minusJeden=1, odpoved=2;

	if (n < 3)
		return 1;

	for (n -= 3; n; n--)
	{
		minusDva = minusJeden;
		minusJeden = odpoved;
		odpoved = minusJeden + minusDva;
	}

	return odpoved;
}

Příkaz switch

Umožňuje větvení podle více hodnot.
Syntaxe:
switch (věraz)
{
     case hodnota1:    příkaz;
     case hodnota2:    příkaz;
     ...
     case hodnotaN:    příkaz;
     default:          příkaz;
}
Jestliže se se shoduje hodnota s hodnotou výrazu je proveden daný příkaz. Za příkazem je nutné uvést příkaz break! Pokud se tento příkaz neuvede, pokračuje provádění příkazů i pro jiné hodnoty! Pokud je záměrně break vypuštěn, mělo by se to objevit v komentáři, že se nejedná o omyl.
switch (věraz)
{
     case hodnota1:    příkaz;
                       break;
     case hodnota2:    příkaz;
                       break;
     ...
}
Pokud nesedí ani jedna hodnota, je proveden příkaz za default - pokud je uveden.
Příklad:
// Ukázka příkazu switch

#include <iostream>

int main()
{
	using namespace std;
	unsigned short int cislo;
	cout << "Vlozte cislo mezi 1 a 5: ";
	cin >> cislo;
	switch (cislo)
	{
	case 0:		cout << "Prilis male!";
				break;
	case 5:		cout << "Dobra prace!\n";  // spada do rozmezi
	case 4:		cout << "Hezka volba!\n";  // spada do rozmezi
	case 3:		cout << "Vynikajici!\n";   // spada do rozmezi
	case 2:		cout << "Skvele!\n";       // spada do rozmezi
	case 1:		cout << "Neuveritelne!\n";
				break;
	default:	cout << "Prilis velke!\n";
				break;
	}
	cout << "\n\n";
	return 0;
}

Vytvoření menu pomocí switch a for (;;)

Nekonečná smyčka zajistí vystavení nabídky dokud nebude vybrána položka pro ukončení smyčky (větvění pomocí switch).
Někdy je použit kód:
#define EVER ;;
for (EVER)
{
     příkazy;
}
FOREVER - navždy

Příklad: Menu
// Použití nekonečné smyčky for
// pro řízení interakce s uživatelem
#include <iostream>

// prototypy
int nabidka();
void ProvedUkolJedna();
void ProvedUkoly(int);

using namespace std;

int main()
{
	bool konec = false;
	for (;;)
	{
		int volba = nabidka();
		switch(volba)
		{
		case (1):
			ProvedUkolJedna();
			break;
		case (2):
			ProvedUkoly(2);
			break;
		case (3):
			ProvedUkoly(3);
			break;
		case (4):
			continue;  // nadbytečné!
			break;
		case (5):
			konec=true;
			break;
		default:
			cout << "Prosim vyberte znovu!\n";
			break;
		}          // konec příkazu switch

		if (konec)
			break;
	}                // ukončení smyčky for
	return 0;
}                    // konec funkce main()

int nabidka()
{
	int volba;

	cout << " **** Nabidka ****\n\n";
	cout << "(1) Volba jedna.\n";
	cout << "(2) Volba dva.\n";
	cout << "(3) Volba tri.\n";
	cout << "(4) Znovuzobrazeni nabidky.\n";
	cout << "(5) Konec.\n\n";
	cout << ": ";
	cin >> volba;
	return volba;
}

void ProvedUkolJedna()
{
	cout << "Ukol jedna!\n";
}

void ProvedUkoly(int ktery)
{
	if (ktery == 2)
		cout << "Ukol dva!\n";
	else
		cout << "Ukol tri!\n";
}

PROGRAM PO PRVNÍM TÝDNU:

#include <iostream>
using namespace std;
enum VOLBA { KresliObd = 1, SpoctiObsah, SpoctiObvod,
   ZmenRozmery, Konec };

// Deklarace třídy Obdélník
class Obdelnik
{
   public:
      // konstruktory
      Obdelnik(int sirka, int vyska);
      ~Obdelnik();

      // přístupové funkce
      int ZiskejVysku() const { return vyskaObd; }
      int ZiskejSirku() const { return sirkaObd; }
      int SpoctiObsah() const { return vyskaObd * sirkaObd; }
      int SpoctiObvod() const { return 2*vyskaObd + 2*sirkaObd; }
      void NastavVelikost(int novaSirka, int novaVyska);

      // Různé metody


   private:
      int sirkaObd;
      int vyskaObd;
};

// Implementace metod třídy
void Obdelnik::NastavVelikost(int novaSirka, int novaVyska)
{
   sirkaObd = novaSirka;
   vyskaObd = novaVyska;
}


Obdelnik::Obdelnik(int sirka, int vyska)
{
   sirkaObd = sirka;
   vyskaObd = vyska;
}

Obdelnik::~Obdelnik() {}

int Nabidka();
void KresbaObd(Obdelnik);
void VypocetObsahu(Obdelnik);
void VypocetObvodu(Obdelnik);

int main ()
{
   // inicializace obdélníka na 30, 5
   Obdelnik obd(30,5);

   int volba = KresliObd;
   int fKonec = false;

   while (!fKonec)
   {
      volba = Nabidka();
      if (volba < KresliObd || volba > Konec)
      {
         cout << "\nNeplatna volba, zkuste prosim znovu.\n\n";
         continue;
      }
      switch (volba)
      {
      case KresliObd:
         KresbaObd(obd);
         break;
      case SpoctiObsah:
         VypocetObsahu(obd);
         break;
      case SpoctiObvod:
         VypocetObvodu(obd);
         break;
      case ZmenRozmery:
         int novaDelka, novaSirka;
         cout << "\nNova sirka: ";
         cin >> novaSirka;
         cout << "Nova vyska: ";
         cin >> novaDelka;
         obd.NastavVelikost(novaSirka, novaDelka);
         KresbaObd(obd);
         break;
      case Konec:
         fKonec = true;
         cout << "\nUkonceni programu...\n\n";
         break;
      default:
         cout << "Chybna volba!\n";
         fKonec = true;
         break;
      }   // konec switch
   }      // konec while
   return 0;
}         // konec main

int Nabidka()
{
   int volba;
   cout << "\n\n   *** Nabidka *** \n";
   cout << "(1) Kresba Obdelnika\n";
   cout << "(2) Obsah\n";
   cout << "(3) Obvod\n";
   cout << "(4) Zmena rozmeru\n";
   cout << "(5) Konec\n";

   cin >> volba;
   return volba;
}

void KresbaObd(Obdelnik obd)
{
   int vyska = obd.ZiskejVysku();
   int sirka = obd.ZiskejSirku();

   for (int i = 0; i<vyska; i++)
   {
      for (int j = 0; j<sirka; j++)
         cout << "*";
      cout << "\n";
   }
}


void VypocetObsahu(Obdelnik obd)
{
   cout << "Obsah: " << obd.SpoctiObsah() << endl;
}

void VypocetObvodu(Obdelnik obd)
{
   cout << "Obvod: " << obd.SpoctiObvod() << endl;
}

8 - Ukazatele

UKAZATEL

Je proměnná, která uchovává paměťovou adresu.
Ukazatele se nejčastěji používají pro:
Každá proměnná je umístěna v jedinečném místě v paměti, které se označuje její adresou.
Paměť - je rozdělena do sekvenčně číslovaných paměťových buňek.
Jedna paměťová buňka má velikost jeden bajt. Takže takový 4B long int bude zabírat 4 políčka. Ukazatel ukazuje na první políčko.

Operátor adresy &

- vrací adresu objektu v paměti.
Příklad:
// Demonstruje operátor adresy a adresy lokálních proměnných

#include <iostream>

int main()
{
   using namespace std;
   unsigned short promennaShort=5;
   unsigned long  promennaLong=65535;
   long zPromenna = -65535;

   cout << "promenna typu Short:\t" << promennaShort;
   cout << "\tAdresa promenne typu Short:\t";
   cout << &promennaShort << "\n";

   cout << "promenna typu Long:\t" << promennaLong;
   cout << "\tAdresa promenne typu Long:\t";
   cout << &promennaLong << "\n";

   cout << "promenna se znamenkem:\t" << zPromenna;
   cout << "\tAdresa promenne se znamenkem:\t";
   cout << &zPromenna << "\n";

   return 0;
}
Kde k výpisu adresy proměnné použijeme & a název proměnné, tedy například: &promennaShort

Uložení adresy do ukazatele

Nejdříve musíme definovat proměnnou jako ukazatel - například na typ int:
int *ukVek = 0;, který inicializujeme na hodnotu 0. Vzniká tak nulový ukazatel. Pokud není ukazatel inicializován, je nebezpečný, proto INICIALIZUJEME VŠECHNY UKAZATELE
Definujme: unsigned short int jakStary = 50;
unsigned short int *ukVek = 0;
Pak ukazateli ukVek přiřadíme adresu proměnnéjakStary následovně:
ukVek = &jakStary;
Proměnná ukVek nyní obsahuje adresu proměnné jakStarý.
INICIALIZACI LZE ZAŘÍDIT TAK:
unsigned short int *ukVek = &jakStary;
kde ukazatel bude inicializován na adresu proměnné‚ jakStary

Kompilátor pro ukazatel vyhrazuje stejn‚ místo - vždy 4 bajty!

Je dobré‚ pro název ukazatele použít označení uk, nebo pt(pointer).

Oper tor nepřímého přístupu *

Nebo také Operátor zrušení odkazu - znamená získání hodnoty uložené na adrese, která je uložena v ukazateli.
Mějme definováno:
unsigned short int tvujVek,jakStary = 50;
unsigned short int *ukVek = &jakStary;

tvujVek=*ukVek;
Proměnné‚ tvujVek byla přiřazena hodnota proměnné jakStary, pomocí ukazatele ukVek. Tj. nepřímým přístupem. Ukazatel ukVek ukazuje na adresu proměnné‚ jakStary a pomocí operátoru * umí získat hodnotu uloženou na této adrese.


* SE U UKAZATELŮ POUŽÍVÁ DVĚMA ODLIŠNÝMI ZPůSOBY.
JAKO SOUČÁST DEKLARACE - PŘEDCHÁZÍ JI TYP, JEHOŽ ADRESU UKAZATELE PONESE.
NAPŘ.:
unsigned short int * tvujVek = 0;
PŘI RUŠENÍ ODKAZU UKAZATELE INDIKUJE OPERÁTOR NEPŘÍMÉHO PŘÍSTUPU, ŽE JDE O PŘÍSTUP K HODNOTĚ NA PAMĚŤOVÉM MÍSTĚ ULOŽENÉHO V TOMTO UKAZATELI A NIKOLI O ADRESU SAMOTNOU:
*ukVek = 5

Manipulace s daty prostřednictvím ukazatelů

Jakmile je v ukazateli přiřazena adresa proměnné, je možné jej použít k získání přístupu k datům této proměnné.
Příklad: získání a změna hodnot přímým i nepřímým přístupem
// Používání ukazatelů

#include <iostream>

typedef unsigned short int USHORT;

int main()
{

   using std::cout;

   USHORT mujVek;     // proměnná
   USHORT *ukVek;     // ukazatel

   mujVek = 5;

   cout << "mujVek: " << mujVek << "\n";
   ukVek = &mujVek;   // přiřazení adresy proměnné mujVek do ukVek
   cout << "*ukVek: " << *ukVek << "\n\n";

   cout << "Nastaveni *ukVek na 7...\n";
   *ukVek = 7;        // nastavení mujVek na 7

   cout << "*ukVek: " << *ukVek << "\n";
   cout << "mujVek: " << mujVek << "\n\n";

   cout << "Nastaveni mujVek na 9...\n";
   mujVek = 9;

   cout << "mujVek: " << mujVek << "\n";
   cout << "*ukVek: " << *ukVek << "\n";

   return 0;
}
mujVek: 5
*ukVek: 5

Nastavení *ukVek na 7...
*ukVek: 7
mujVek: 7

Nastaven mujVek na 9...
mujVek: 9
*ukVek: 9
Příklad: Hodnoty ukazatelů
// Hodnoty ukazatelů

#include <iostream>


int main()
{
   using std::cout;

   unsigned short int mujVek = 5, tvujVek = 10;

   // ukazatel
   unsigned short int * ukVek = &mujVek;

   cout << "mujVek:\t\t" << mujVek
      << "\t\ttvujVek:\t" << tvujVek << "\n";

   cout << "&mujVek:\t" << &mujVek
      << "\t&tvujVek:\t" << &tvujVek << "\n";

   cout << "ukVek:\t\t" << ukVek << "\n";
   cout << "*ukVek:\t\t" << *ukVek << "\n";


   cout << "\nPrenastaveni ukVek = &tvujVek...\n\n";
   ukVek = &tvujVek;           // přenastavení ukazatele

   cout << "mujVek:\t\t" << mujVek
      << "\t\ttvujVek:\t" << tvujVek << "\n";

   cout << "&mujVek:\t" << &mujVek
      << "\t&tvujVek:\t" << &tvujVek << "\n";

   cout << "ukVek:\t\t" << ukVek << "\n";
   cout << "*ukVek:\t\t" << *ukVek << "\n";

   cout << "\n&ukVek:\t\t" << &ukVek << "\n";

   return 0;
}
Příklad možného výstupu (adresy se mohou lišit):
mujVek:    5           tvujVek:    10
&mukVek:   0012FF7C    &tvujVek:   0012FF78
ukVek:     0012FF7C
*ukVek:    5

Prenastaveni ukVek = &tvujVek...

mujvek:    5           tvujVek:    10
&mujVek:   0012FF7C    &tvujVek:   0012FF78
ukVek:     0012FF78
*ukVek:    10

&ukVek:    0012FF74
Používání ukazatelů
Ukazatel se deklaruje zápisem typu proměnné nebo objektu, jehož adresa se má do ukazatele uložit. Za typem nýsleduje operátor ukazatele (*) a název ukazatele.

Příklad:
unsigned short int * ukUkazatel = 0;
Pro přiřazení neboli inicializaci ukazeatele uveďte název proměnné, jejíž adresa se přiřazuje, s předřazeným operátorem adresy (&). Například:
unsigned short int Promenna = 5;
unsigned short int * ukUkazatel = &Promenna;
Při zrušení odkazu ukazatele se název ukazatele předřadí operátorem dereference (*). Např.:
unsigned short int hodnota = *ukUkazatel;

Zásobník a hromada

Paměť se dá rozdělit na 5 oblastí: Lokální proměnné a parametry funkcí jsou uloženy v zásobníku. Kód je v prostoru kódu a globální proměnné v globálním oboru názvů. Pro interní funkce správy běhu programu, sledování vrcholu zásobníku a ukazatele na aktuální instrukci se používají registry.
Veškerá zbývající pamět se stává součástí volné oblasti paměti!
HROMADA JE "VELKÉ MNOŽSTVÍ SEKVENČNĚ OČÍSLOVANÝCH PŘIHRÁDEK PAMĚTI, DO KTERÝCH LZE VKLÁDAT DATA, ALE KTERÉ SE NEDAJÍ OZNAČIT! JE NUTNÉ ZÍSKAT ADRESU PŘIHRÁDKY, ULOŽIT JI DO UKAZATELE A PŘISTUPOVAT NEPŘÍMO K DANÉ PŘIHRÁDCE (PŘES UKAZATEL)
PŘÍMÝ PŘÍSTUP NENÍ MOŽNÝ!


Výhodou hromady je, že vyhrazená paměť se uvolní až po skončení programu nebo po explicitním uvolnění.
Lokální proměnné mají krátkou životnost - po návratu z funkce jsou všechny tyto proměnné zrušeny.
Pokud uvnitř funkce vyhradíme pamět v hromadě, je dostupná i po návradu z funkce!!! A pokud správně předáváme příslušné ukazatele na alokovanou pamět mezi funkcemi, pak se nemůže stát, že se hodnota nečekaně změní, jak je to snadno možné u globálních proměnných.

Klíčové slovo new

Pro vyhrazení paměti v hromadě slouží klíčové slovo new
Za klíčovým slovem následuje typ objektu, který chceme vytořit, aby kompilátor mohl vyhradit příslušnou paměť.
new unsigned short int;  //vyhradˇ se 2B pamŘti
new unsigned long int;   //vyhradˇ se 4B pamŘti
Návratová hodnota operátoru new je PAMĚŤOVÁ ADRESA, kterou je nutné přiřadit ukazateli!!!
Chceme-li tedy ve volné oblasti paměti vytvořit objekt typu unsigned short, je třeba napsat:
unsigned short int * ukUkazatel;
ukUkazatel = new unsigned short int;
nebo třeba přímo:
unsigned short int * ukUkazatel = new unsigned short int;
Ukazatel ukUkazatel se bude odkazovat ne objekt typu unsigned short int ve volné oblasti paměti. Ukazatel se používá stejně jako každý jiný ukazatel (viz. výše).
Například do jeho oblasti paměti lze vložit hodnotu 150:
*ukUkazatel = 150;
"Vlož 150 do odkazu ukazatel".

Klíčové slovo delete

slouží k uvolnění části paměti hromady, na kterou ukazuje ukazatel (která byla vyhrazena klíčovým slovem new).
delete ukUkazatel;
vrátí vyhrazenou pamět (2B pro unsigned short) zpět do volné oblasti paměti.
Ukazatel lze znovu pouřít pro vyhrazení paměti. Po uvolnění paměti je nutné ukazatel VYNULOVAT. Pokud totiž uvolníme pamět, hodnota ukazatele není nijak definovaná. Pokud na něj zavoláme znovu delete, program se zhroutí!
Volání delete na nulový ukazatel je neškodné!
Např.:
Zvire *ukPes=new Zvire;
delete ukPes;    //uvolnění paměti
       ukPes=0;  //nastav ukazatel na nulu
delete ukPes;    //neškodné‚

Úniky paměti


Ukazatel je LOKÁLNÍ PROMĚNNÁ!!! Po návratu z funkce, v níž byl deklarován, je jako ostatní lokální proměnné mimo svůj obor platnosti a zmizí. Pokud tedy nebyla uvolněna pamět, na niž ukazoval, je tato část paměti NEDOSTUPNÁ! Není možné se na ni dostat a uvolnit ji. Uvolní se po skončení programu.

Další možný únik paměti nastane, pokud uklazateli přiřadíme novou adresu, aniž bychom nejdříve uvolnili paměť!
unsigned short int * ukUkazatel=new unsigned short int;
*ukUkazatel = 72;
ukUkazatel=new unsigned short int;
*ukUkazatel=84;
Pokud nyní zavoláme delete ukUkazatel; uvolní se pamět na adrese, kam byla vložena hodnota 84. Ale paměť na adrese, kde je vložena hodnota 72 už uvolnit nejde, protože se na ni nemáme jak dostat!!!

KAŽDÉ VOLÁNÍ new BY MĚLO MÍT ODPOVÍDAJÍCÍ delete!

Příklad: přidělení a uvolnění paměti
// Alokace a smazání ukazatele

#include <iostream>

int main()
{
   using std::cout;

   int lokalniPromenna = 5;
   int * ukLokalni = &lokalniPromenna;
   int * ukHromada = new int;

   *ukHromada = 7;
   cout << "lokalniPromenna: " << lokalniPromenna << "\n";
   cout << "*ukLokalni: " << *ukLokalni << "\n";
   cout << "*ukHromada: " << *ukHromada << "\n";
   delete ukHromada;
   ukHromada = new int;
   *ukHromada = 9;
   cout << "*ukHromada: " << *ukHromada << "\n";
   delete ukHromada;

   return 0;
}
lokalniPromenna: 5
*ukLokalni:      5
*ukHromada:      7
*ukHromada:      9

Vytvoření objektu ve volném úložišti

Ukazatel na objekt lze vytvořit stejně jako na např. celočíselný typ. Jestliže je deklarován objekt typu Kocka, lze deklarovat i ukazatel na tuto třídu a vytvořit instanci objektu Kocka nejen na zásobníku, ale i ve volném úložišti.
Kocka *ukKocka = new Kocka;
Dojde k zavolání výchozího konstuktoru (bez parametrů). Tento konstruktor je volán vždy při vytvoření objektu (v zásobníku nebo volném úložišti).

Vymazání objektu

Když je na ukazatel na nějaký objekt ve volném úložišti zavolán operátor delete dojde ještě před samotným uvolněním paměti k zavolání destruktoru objektu!
To umožňuje zbavit se nepotřebných dat podobně, jako u odstraňování ze zásobníku.
Příklad: vytvořen a vymazání objektu ve volném úložišti
// Vytváření objektů ve volné oblasti paměti
// prostřednictvím příkazů new a delete

#include <iostream>

class ProstaKocka
{
public:
   ProstaKocka();
   ~ProstaKocka();
private:
   int vekKocky;
};

ProstaKocka::ProstaKocka()
{
   std::cout << "Zavolan konstruktor.\n";
   vekKocky = 1;
}

ProstaKocka::~ProstaKocka()
{
   std::cout << "Zavolan destruktor.\n";
}

int main()
{
   std::cout << "ProstaKocka Micka...\n";
   ProstaKocka Micka;
   std::cout << "ProstaKocka * ukMourek = new ProstaKocka...\n";
   ProstaKocka * ukMourek = new ProstaKocka;
   std::cout << "delete ukMourek...\n";
   delete ukMourek;
   std::cout << "Konec programu, vsimnete si destrukce Micky...\n";
   return 0;
}
ProstaKocka Micka...
Zavolan konstruktor.
ProstaKocka * ukMourek=new ProstaKocka...
Zavolan konstruktor.
delete ukMourek...
Zavolan destruktor.
Konec programu, vsimnete si destrukce Micky...
Zavolan destruktor.

Přístup k datovým členům

K lokálně vytvořeným datovým členům a funkcím objektu třídy Kocka jsme získali přístup pomocí operátoru tečka.
Pro přístup k objektu Kocka umístěnému ve volné oblasti paměti musíme provést zrušení odkazu ukazatele a na objekt, na který se tento ukazatel odkazuje zavolat operátor tečka.
(*ukMourek).ZiskejVek();
Závorky se používají proto, aby bylo zřejmé, že nejprve má dojít ke zrušení odkazu ukazatele ukMourek a potom se má zpřístupnit funkce ZiskejVek().
Pro nahrazení tohoto zápisu složí operátor ->

Příklad: získání přístupu ke členským datům objektů ve volném ůložišti
// Přístup k datovým členům objektů na hromadě
// prostřednictvím operátoru ->

#include <iostream>

class ProstaKocka
{
public:
   ProstaKocka() { vekKocky = 2; }
   ~ProstaKocka() {}
   int ZiskejVek() const { return vekKocky; }
   void NastavVek(int vek) { vekKocky = vek; }
private:
   int vekKocky;
};

int main()
{
   ProstaKocka * Micka = new ProstaKocka;
   std::cout << "Micka ma " << Micka->ZiskejVek() << " roku\n";
   Micka->NastavVek(5);
   std::cout << "Micka ma " << Micka->ZiskejVek() << " roku\n";
   delete Micka;
   return 0;
}
Micka ma 2 roku.
Micka ma 5 roku.

Členská data ve volkním úložišti

Datové členy třídy mohou být ukazateli na objekty ve volném úložišti. Paměť lze přidělit v konstruktoru nebo metodě třídy a uvolnit v destruktoru třídy.
Příklad: Ukazatele jako členská data
// Ukazatele jako datoví členové,
// ke kterým se přistupuje operátorem >

#include <iostream>

class ProstaKocka
{
public:
   ProstaKocka();
   ~ProstaKocka();
   int ZiskejVek() const { return *vekKocky; }
   void NastavVek(int vek) { *vekKocky = vek; }

   int ZiskejVahu() const { return *vahaKocky; }
   void NastavVahu(int vaha) { *vahaKocky = vaha; }

private:
   int * vekKocky;
   int * vahaKocky;
};

ProstaKocka::ProstaKocka()
{
   vekKocky = new int(2);
   vahaKocky = new int(5);
}

ProstaKocka::~ProstaKocka()
{
   delete vekKocky;
   delete vahaKocky;
}

int main()
{
   ProstaKocka * Micka = new ProstaKocka;
   std::cout << "Micka ma " << Micka->ZiskejVek() << " roku\n";
   Micka->NastavVek(5);
   std::cout << "Micka ma " << Micka->ZiskejVek() << " roku\n";
   delete Micka;
   return 0;
}
Micka ma 2 roku.
Micka ma 5 roku.
Členské proměnné třídy se deklarují jako ukazatele na celočíselný typ (int * vekKocky; int vahaKocky;).
Konstruktor inicializuje ukazatele na pamět ve volném úložišti a přiřadí jim výchozí hodnoty (vekKocky=new int(2);vahaKocky=new int(5);).
Destruktory uvolňují alokovanou paměť (delete vekKocky; delete vahaKocky;). Není nutné nulovat ukazatele, protože přestanou existovat! (v destruktoru zanikají).

Ukazatel this

Je skrytým parametrem každé členské funkce třídy. Ukazuje na konkrétní objekt, se kterým třída právě pracuje.
V každém volání funkce ZiskejVek() a NastavVek() je zahrnut skrytý parametr - ukazatel this na aktuální objekt.
Př.:
// Používání speciálního ukazatele "this"


#include <iostream.h>

class Obdelnik
{
public:
   Obdelnik();
   ~Obdelnik();
   void NastavDelku(int delka)
      { this->delkaObd = delka; }
   int ZiskejDelku() const
      { return this->delkaObd; }

   void NastavSirku(int sirka)
      { sirkaObd = sirka; }
   int ZiskejSirku() const
      { return sirkaObd; }

private:
   int delkaObd;
   int sirkaObd;
};

Obdelnik::Obdelnik()
{
   sirkaObd = 5;
   delkaObd = 10;
}
Obdelnik::~Obdelnik()
{}

int main()
{
   Obdelnik obd;
   cout <<"obd je " << obd.ZiskejDelku()
      <<" centimetru dlouhy.\n";
   cout <<"obd je " << obd.ZiskejSirku()
      <<" centimetru siroky.\n";
   obd.NastavDelku(20);
   obd.NastavSirku(10);
   cout <<"obd je " << obd.ZiskejDelku()
      <<" centimetru dlouhy.\n";
   cout <<"obd je " << obd.ZiskejSirku()
      <<" centimetru siroky.\n";
   return 0;
}
Mezi chováním funkcí pro délku a šířku není rozdíl - i bez ukazatele this, fungují stejně.
Více o this v dni 10.

Zbloudilé ukazatele

Vznikají uvolněním paměti a následným NENULOVÁNÍM ukazatele. To jest, když po volání delete Ukazatel; nenastavíme Ukazatel=0;
Při pokusu znovu použít ukazatel, aniž bychom mu přiřadili novou adresu, nelze jistě očekávat žádný výsledek. Program se může zhroutit, nebo se zhroutit až v jiné fázi...takovéto chyby se pak těžce hledají.
Rozdíl mezi nulovým a zbloudilým ukazatelem
Když se vymaže ukazatel, kompilátor uvolní paměť, ale ukazatel samotný i nadále existuje. Stane se z něj zbloudilý ukazatel.
Když se pak napíše
mujUkazatel=0;
, stane se ze zbloudilého ukazatele nulový ukazatel.
Za normálních okolností platí, že pokud se vymaže ukazatel a poté se vymaže znovu už jako zbloudilý ukazatel, bude chování programu nedefinované. To znamená, že se může stát ledacos; při troše štěstí může dojít ke zhroucení programu. Jestliže se vymaže nulový ukazatel, nestane se nic, což je bezpečnější.
Používání zbloudilého nebo nulového ukazatele NENÍ DOVOLENO!
(*mujUkazatel=5;)
a může vést ke zhroucení programu. Je-li však ukazatel nulový, program se zhroutí určítě, což je další výhoda proti zbloudilému ukazateli. Dává se přednost předvídanému zhroucení programu, protože se snáz ladí chyby.

Ukazatele const

Klíčové slovo const se může používat před, za nebo po obou stranách typu ukazatele:
const int * uk1;
int * const uk2;
const int * const uk3;

Podíváme li se napravo od klíčového slovo const, vidíme co je deklarováno jako konstantní - je-li to typ, pak bude konstantní hodnota. Je-li napravo proměnná ukazatele, pak bude konstantní ukazatel.

Ukazatele const a členské funkce const

Když se funkce deklaruje jako const viz. 6 pak kompilátor ohlási jako chybu jakýkoliv pokus o změnu dat objektu uvnitř této funkce.
Jestliže je deklarován ukazatel na objekt označený klíčovým slovem const, pak jedinými metodami, které lze prostřednictvím tohoto ukazatele volat, jsou metody označené const
Příklad:
// Používání ukazatelů spolu s konstantními metodami

#include <iostream>
using namespace std;

class Obdelnik
{
public:
   Obdelnik();
   ~Obdelnik();
   void NastavDelku(int delka) { delkaObd = delka; }
   int ZiskejDelku() const { return delkaObd; }
   void NastavSirku(int sirka) { sirkaObd = sirka; }
   int ZiskejSirku() const { return sirkaObd; }

private:
   int delkaObd;
   int sirkaObd;
};

Obdelnik::Obdelnik()
{
   sirkaObd = 5;
   delkaObd = 10;
}

Obdelnik::~Obdelnik()
{}

int main()
{
   Obdelnik * ukObd = new Obdelnik;
   const Obdelnik * ukKonstObd = new Obdelnik;
   Obdelnik * const ukKonstUkaz = new Obdelnik;

   cout <<"ukObd sirka: " << ukObd->ZiskejSirku()
      <<" centimetru\n";
   cout <<"ukKonstObd sirka: " << ukKonstObd->ZiskejSirku()
      <<" centimetru\n";
   cout <<"ukKonstUkaz sirka: " << ukKonstUkaz->ZiskejSirku()
      <<" centimetru\n";

   ukObd->NastavSirku(10);
   // ukKonstObd->NastavSirku(10);
   ukKonstUkaz->NastavSirku(10);

   cout <<"ukObd sirka: " << ukObd->ZiskejSirku()
      <<" centimetru\n";
   cout <<"ukKonstObd sirka: " << ukKonstObd->ZiskejSirku()
      <<" centimetru\n";
   cout <<"ukKonstUkaz sirka: " << ukKonstUkaz->ZiskejSirku()
      <<" centimetru\n";
   return 0;
}
ukObd sirka: 5 centimetru
ukKonstObd sirka: 5 centimetru
ukKonstUkaz sirka: 5 centimetru
ukObd sirka: 10 centimetru
ukKonstObd: 5 centimetru
ukKonstUkaz sirka: 10 centimetru

Máme-le deklarován ukazatel na konstantí objekt - ukKonstObd, pak může volat jen konstantní členské funkce - ZiskejDelku() a ZiskejSirku().

Ukazatele const this

Když je objekt deklarován jako konstantní, znamená to, že ukazatel this je ukazatelem na konstantní objekt. Ukazatel const this lze používat jen u konstantních členských funkcí.

Aritmetika ukazatelů - pro pokročilé

Ukazatele je možné jeden od druhého odečítat. Jedna účinná technika spočívá v definici dvou ukazatelů, které ukazují na různé prvky v poli, přičemž jejich rozdíl říká, kolik prvků tyto dva členy odděluje.
To je užitečné například při syntaktické analýze znakových polí:
#include <iostream>
#include <ctype.h>
#include <string.h>

bool ZiskejSlovo(char * retezec,
                 char * slovo, int& posunSlovo);

// řídicí program
int main()
{
   const int velPamet = 255;
   char pamet[velPamet+1];   // paměť pro uchování celého řetězce
   char slovo[velPamet+1];   // paměť pro uchování slova
   int posunSlovo = 0;       // začínáme na začátku

   std::cout <<"Vlozte retezec: ";
   std::cin.getline(pamet,velPamet);

   while (ZiskejSlovo(pamet,slovo,posunSlovo))
   {
      std::cout <<"Ziskano slovo: " << slovo << std::endl;
   }

   return 0;

}


// funkce pro rozklad řetězce a získání slova
bool ZiskejSlovo(char* retezec, char* slovo, int& posunSlovo)
{

   if (!retezec[posunSlovo])    // konec řetězce?
      return false;

   char *p1, *p2;
   p1 = p2 = retezec+posunSlovo;   // ukaž na následující slovo

   // pohlcení počátečních mezer
   for (int i = 0; i<(int)strlen(p1) && !isalnum(p1[0]); i++)
      p1++;

   // podívejme se, jestli máme slovo
   if (!isalnum(p1[0]))
      return false;

   // p1 nyní ukazuje na začátek následujícího slova
   // nechme p2 ukazovat na stejné místo
   p2 = p1;

   // přejděme s p2 na konec slova
   while (isalnum(p2[0]))
      p2++;

   // p2 nyní ukazuje na konec slova
   // p1 nyní ukazuje na začátek slova
   // délka slova je jejich rozdíl
   int delka = int (p2 - p1);

   // zkopírování slova do připravené paměti
   strncpy (slovo,p1,delka);

   // ukončíme řetězec nulou
   slovo[delka]='\0';

   // nyní najdeme začátek následujícího slova
   for (int j = int(p2-retezec); j<(int)strlen(retezec)
      && !isalnum(p2[0]); j++)
   {
      p2++;
   }

   posunSlovo = int(p2-retezec);

   return true;
}
Vlozte retezec: zprava o jazyce C++
Ziskano slovo: zprava
Ziskano slovo: o
Ziskano slovo: jazyce
Ziskano slovo: C
Řádek 32: if (!retezec[posunSlovo]) využívá vlastnosti C++, kde se hodnota 0 považuje za false. Tento řádek by se dal nahradit následovně: if (retezec[posunSlovo]==0)
K hledání slov se používají dva ukazatele uk1 a uk2, které se nastaví dovnitř řetězce na pozici danou indexem posunSlovo. Na začátku je index nula, tedy oba ukazují na začátek slova. uk1 se nastaví na první alfanumerický znak, pak se uk2 nastaví na stejnou pozici - oba ukazují na začátek slova. Prochází se slovo dokud se nenarazí na nealfanumerický znak a uk2 bude ukazovat na konec slova. Odečtením ukazatelů získáme počet písmen nalezeného slova, což využijeme pro zkopírování slova funkcí strcopy

9 - Odkazy

ODKAZ

Odkaz je jiné jméno pro proměnnou - alias.
Vytvoření odkazu:
Typ cílového objektu, operátor odkazu (&) a název odkazu. Odkaz je inicializován názvem jiného objektu. Odkazy jsou SYNONYMA cílů.
ODKAZY SE VŽDY MUSÍ INICIALIZOVAT

int &odNejakýOdkaz = nejakaPromenna;
Kde odkaz odNejakyOdkaz je odkazem na celočíselnou promměnnou nejakaPromenna a lze jej kdykoliv použít místo samotné proměnné.

Odkazy mohou mít libovolný název, používají se ale často názvy začínající od nebo r /reference/.

int &odNejakýOdkaz = nejakaPromenna;
je totožné s
int & odNejakýOdkaz = nejakaPromenna;
a také s
int& odNejakyOdkaz=nejakaPromenna;

Prázdné znaky se v kódu ignorují, proto jsou správné všechny tyto možnosti zápisu. Ale POZOR:
KOCKA& odOdkaz1,odOdkaz2
Znamená to, že odOdkaz1 je odkaz na objekt KOCKA, ale odOdkaz2 je pouze objekt typu KOCKA.

Příklad:
// Ukázka používání odkazů

#include <iostream>

int main()
{
   using namespace std;
   int prvniProm;
   int &odkaz = prvniProm;

   prvniProm = 5;
   cout << "prvniProm: " << prvniProm << endl;
   cout << "odkaz: " << odkaz << endl;

   odkaz = 7;
   cout << "prvniProm: " << prvniProm << endl;
   cout << "odkaz: " << odkaz << endl;

   return 0;
}
prvniProm: 5
odkaz: 5
PrvniProm: 7
odkaz: 7

Operátor adresy & aplikovaný na odkazy

Jestliže aplikujeme operátor adresy na odkaz, získáme adresu jeho cíle. Odkazy jsou totiž jiná jména pro cílové proměnné.
Příklad:
#include <iostream>

int main()
{
   using namespace std;
   int prvniProm;
   int &odkaz = prvniProm;

   prvniProm = 5;
   cout << "prvniProm: " << prvniProm << endl;
   cout << "odkaz: " << odkaz << endl;

   cout << "&prvniProm: " << &prvniProm << endl;
   cout << "&odkaz: " << &odkaz << endl;

   return 0;
}
prvniProm: 5
odkaz: 5
&prvniProm: 0012FF7C
&odkaz: 0012FF7C

Přiřazení jiné hodnoty odkazu

Pokud odkazu po inicializaci přiřadíme jinou proměnnou, nestane se synonymem této proměnné, ale proměnné, jejímž je odkaz aliasem se přiřadí hodnota nové proměnné.
Příklad:
// Přiřazení nové hodnoty odkazu

#include <iostream>

int main()
{
   using namespace std;
   int prvniProm;
   int &odkaz = prvniProm;

   prvniProm = 5;
   cout << "prvniProm:\t " << prvniProm << endl;
   cout << "odkaz:\t\t " << odkaz << endl;
   cout << "&prvniProm:\t " << &prvniProm << endl;
   cout << "&odkaz:\t\t " << &odkaz << endl;

   int druhaProm = 8;
   odkaz = druhaProm;    // Co myslíte, že se stane?   
   cout << "\nprvniProm:\t " << prvniProm << endl;
   cout << "druhaProm:\t " << druhaProm << endl;
   cout << "odkaz:\t\t " << odkaz << endl;
   cout << "&prvniProm:\t " << &prvniProm << endl;
   cout << "&druhaProm:\t " << &druhaProm << endl;
   cout << "&odkaz:\t\t " << &odkaz << endl;

   return 0;
}
prvniProm: 		5
odkaz: 		5
&prvniProm: 	0012FF7C
&odkaz: 		0012FF7C

prvniProm: 		8
druhaProm: 		8
odkaz: 		8
&prvniProm: 	0012FF7C
&druhaProm: 	0012FF74
&odkaz: 		0012FF7C
ODKAZOVAT SE LZE NA LIBOVOLNÝ OBJEKT! I UŽIVATELEM VYTVOŘENÝ. NELZE SE VŠAK ODKAZOVAT NA TYP!!!
int &odOdkaz=int; //CHYBA
Kocka &odKocka=Kocka; //CHYBA
ale správně je:
Kocka Micka;
Kocka &OdKocka=Micka;

Příklad:
// Odkazy na objekty tříd

#include <iostream>

class ProstaKocka
{
public:
   ProstaKocka (int vek, int vaha);
   ~ProstaKocka() {}
   int ZiskejVek() { return vekKocky; }
   int ZiskejVahu() { return vahaKocky; }
private:
   int vekKocky;
   int vahaKocky;
};

ProstaKocka::ProstaKocka(int vek, int vaha)
{
   vekKocky = vek;
   vahaKocky = vaha;
}

int main()
{
   ProstaKocka Micka(5,8);
   ProstaKocka & odKocka = Micka;

   std::cout << "Micka ma ";
   std::cout << Micka.ZiskejVek() << " roku. \n";
   std::cout << "A Micka vazi ";
   std::cout << odKocka.ZiskejVahu() << " kilogramu. \n";
   return 0;
}
Micka ma 5 roku.
A Micka vazi 8 kilogramu.

Nulové ukazatele a nulové odkazy

Pokud nejsou ukazatele inicializovány, nebo se jejich data vymažou, má se jim přiřadit nulová hodnota.
Odkazy ale nulové být nemohou! Program s odkazem na nulový objekt se považuje za neplatný!

Předávání argumentů funkcí odkazem

Funkce mají dvě omezení: argumenty se předávájí hodnotou a návratový příkaz může vrátit jen jednu hodnotu.
Obě tato omezení lze překonat předávání hodnot funkcí odkazem. A to se dá uskutečnit pomocí ukazatelů nebo odkazů.
Příklad:
//demonstruje předání hodnotou

#include <iostream>

void zamena(int x, int y);

int main()
{
   int x = 5, y = 10;

   std::cout << "Funkce main. Pred zamenou, x: " << x << " y: " << y << "\n";
   zamena(x,y);
   std::cout << "Funkce main. Po zamene, x: " << x << " y: " << y << "\n";
   return 0;
}

void zamena (int x, int y)
{
   int pom;

   std::cout << "Funkce zamena. Pred zamenou, x: " << x << " y: " << y << "\n";

   pom = x;
   x = y;
   y = pom;

   std::cout << "Funkce zamena. Po zamene, x: " << x << " y: " << y << "\n";
}
Uvnitř funkce nedošlo ke změně, protože jde o předání hodnotou. Pro správnou funkčnost musíme předávat hodtnoty odkazem, například pomocí ukazatelů:
//Demonstruje předání odkazem

#include <iostream>

void zamena(int *x, int *y);

int main()
{
   int x = 5, y = 10;

   std::cout << "Funkce main. Pred zamenou, x: " << x << " y: " << y << "\n";
   zamena(&x,&y);
   std::cout << "Funkce main. Po zamene, x: " << x << " y: " << y << "\n";
   return 0;
}

void zamena (int *ox, int *oy)
{
   int pom;

   std::cout << "Funkce zamena. Pred zamenou, *ox: " << *ox <<
      " *oy: " << *oy << "\n";

   pom = *ox;
   *ox = *oy;
   *oy = pom;

   std::cout << "Funkce zamena. Po zamene, *ox: " << *ox <<
      " *oy: " << *oy << "\n";
}
a nebo pomocí odkazů:
// demonstruje předání odkazem s pomocí odkazu!

#include <iostream>

void zamena(int &x, int &y);

int main()
{
   int x = 5, y = 10;

   std::cout << "Funkce main. Pred zamenou, x: " << x <<
      " y: " << y << "\n";

   zamena(x,y);

   std::cout << "Funkce main. Po zamene, x: " << x <<
      " y: " << y << "\n";

   return 0;
}

void zamena (int &ox, int &oy)
{
   int pom;

   std::cout << "Funkce zamena. Pred zamenou, ox: " << ox <<
      " oy: " << oy << "\n";

   pom = ox;
   ox = oy;
   oy = pom;

   std::cout << "Funkce zamena. Po zamene, ox: " << ox <<
      " oy: " << oy << "\n";
}
Funkce main. Pred zamenou, x: 5 y: 10
Funkce zamena. Pred zamenou, ox: 5 oy: 10
Funkce zamena. Po zamene, ox: 10 oy: 5
Funkce main. Po zamene, x: 10 y: 5

Vrácení více hodnot

Funkce umožnuje vracet jednu hodnotu. Pokud chceme vrátic více hodnot, předáme funkci více objektů ODKAZEM a nebo pomocí ukazatelů. Tím se obchází návratová hodnota a lze využít např. pro podávání zpráv o chybách.
Příklad: pomocí ukazatelů
// Vrácení různých hodnot funkcí

#include <iostream>

using namespace std;
short Faktor(int c, int* oDruhaMocnina, int* oTretiMocnina);

int main()
{
   int cislo, druhaMocnina, tretiMocnina;
   short chyba;

   cout << "Vlozte cislo (0 - 20): ";
   cin >> cislo;

   chyba = Faktor(cislo, &druhaMocnina, &tretiMocnina);

   if (!chyba)
   {
      cout << "cislo: " << cislo << "\n";
      cout << "druha mocnina: " << druhaMocnina << "\n";
      cout << "treti mocnina: " << tretiMocnina << "\n";
   }
   else
      cout << "Doslo k chybe!!\n";
   return 0;
}

short Faktor(int c, int* oDruhaMocnina, int* oTretiMocnina)
{
   short Hodnota = 0;
   if (c > 20)
      Hodnota = 1;
   else
   {
      *oDruhaMocnina = c*c;
      *oTretiMocnina = c*c*c;
      Hodnota = 0;
   }
   return Hodnota;
}
Funkci faktor se předá zadané číslo a adresy proměnných drhuhaMocnina a tretiMocninave funkci se ukazatelům přiřadí hodnota, která je dostupná i ve funkci main.

Příklad: využítí odkazů
// Vrácení různých hodnot funkcí za pomoci odkazů

#include <iostream>

using namespace std;
typedef unsigned short USHORT;
enum KOD_CHYBY { USPECH, CHYBA };

KOD_CHYBY Faktor(USHORT, USHORT&, USHORT&);

int main()
{
   USHORT cislo, druhaMocnina, tretiMocnina;
   KOD_CHYBY vysledek;

   cout << "Vlozte cislo (0 - 20): ";
   cin >> cislo;

   vysledek = Faktor(cislo, druhaMocnina, tretiMocnina);

   if (vysledek == USPECH)
   {
      cout << "cislo: " << cislo << "\n";
      cout << "druha mocnina: " << druhaMocnina << "\n";
      cout << "treti mocnina: " << tretiMocnina << "\n";
   }
   else
      cout << "Doslo k chybe!!\n";
   return 0;
}

KOD_CHYBY Faktor(USHORT c, USHORT &oDruhaMocnina, USHORT &oTretiMocnina)
{
   if (c > 20)
      return CHYBA;          // jednoduchý kód chyby
   else
   {
      oDruhaMocnina = c*c;
      oTretiMocnina = c*c*c;
      return USPECH;
   }
}

Předávání odkazem kvůli výkonu

Při každém předání objektu hodnotou se vytvoří kopie tohoto objektu. Při každém vrácení objektu z funkce se vytvoří další kopie. Tyto objekty se kopírují do zásobníku a zabírají místo a taky mírně zpomalují běh programu.
Pokud se jedná o uživatelem vytvořený objekt, pak při vytvoření každé kopie se volá konstruktor pro kopírování. Když se dočasný objekt zruší (tj. po návratu z funkce) je zavolán destruktor objektu. Jestli je objekt vrácený hodnotou, vytvoří se nejdříve kopie, která se pak musí také zničit.

Příklad:
// Předávání ukazatelů objektům

#include <iostream>

using namespace std;
class ProstaKocka
{
public:
   ProstaKocka ();                      // konstruktor
   ProstaKocka(ProstaKocka&);           // konstruktor pro kopírování
   ~ProstaKocka();                      // destruktor
};

ProstaKocka::ProstaKocka()
{
   cout << "Prosta Kocka: Konstruktor...\n";
}

ProstaKocka::ProstaKocka(ProstaKocka&)
{
   cout << "Prosta Kocka: Konstruktor pro kopirovani...\n";
}

ProstaKocka::~ProstaKocka()
{
   cout << "Prosta Kocka: Desktruktor...\n";
}

ProstaKocka Funkce1 (ProstaKocka kocka);
ProstaKocka* Funkce2 (ProstaKocka *kocka);

int main()
{
   cout << "Vytvoreni kocky...\n";
   ProstaKocka Micka;
   cout << "Volani Funkce1...\n";
   Funkce1(Micka);
   cout << "Volani Funkce2...\n";
   Funkce2(&Micka);
   return 0;
}

// Funkce1, předání hodnotou
ProstaKocka Funkce1 (ProstaKocka kocka)
{
   cout << "Funkce1, navrat...\n";
   return kocka;
}

// Funkce1, předání odkazem
ProstaKocka* Funkce2 (ProstaKocka *kocka)
{
   cout << "Funkce2, navrat...\n";
   return kocka;
}
Vytvoreni kocky...
Prosta Kocka: Konstruktor...
Volani Funkce1...
Prosta Kocka: Konstruktor pro kopirovani...
Funkce1, navrat...
Prosta Kocka: Konstruktor pro kopirovani...
Prosta Kocka: Destruktor...
Prosta Kocka: Destruktor...
Volani Funkce2...
Funkce2, navrat...
Prosta Kocka: Destruktor...

Předání ukazatele na konstantní objekt

Předávání odkazem je nebezpečné, protože máme přístup k adresovému prostoru objektu a je možné měnit hodnoty, které by měnit jít neměli. Předávání hodnotou zajištuje bezpečí objektů před změnou.
Dalším řešením je předání UKAZATELE NA KONSTANTNÍ OBJEKT - tím se zabrání volání jakékoliv nekonstantní metody na daný objekt.
Příklad:
// Předávání ukazatelů objektům

#include <iostream>

using namespace std;
class ProstaKocka
{
public:
   ProstaKocka ();
   ProstaKocka(ProstaKocka&);
   ~ProstaKocka();

   int ZiskejVek() const { return vekKocky; }
   void NastavVek(int vek) { vekKocky = vek; }

private:
   int vekKocky;
};

ProstaKocka::ProstaKocka()
{
   cout << "Prosta Kocka: Konstruktor...\n";
    vekKocky = 1;
}

ProstaKocka::ProstaKocka(ProstaKocka&)
{
   cout << "Prosta Kocka: Konstruktor pro kopirovani...\n";
}

ProstaKocka::~ProstaKocka()
{
   cout << "Prosta Kocka: Desktruktor...\n";
}

const ProstaKocka* const Funkce2
   (const ProstaKocka * const kocka);

int main()
{
   cout << "Vytvoreni kocky...\n";
   ProstaKocka Micka;
   cout << "Micka ma ";
   cout << Micka.ZiskejVek();
   cout << " roku\n";
   int vek = 5;
   Micka.NastavVek(vek);
   cout << "Micka ma ";
   cout << Micka.ZiskejVek();
   cout << " roku\n";
   cout << "Volani Funkce2...\n";
   Funkce2(&Micka);
   cout << "Micka ma ";
   cout << Micka.ZiskejVek();
   cout << " roku\n";
   return 0;
}

// Funkce2, předání konstatním odkazem
const ProstaKocka * const Funkce2
   (const ProstaKocka * const kocka)
{
   cout << "Funkce2, navrat...\n";
   cout << "Micka ma nyni " << kocka->ZiskejVek();
   cout << " roku\n";
   // kocka->NastavVek(8);   const!
   return kocka;
}
ProstaKocka má dvě nové přístupové proměnné - ZiskejVek(), která je konstantní a NastavVek(), které konstantní není.
Parametr Funkce2() je deklarován jako konstantní ukazatel na konstantní objekt a návratová hodnota je rovněž konstantním ukazatelem na konstantní objekt.
Protože se parametr a vrácená hodnota předávají odkazem nepoužívá se konstruktor pro kopírování, protože se nevytvářejí kopie objektů.
UVNITŘ FUNKCE2() JE OBJEKT POVAŽOVÁN ZA KONSTANTNÍ A NELZE TEDY POUŽÍT NEKONSTANTNÍ PŘÍSTUPOVOU METODU NastavVek()!!!!

Program je funkční a splňuje požadavky, ale je stále poněkud těžkopádný. Jednodušší by bylo použít odkaz namísto ukazatele.

Odkazy jako alternativa

// Předávání odkazů na objekty

#include <iostream>

using namespace std;
class ProstaKocka
{
public:
   ProstaKocka ();
   ProstaKocka(ProstaKocka&);
   ~ProstaKocka();

   int ZiskejVek() const { return vekKocky; }
   void NastavVek(int vek) { vekKocky = vek; }

private:
   int vekKocky;
};

ProstaKocka::ProstaKocka()
{
   cout << "Prosta Kocka: Konstruktor...\n";
    vekKocky = 1;
}

ProstaKocka::ProstaKocka(ProstaKocka&)
{
   cout << "Prosta Kocka: Konstruktor pro kopirovani...\n";
}

ProstaKocka::~ProstaKocka()
{
   cout << "Prosta Kocka: Desktruktor...\n";
}

const ProstaKocka & Funkce2 (const ProstaKocka & kocka);

int main()
{
   cout << "Vytvoreni kocky...\n";
   ProstaKocka Micka;
   cout << "Micka ma " << Micka.ZiskejVek() << " roku\n";
   int vek = 5;
   Micka.NastavVek(vek);
   cout << "Micka ma " << Micka.ZiskejVek() << " roku\n";
   cout << "Volani Funkce2...\n";
   Funkce2(Micka);
   cout << "Micka ma " << Micka.ZiskejVek() << " roku\n";
   return 0;
}

// Funkce2, předání odkazem
const ProstaKocka & Funkce2 (const ProstaKocka & kocka)
{
   cout << "Funkce2, navrat...\n";
   cout << "Micka ma nyni " << kocka.ZiskejVek();
   cout << " roku\n";
   // kocka.NastavVek(8);   const!
   return kocka;
}
Konstantní odkazy
Programátoři C++ obvykle nerozlišují mezi "konstantním odkazem na objekt ProstaKocka" a "odkazem na konstantní objekt ProstaKocka". Odkazy samotné nelze nikdy změnit tak, aby odkazovali na jiný objekt, proto jsou z principu vždy konstantní. Jestliže se u odkazu použije klíčové slovo const, znamená to, že konstantní má být odkazovaný objekt.

Kdy používat odkazy a kdy ukazazele

Odkazy nelze změnit - jestli je nutné se nejdříve odkazovat na objekt a pak na jiný, je nutno použít ukazatel.
Odkazy nesmí být prázdné - existuje-li možnost, že objekt, na který se odkazujeme, může být nulový, nesmí se použít odkaz, ale ukazatel.
Příkladem je operátor new. Jestliže tento operátor nemůže alokovat požadovanou paměť, vrátí nulový ukazatel. Odkaz se tedy nesmí inicializovat na takto vytvořenou pamět, dokud není jisté že není nulová.
Příklad správného použití:
int *ukInt=new Int;
if (ukInt !=NULL)
  int &odInt = *ukInt;

Jinak programátoři dávají přednost odkazům před ukazateli.

Vrácení odkazu na objekt mimo obor platnosti

// Vrácení odkazu na objekt, který neexistuje

#include <iostream>


class ProstaKocka
{
public:
   ProstaKocka (int vek, int vaha);
   ~ProstaKocka() {}
   int ZiskejVek() const { return vekKocky; }
   int ZiskejVahu() const { return vahaKocky; }
private:
   int vekKocky;
   int vahaKocky;
};

ProstaKocka::ProstaKocka(int vek, int vaha)
{
   vekKocky = vek;
   vahaKocky = vaha;
}

ProstaKocka &Funkce();

int main()
{
   ProstaKocka &oKocka = Funkce();
   int vek = oKocka.ZiskejVek();
   std::cout << "oKocka ma " << vek << " roku!\n";
   return 0;
}

ProstaKocka &Funkce()
{
   ProstaKocka Micka(5,9);
   return Micka;
}
V programu inicializujeme odkaz na typ ProstaKocka návratovou hodnotou Funkce(), která je deklarována tak, že vrací odkaz na objekt ProstaKocka.
V těle Funkce() se deklaruje lokální objekt typu ProstaKocka a inicializují se hodnoty věku a váhy. Funkce pak vrací odkazem tento lokální objekt. Když se Funkce() vrátí, bude Micka zničena a odkaz bude alias na neexistující objekt.

Vracení odkazu na objekt ve volném úložišti

Problém v předchozím příkladu by se dal řešit vytvořením objektu Micka ve volném úložišti.
Příklad:
// Vyřešení problému s životností lokální proměnné

#include <iostream>

class ProstaKocka
{
public:
   ProstaKocka (int vek, int vaha);
   ~ProstaKocka() {}
   int ZiskejVek() const { return vekKocky; }
   int ZiskejVahu() const { return vahaKocky; }

private:
   int vekKocky;
   int vahaKocky;
};

ProstaKocka::ProstaKocka(int vek, int vaha)
{
   vekKocky = vek;
   vahaKocky = vaha;
}

ProstaKocka &Funkce();

int main()
{
   ProstaKocka & oKocka = Funkce();
   int vek = oKocka.ZiskejVek();
   std::cout << "oKocka ma " << vek << " roku!\n";
   std::cout << "&oKocka: " << &oKocka << std::endl;
   // Jak se zbavit této struktury v paměti?
   ProstaKocka * pKocka = &oKocka;
   delete pKocka;
   // Hmm, kam nyní ukazuje oKocka??
   return 0;
}

ProstaKocka & Funkce()
{
   ProstaKocka * pMicka = new ProstaKocka(5,9);
   std::cout << "pMicka: " << pMicka << std::endl;
   return *pMicka;
}

Vytvořením ukazatele ve Funkci() vytvoříme objekt ve volném úložišti. Ale pokud chceme zamezit úniku paměti, musíme deklarovat nový ukazatel na adresu paměti, kterou lze získat z odkazu na objekt. Ale po provedení delete, se budeme odkazovat na prázdný objekt!!!

Správné řešení je deklarovat objekt ve volající funkci a do Funkce() jej předat odkazem.

10 - Pokročilé funkce

Přetížené členské funkce

Přetížení funkcí znamená, že existují dvě nebo více funkcí se stejným názvem, ale odlišnými parametry. Přetížení členských funkcí třídy se provádí obdobně jako přetížení globálních funkcí.
Příklad:
// Přetížení členských funkcí třídy

#include <iostream>

// Deklarace třídy Obdélník
class Obdelnik
{
public:
	// konstruktory
	Obdelnik(int sirka, int vyska);
	~Obdelnik(){}

	// přetížená členská funkce KresliTvar třídy
	void KresliTvar() const;
	void KresliTvar(int sirkaA, int vyskaA) const;

private:
	int sirkaObd;
	int vyskaObd;
};

// Implementace konstruktoru
Obdelnik::Obdelnik(int sirka, int vyska)
{
	sirkaObd = sirka;
	vyskaObd = vyska;
}


// Přetížená funkce KresliTvar - nepřijímá žádné vstupní parametry
// Kreslí na základě aktuálních hodnot členských proměnnýcg
void Obdelnik::KresliTvar() const
{
	KresliTvar(sirkaObd, vyskaObd);
}


// Přetížená funkce KresliTvar - přijímá dva vstupní parametry
// Kreslí podle předaných parametrů
void Obdelnik::KresliTvar(int sirka, int vyska) const
{
	for (int i = 0; i<vyska; i++)
	{
		for (int j = 0; j<sirka; j++)
		{
			std::cout << "*";
		}
		std::cout << "\n";
	}
}

// Hlavní program pro demonstraci přetížených funkcí
int main()
{
	// inicializace obdélníka na rozměry 30,5
	Obdelnik obd(30,5);
	std::cout << "KresliTvar(): \n";
	obd.KresliTvar();
	std::cout << "\nKresliTvar(40,2): \n";
	obd.KresliTvar(40,2);
	return 0;
}

Používání výchozích hodnot

Stejně jako globální funkce může mít jednu nebo více výchozích hodnot, může mít výchozí hodnoty i členská funkce.
Příklad:
// Výchozí hodnoty členských funkcí třídy

#include <iostream>

using namespace std;

// Deklarace třídy Obdélník
class Obdelnik
{
public:
	// konstruktory
	Obdelnik(int sirka, int vyska);
	~Obdelnik(){}
	void KresliTvar(int sirkaA, int vyskaA,
		bool PouzitAktualni = false) const;

private:
	int sirkaObd;
	int vyskaObd;
};

// Implementace konstruktoru
Obdelnik::Obdelnik(int sirka, int vyska):
sirkaObd(sirka),       // inicializace
vyskaObd(vyska)
{}                     // prázdné tělo


// Třetí parametr má výchozí hodnotu
void Obdelnik::KresliTvar(
int sirka,
int vyska,
bool PouzitAktualni
) const
{
	int sirkaTisku;
	int vyskaTisku;

	if (PouzitAktualni == true)
	{
		sirkaTisku = sirkaObd;           // použití aktuálních hodnot objektu
		vyskaTisku = vyskaObd;
	}
	else
	{
		sirkaTisku = sirka;              // použití hodnot parametrů
		vyskaTisku = vyska;
	}


	for (int i = 0; i<vyskaTisku; i++)
	{
		for (int j = 0; j<sirkaTisku; j++)
		{
			cout << "*";
		}
		cout << "\n";
	}
}

// Hlavní program pro demonstraci přetížených funkcí
int main()
{
	// inicializace obdélníka na rozměry 30,5
	Obdelnik obd(30,5);
	cout << "KresliTvar(0,0,true): \n";
	obd.KresliTvar(0,0,true);
	cout << "\nKresliTvar(40,2): \n";
	obd.KresliTvar(40,2);
	return 0;
}

Metoda kresli tvar má výchozí hodnotu třetího parametru nastavenou na false a podle její hodnoty dochází ke kreslení správného tvaru.

Výchozí hodnoty nebo přetížení funkcí?

Přetížené funkce jsou srozumitelnější a dají se snadno rozšiřovat. Jejich použití je zvlášť vhodné, pokud:

Výchozí konstruktor

Pokud není pro třídu explicitně deklarován konstruktor, vytvoří se vlastní výchozí konstruktor, který nepřijímá žádné parametry a nic nedělá.
Konstruktor vytvořený kompilátorem se nazývá výchozí, ale výchozím konstruktorem se podle úmluvy nazývá jakýkoliv konstruktor, který nepřijímá žádné parametry. Pokud ale pro třídu vytvoříme nějaký konstruktor, výchozí konstruktor se již nevytvoří. Pokud tedy chceme mít ještě konstruktor bez parametrů, musíme jej explicitně vytvořit sami.

Přetížení konstuktorů

Smysl konstuktoru spočívá v zavedení objektu. Konstruktor Obdelnik vytvoří platný objekt obdélníku. Před spuštěním konstruktoru nmeexistuje žádný obdelník, pouze část paměti. Po ukončení konstruktoru exxistuje úplný objekt obdelníka, který je možno používat.
Přetížení konstruktorů se provádí stejně jako přetížení funkcí a je velmi silnou a flexibilní vlastností.
Příklad:
// Přetížení konstruktorů

#include <iostream>
using namespace std;

class Obdelnik
{
public:
	Obdelnik();
	Obdelnik(int sirka, int delka);
	~Obdelnik() {}
	int ZiskejSirku() const { return sirkaObd; }
	int ZiskejDelku() const { return delkaObd; }
private:
	int sirkaObd;
	int delkaObd;
};

Obdelnik::Obdelnik()
{
	sirkaObd = 5;
	delkaObd = 10;
}

Obdelnik::Obdelnik (int sirka, int delka)
{
	sirkaObd = sirka;
	delkaObd = delka;
}

int main()
{
	Obdelnik Obd1;
	cout << "Sirka Obd1: " << Obd1.ZiskejSirku() << endl;
	cout << "Delka Obd1: " << Obd1.ZiskejDelku() << endl;

	int sirkaA, delkaA;
	cout << "Zadejte sirku: ";
	cin >> sirkaA;
	cout << "\nZadejte delku: ";
	cin >> delkaA;

	Obdelnik Obd2(sirkaA, delkaA);
	cout << "\nSirka Obd2: " << Obd2.ZiskejSirku() << endl;
	cout << "Delka Obd2: " << Obd2.ZiskejDelku() << endl;
	return 0;
}

Sirka Obdl: 5
Delka Obdl: 10
Zadejte sirku: 20

Zadejte delku: 50

Sirka Obdl: 20
Delka Obdl: 50

Inicializace objektů

Doposud se členské proměnné nastavovali v těle konstruktoru.
Konstruktory ale mají dvě fáze: fázi inicializace a fazi spuštění samotného těla konstruktoru
Většinu proměnných lze nastavit v obou fázích. Šikovnější a často účinnější je nastavovat členské proměnné ve fázi inicializace.
Příklad:
KOCKA():	//název konstruktoru a parametry
vekKocky(5), vahaKocky(8)	//inicializační seznam
{}		//tělo konstruktoru
Za pravou závorkou uzavírající seznam parametrů konstruktoru následuje dvojtečka, za níž je název členské proměnné a pár závorek. Výraz uvnitř závorek se použije k inicializaci členské proměnné. Pokud je inicializací více, oddělují se od sebe čárkami.

Některé proměnné musí být inicializovány a nelze jim přiřadit hodnotu - například konstanty a odkazy.

Příklad:
Obdelnik::Obdelnik():
    sirkaObd(5),
    delkaObd(10)
{
}

Obdelnik::Obdelnik (int sirka, int delka):
    sirkaObd(5),
    delkaObd(10)
{
}

Konstruktor pro kopírování

Kromě výchozího konstruktoru a destruktoru nabízí kompilátor výchozí konstruktor pro kopírování. Kopírovací konstruktor se volá pokaždé, když se má vytvořit kopie objektu. Všechny kopírovací konstruktory akceptují jeden parametr - odkaz na objekt stejné třídy. Je vhodné tento odkaz vytvořit jako konstantní, protože konstruktor nebude muset předávaný objekt měnit. Např:
KOCKA (const KOCKA & Kocka);
Konstruktor KOCKA si zde bere jako parametr konstantní odkaz na existující objekt KOCKA. Úkolem konstruktoru pro kopírování je vyzvořit kopii objektu Kocka.
Výchozí konstruktor pro kopírování jednoduše zkopíruje všechny členské proměnné z objektu předávaného jako parametr do členských proměnných nového objektu. Tomu se říká členské, neboli povrchní kopírování. Občas při tomto postupu vznikají problémy při proměnných, které jsou ukazateli na objekty ve volném úložišti. Při hloubkovém kopírování se hodnoty alokované ve volném uložišti zkopírují do nově alokované paměti.
Při povrchním kopírování budou ukazatele do volného úložiště ukazovat na stejné místo. Pokud se ale jeden objekt dostane mimo obor platnosti, povede to ke katastrofě.
Řešení spočívá ve vytvoření vlastního kopírovacího konstruktoru a alokování paměti, když je to potřeba. Po alokaci paměti bude možné do této nové oblasti paměti zkopírovat původní hodnoty.
Příklad:
// Konstruktory pro kopírování

#include <iostream>
using namespace std;

class KOCKA
{
public:
	KOCKA();				// výchozí konstruktor
	KOCKA (const KOCKA &);	// konstruktor pro kopírování
	~KOCKA();				// destruktor
	int ZiskejVek()		const	{ return *vekKocky; }
	int ZiskejVahu()	const	{ return *vahaKocky; }
	void NastavVek(int vek)		{ *vekKocky = vek; }

private:
	int *vekKocky;
	int *vahaKocky;
};

KOCKA::KOCKA()
{
	vekKocky = new int;
	vahaKocky = new int;
	*vekKocky = 5;
	*vahaKocky = 9;
}

KOCKA::KOCKA(const KOCKA & ptr)
{
	vekKocky = new int;
	vahaKocky = new int;
	*vekKocky = ptr.ZiskejVek();   // veřejný přístup
	*vahaKocky = *(ptr.vahaKocky); // privátní přístup
}

KOCKA::~KOCKA()
{
	delete vekKocky;
	vekKocky = 0;
	delete vahaKocky;
	vahaKocky = 0;
}

int main()
{
	KOCKA micka;
	cout << "Vek Micky je: " << micka.ZiskejVek() << endl;
	cout << "Nastaveni veku Micky na 6...\n";
	micka.NastavVek(6);
	cout << "Vytvoreni Mourka na zaklade Micky\n";
	KOCKA mourek(micka);
	cout << "Vek Micky je: " << micka.ZiskejVek() << endl;
	cout << "Vek Mourka je: " << mourek.ZiskejVek() << endl;
	cout << "Nastaveni veku Micky na 7...\n";
	micka.NastavVek(7);
	cout << "Vek Micky je: " << micka.ZiskejVek() << endl;
	cout << "Vek Mourka je: " << mourek.ZiskejVek() << endl;
	return 0;
}

Vek Micky je: 5
Nastaveni veku Micky na 6...
Vytvoreni Mourka na zaklade Micky
Vek Micky je: 6
Vek Mourka je: 6
Nastaveni veku Micky na 7...
Vek Micky je: 7
Vek Mourka je 6

Přetížení operátorů

Jazyk C++ má řadu zabudovaných typů, z nichž každý má řadu zabudovaných operátorů.
Přetížení operátorů ukážeme na příkladě, na objektu Citac.
Příklad:
// Třída Čítač

#include <iostream>
using namespace std;

class Citac
{
public:
	Citac();
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }

private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

int main()
{
	Citac i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	return 0;
}
V této podobě se jedná o špatně použitelnou třídu. Na rozdíl od celočíselné hodnoty není možné hodnotu typu Citac inkrementovat, dekrementovat, nelze tyto objekty sčítat.

Definice funkce pro inkrementaci

Přetížením operátorů se tnovu zavádí většina funkcí, o které byla třída ochuzena. Například existují dva způsoby jak se dá objektu Citac přidat vlastnost inkrementace.
Příklad:
// Třída Čítač

#include <iostream>
using namespace std;

class Citac
{
public:
	Citac();
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	void Inkrementace() { ++hodnota; }

private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

int main()
{
	Citac i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	i.Inkrementace();
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	return 0;
}
Hodnota citace i je 0
Hodnota citace i je 1

Přetížení prefixového operátoru

Prefixový operátor je možné přetížit tak, že se deklaruje funkce v následujícím tvaru:
návratovýTyp Operator op()
v tomto případě tedy: void operator++ ()
Příklad:
// Třída Čítač
// prefixový operátor inkrementace

#include <iostream>
using namespace std;

class Citac
{
public:
	Citac();
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	void Inkrementace() { ++hodnota; }
	void operator++ () { ++hodnota; }

private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

int main()
{
	Citac i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	i.Inkrementace();
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	++i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	return 0;
}
Hodnota citace i je 0
Hodnota citace i je 1
Hodnota citace i je 2
Takto popsaný operátor má nedostatek - pokud jej použijeme na pravé straně výrazu, nepodaří se to.
Citac c= ++i;
Operátor inkrementace totiž nevrací žádnou hodnotu (void), proto hodnota c nebude inicializována.

Vrácení typů v přetížených operátorech

Operátor inkrementace by měl vracet objekt Citac, aby bylo možné jej přiřadit dalěímu objektu Citac. Jedna možnost je vrácení pomocí vytvoření dočasné proměnné.
Příklad:
// operátor++ vrací dočasný objekt

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	void Inkrementace() { ++hodnota; }
	Citac operator++ ();

private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

Citac Citac::operator++()
{
	++hodnota;
	Citac pom;
	pom.NastavHodnotu(hodnota);
	return pom;
}

int main()
{
	Citac i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	i.Inkrementace();
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	++i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	Citac a = ++i;
	cout << "Hodnota a: " << i.ZiskejHodnotu();
	cout << " a hodnota i: " << i.ZiskejHodnotu() << endl;
	return 0;
}
Hodnota citace i je 0
Hodnota citace i je 1
Hodnota citace i je 2
Hodnota a: 3 a hodnota i: 3

Vracení bezejmenného dočasného objektu

Není nutné vytvářet dočasný objekt pojmenovávat. Kdyby měla třída Citac konstruktor přijímající počáteční hodnotu čítače, stačilo by operátoru inkrementace jednoduše vracet výsledek tohoto konstruktoru.
Příklad:
// operátor++ vrací bezejmenný dočasný objekt

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	Citac(int hodnotaA);
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	void Inkrementace() { ++hodnota; }
	Citac operator++ ();

private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

Citac::Citac(int hodnotaA):
hodnota(hodnotaA)
{}

Citac Citac::operator++()
{
	++hodnota;
	return Citac (hodnota);
}

int main()
{
	Citac i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	i.Inkrementace();
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	++i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	Citac a = ++i;
	cout << "Hodnota a: " << i.ZiskejHodnotu();
	cout << " a hodnota i: " << i.ZiskejHodnotu() << endl;
	return 0;
}
Citac má nyní konstruktor přijímající parametr typu int. V implementaci operátoru ++ se vytvoří dočasný objekt, který se inicializuje hodnotou proměnné hodntota. Dočasné objekty se však musí vytvářet pomocí konstruktoru a zničit pomocí destruktoru, což jsou zbytečné operace.

Řešení s použitím ukazatele this

Ukazatel this se předává všem členským funkcím, včetně přetížených. Ukazatel this ukazuje na objekt i, a jestliže dojde ke zrušení odkazu u tohoto ukazatele, vrátí objekt i, který ve své členské proměnné hodnota už bude mít správnou hodnotu. Tímto způsobem se lze vyhnout dočasným objektům.
Příklad:
// Vrácení dereferencovaného ukazatele "this"

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	void Inkrementace() { ++hodnota; }
	const Citac& operator++ ();

private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

const Citac& Citac::operator++()
{
	++hodnota;
	return *this;
}

int main()
{
	Citac i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	i.Inkrementace();
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	++i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	Citac a = ++i;
	cout << "Hodnota a: " << i.ZiskejHodnotu();
	cout << " a hodnota i: " << i.ZiskejHodnotu() << endl;
	return 0;
}
Vrácený objekt Citac je konstantní. Kdyby nebyl, daly by se měnit jeho hodnoty např. Citac a = ++++i;

Přetížení postfixového operátoru

Pokud budeme přetěžovat postfixový operátor, musí kompilátor poznat, že se jedná o postfix. Platí úmluva, že pro postfixový operátor se jako parametr uvádí hodnota.
Příklad:
// Vrácení dereferencovaného ukazatele "this"

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	const Citac& operator++ ();    // prefixový operátor
	const Citac operator++ (int);    // postfixový operátor

private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

const Citac& Citac::operator++()
{
	++hodnota;
	return *this;
}

const Citac Citac::operator++(int priznak)
{
	Citac pom(*this);
	++hodnota;
	return pom;
}

int main()
{
	Citac i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	i++;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	++i;
	cout << "Hodnota citace i je " << i.ZiskejHodnotu() << endl;
	Citac a = ++i;
	cout << "Hodnota a: " << a.ZiskejHodnotu();
	cout << " a hodnota i: " << i.ZiskejHodnotu() << endl;
	a = i++;
	cout << "Hodnota a: " << a.ZiskejHodnotu();
	cout << " a hodnota i: " << i.ZiskejHodnotu() << endl;
	return 0;
}
Hodnota citace i je 0
Hodnota citace i je 1
Hodnota citace i je 2
Hodnota a: 3 a hodnota i: 3
Hodnota a: 3 a hodnota i: 4
Postfixovému operátoru se předává parametr (priznak), který slouží jako signál, že se jedná právě o postfix, ale jeho hodnota se nikdy nepoužije.

Operátor sčítání

Sčítání je narozdíl od inkrementace binární operátor. Je třeba, aby bylo možné deklarovat dvě proměnné typu Citac a ty sečíst (Citac3=Citac2+Citac1).
Mohla by třeba existovat funkce Pricti(), která by si jako argument brala objekt Citac a po sečtení by vrátila tento objekt s výsledkem.
Příklad:
// Funkce Přičti

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	Citac(int uvodniHodnota);
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	Citac Pricti(const Citac &);

private:
	int hodnota;

};

Citac::Citac(int uvodniHodnota):
hodnota(uvodniHodnota)
{}

Citac::Citac():
hodnota(0)
{}

Citac Citac::Pricti(const Citac & ptr)
{
	return Citac(hodnota + ptr.ZiskejHodnotu());
}

int main()
{
	Citac citac1(2), citac2(4), citac3;
	citac3 = citac1.Pricti(citac2);
	cout << "citac1: " << citac1.ZiskejHodnotu() << endl;
	cout << "citac2: " << citac2.ZiskejHodnotu() << endl;
	cout << "citac3: " << citac3.ZiskejHodnotu() << endl;

	return 0;
}

Přetížení operátoru +

// Přetížení operátoru plus (+)

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	Citac(int uvodniHodnota);
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	Citac operator+ (const Citac &);

private:
	int hodnota;

};

Citac::Citac(int uvodniHodnota):
hodnota(uvodniHodnota)
{}

Citac::Citac():
hodnota(0)
{}

Citac Citac::operator+ (const Citac & ptr)
{
	return Citac(hodnota + ptr.ZiskejHodnotu());
}

int main()
{
	Citac citac1(2), citac2(4), citac3;
	citac3 = citac1 + citac2;
	cout << "citac1: " << citac1.ZiskejHodnotu() << endl;
	cout << "citac2: " << citac2.ZiskejHodnotu() << endl;
	cout << "citac3: " << citac3.ZiskejHodnotu() << endl;

	return 0;
}
Řádek citac3=citac2+citac1; kompilátor přeloží jako citac3=citac1.Operator+(citac2);, protože metoda operator+ se volá s operandem na levé straně operátoru a jako argument je jí předán operand na pravé straně.

Omezení při přetěžování

Nelze přetížit zabudované typy jako int, char atd. Nelze ani měnit priority a aritu operátorů. Nelze vytvářet nové operátory. Operátory jako =, [], (), > lze přetížit pouze jako členské funkce.

Operátor přiřazení

KOCKA kocka1(5,7);
KOCKA kocka2(3,4);
kocka2=kocka1;
Co když je proměnná vekKocky ukazatelem? Co se stane s původními hodnotamiv objektu kocka2?
Při povrchním kopírování se zkopírují pouze členy a oba objekty budou ukazovat na stejnou část paměti ve volném úložišti. Hloubkové kopírování potřebnou paměť alokuje.
Je tu ještě jeden problém. Objekt kocka2 už existuje a má alokovanou paměť. Tuto část paměti je třeba vymazat, pokud nemá dojít k žádnému úniku paměti.
Co se stane pokud přiřadíme objekt jemu samotnému kocka2 = kocka2;? Tuto situaci musíme prošetřit jinak by si objekt mohl vymazat svou vlastní alokovanou paměť.
Příklad:
// Konstruktory pro kopírování

#include <iostream>

using namespace std;

class KOCKA
{
public:
	KOCKA();				// výchozí konstruktor
	// konstruktor pro kopírování a destruktor zmizely!
	int ZiskejVek()		const	{ return *vekKocky; }
	int ZiskejVahu()	const	{ return *vahaKocky; }
	void NastavVek(int vek)		{ *vekKocky = vek; }
	KOCKA & operator=(const KOCKA &);

private:
	int *vekKocky;
	int *vahaKocky;
};

KOCKA::KOCKA()
{
	vekKocky = new int;
	vahaKocky = new int;
	*vekKocky = 5;
	*vahaKocky = 9;
}

KOCKA & KOCKA::operator=(const KOCKA & ptr)
{
	if (this == &ptr)
		return *this;
	*vekKocky = ptr.ZiskejVek();
	*vahaKocky = ptr.ZiskejVahu();
	return *this;
}


int main()
{
	KOCKA micka;
	cout << "Vek Micky je: " << micka.ZiskejVek() << endl;
	cout << "Nastaveni veku Micky na 6...\n";
	micka.NastavVek(6);
	KOCKA fousek;
	cout << "Vek Fouska je: " << fousek.ZiskejVek() << endl;
	cout << "Zkopirovani Micky na Fouska...\n";
	fousek = micka;
	cout << "Vek Fouska je: " << fousek.ZiskejVek() << endl;
	return 0;
}
Vek Micky je: 5
Nastaveni veku Micky na 6...
Vek Fouska je: 5
Zkopirovani Micky na Fouska...
Vek Fouska je: 6
K porovnání, jestli se nejedná o přiřazení sobě samému, slouží ukazatel this.

Konverze datových typů

Co když se pokusíme proměnnou zabudovaného typu jako například int přiřadit do objektu třídy uživatelem definované.
Příklad:
// Tento kód nelze zkompilovat!

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

int main()
{
	int promShort = 5;
	Citac citac1 = promShort;
	cout << "citac1: " << citac1.ZiskejHodnotu() << endl;
	return 0;
}
Program nelze zkompilovat, protože int nelze převést na Citac.
Příklad:
// Konstruktor jako operátor konverze

#include <iostream>

using namespace std;

class Citac
{
public:
	Citac();
	Citac(int hodnotaA);
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

Citac::Citac(int hodnotaA):
hodnota(hodnotaA)
{}


int main()
{
	int promShort = 5;
	Citac citac1 = promShort;
	cout << "citac1: " << citac1.ZiskejHodnotu() << endl;
	return 0;
}
Deklatujeme přetížený konstruktor, který přijímá hodnotu typu int. Tento konstruktor má z typu int vytvořit typ Citac.
Pokud ale přiřadíme typu int typ Citac, dojde opět k chybě.

Operátory konverze

// Operátory konverze

#include <iostream>

class Citac
{
public:
	Citac();
	Citac(int hodnotaA);
	~Citac(){}
	int ZiskejHodnotu()const { return hodnota; }
	void NastavHodnotu(int x) { hodnota = x; }
	operator unsigned short();
private:
	int hodnota;

};

Citac::Citac():
hodnota(0)
{}

Citac::Citac(int hodnotaA):
hodnota(hodnotaA)
{}

Citac::operator unsigned short ()
{
	return ( int (hodnota) );
}

int main()
{
	Citac citac1(5);
	int promShort = citac1;
	std::cout << "promShort: " << promShort << std::endl;
	return 0;
}
Operátor konverze je deklarován bez návratového typu operator unsigned short(); a vrací hodnotu proměnné hodnota převedenou na typ int.
Nyní lze vzájemně přiřazovat objekty typu int a Citac.

11 - Objektově orientovaná analýza a návrh

Vytváření modelů

Cílem modelů je vytvoření smysluplné abstrakce skutečného světa. Tato abstrakce by měla být zjednodušením reality, ale měla by ji plně odrážet.
Návrh objektově orientovaného softwaru je především o vytváření dobrých modelů. Skládá se ze dvou významných součástí: jazyka a procesu.

Softwarový návrh: Modelovací jazyk

Modelovací jazyk je nejméně důležitý aspekt objektově orientované analýzy a návrhu. Je to vlastně konvence, jak svůj model nakreslíme na papír. Jestli třídy budou obdélníky, dědišnost šipka, atd.
Aby byly diagramy jednotné, byl vytvořen jazyk UML (Unified Modeling Language)

Softwarový návrh: Vlastní proces

Proces návrhu je iterační. To znamená, že opakovaně procházíme celým procesem a snažíme se lépe pochopit různé požadavky, a vylepšujeme implementaci jednotlivých částí.
Iterační návrh začíná koncepcí - myšlenkou, co bychom mohli vytvořit. Následuje návrh... Fáze iteračního procesu jsou:
  1. Vytvoření koncepce
  2. Analýza
  3. Návrh
  4. Implementace
  5. Testování
  6. Vydání
Tyto kroky se iterují a neprobíhají sekvenčně.
Analýza je proces pochopení požadavků.
Návrh je proces vytvoření modelů tříd, z nichž vygenerujeme kód.
Implementace je jeho zápis do kódu.
Testování je kontrola správnosti.
Vydání je předání výsledků klientům.

Analýza požadavků

Cílem analýzy požadavků je jasně stanovit a zachytit požadavky. Jejím výsledkem je dokument požadavků, jehož součástí je

Analýza případů použití

Případ použití je obecný popis toho, jak se bude produkt používat. Každý systém nebo osoba, která bude s produktem pracovat je označena za herce (aktéra). Prvním krokem tedy je určit herce.
Mějme za úkol vytvořit automat výdeje hotovosti. Základní charakteristiky herců jsou - jsou pro systém externí, pracují se systémem.
Jako herce tedy navrhneme: Klient, Zaměstnanec banky, Kancelářský systém, Osoba plnící bankomat penězi.
Určíme první případy použití:
Klient zjišťuje stav účtu, Klient ukládá peníze na účet, Kliend vybírá peníze z účtu, Klient převádí peníze mezi účty, Klient otevírá účet, Klient zavírá účet.

Další aktéry a případy použití získáme zodpovězením otázek na již známé data.
Například: Jaké informace musí aktér systému předat? - Musí zadat osobní identifikační číslo > Potřebujeme případ použití pro zpracování PINu.

Jakmile máme vytvořeny případy použití, začneme sestavovat dokument požadavků s podrobným modelem domény. Model domény je dokument zachycující vše, co je známé o dané doméně. Jako jeho součást vytvoříme objekty domény, popisující všechny objekty zmíněné v případu použití.
Vztahy lze nakreslit pomocí jazyka UML. Kde třídy se kreslí jako obdelník, spojení je čarou, zobecnení tříd je značeno šipkou od konkretizované třídy k obecnější nadtřídě, obsažení - (vztah má, obsahuje něco) se značí prázdným kosoúhelníkem a šipkou k vlastněné instanci...

Vytvoření scénářů

Scénář je popis určité sady okolností, které odlišují různé prvky daního případu použití.
Je nutné vytvořit pravidla scénáře. Součástí scénáře by mělo být: Vstupní podmínky, spouště scénáře, akce herce, výsledky a změny systému, popis logického toku scénáře, ukončení scénáře, výstuoní hodnoty a podmínky.

Příklad:
Případ použití:		Klient vybírá hotovost
Scénář:			Úspěšný výběr hotovosti z běžného účtu
Vstupní podmínky:	Klient je přihlášený do systému
Spoušť:			Klient požaduje "výběr"
Popis:			Klient zvolí výbet hotovosti z běžného účtu. Na účtu je dostatek
			hotovosti, v bankomatu je dostatek hotovosti a papíru pro 
			účtenku a síť je aktivní a funguje. Bankomat požádá klienta o 
			určení množství daného výběru a klient požádá o 5000Kč, což je za
			dané situace platné množství. Přístroj vydá 5000Kč a vytiskne
			účtenku. Klient si vezme peníze a účtenku.
Výstupní podmínky:	Stav účtu klienta se sníží o 5000Kč a klient má 5000Kč v 
			hotovosti.

Dokumenty plánování

Pokud je jasné, co má systém dělat a jak se má chovat, je možné začít vytvářet dokument časového plánu a rozpočtu...

Návrh

Je proces přeměny našeho chápání požadavků na model, který lze implementovat do softwaru.
Vytvoří se třídy - z důležitých podstatných jmen v případu použití. Tedy například: Klient, hotovost, účet, běžný účet, účtenka, bankomat, síť...
Zakreslí se jejich vzájemné vztahy.
diagram
K vytvoření třídy je potřeba shrnout všechny atributy a metody třídy, které používá.

Dynamické modely

Kromě statických vztahů je možné modelovat i spolupráci objektů, stavy ... K tomu slouží sekvenční diagram, diagram spolupráce a stavový diagram.

Podrobněji o UML a modelování.

12 - Dědičnost

Co je to dědičnost?

Když řekneme, že něco je určitým druhem něčeho jiného, pak máme na mysli, že se jedná o specializaci dané věci. Dodávka je speciálním druhem auta a to je zase jistý druh vozidla.

Dědičnost a odvození

Pes je savec a automaticky přejímá všechny vlastnosti savce - pohyb, dýchání, ... Navíc koncepce psa přidává štěkání, vrtění ocasem, atd. Psy můžeme dále dělit na sportovní psy, pracovní psy... tím vzniká určitá hiearchie, v níž podřazené třídy mají vlastnosti tříd nadřazených.

Když se deklaruje třída, lze určit z jaké třídy je odvozena pokud za jejím názvem napíšeme dvojtečku, typ odvození a název třídy z níž je odvozena.
class Pes : public Savec 
Třída, te které se odvozuje, musí být deklarována dříve.
Příklad:

#include <iostream>
using namespace std;

enum RASA { ZLATY_RETRIEVER, PUDL, JEZEVCIK, OVCAK, DOBRMAN, LABRADOR };

class Savec
{
public:
    // Konstruktory
    Savec();
    ~Savec();

    // Přístupové metody
    int ZjistitVek() const;
    void ZadatVek(int);
    int ZjistitVahu() const;
    void ZadatVahu();

    // Další metody
    void Promluvit() const;
    void Spat() const;


protected:
    int jehoVek;
    int jehoVaha;
};

class Pes : public Savec
{
public:

    // Konstruktory
    Pes();
    ~Pes();

    // Přístupové metody
    RASA ZjistitRasu() const;
    void ZadatRasu(RASA);

    // Další metody
    VrtetOcasem();
    PrositOJidlo();

protected:
    RASA jehoRasa;
};

Soukromý versus chráněný

Celkem existují tři specifikátory přístupu: public (veřejný), private (soukromý), protected (chráněný).
Má-li nějaká funkce objekt třídy, pak může přistupovat ke všem veřejným členským datům a funkcím. Členské funkce zase mohou přistupovat ke všem soukromým datovým členům a funkcím své vlastní třídy a ke všem chráněným datovým členům a funkcím všech tříd, z nichž jsou odvozené.
Chráněné datové členy jsou plně viditelné odvozeným třídám, jinak jsou ale soukromé.

Příklad:
//Použití odvozeného objektu

#include <iostream>
using std::cout;

enum RASA { ZLATY_RETRIEVER, PUDL, JEZEVCIK, OVCAK, DOBRMAN, LABRADOR };

class Savec
{
public:
    // Konstruktory
    Savec():jehoVek(2), jehoVaha(5){}
    ~Savec(){}

    // Přístupové metody
    int ZjistitVek() const { return jehoVek; }
    void ZadatVek(int vek) { jehoVek = vek; }
    int ZjistitVahu() const { return jehoVaha; }
    void ZadatVahu(int vaha) { jehoVaha = vaha; }

    // Další metody
    void Promluvit()const { cout << "Zvuk savce!\n"; }
    void Spat()const { cout << "Psssst. Prave spim.\n"; }


protected:
    int jehoVek;
    int jehoVaha;
};

class Pes : public Savec
{
public:

    // Konstruktory
    Pes():jehoRasa(ZLATY_RETRIEVER){}
    ~Pes(){}

    // Přístupové metody
    RASA ZjistitRasu() const { return jehoRasa; }
    void ZadatRasu(RASA rasa) { jehoRasa = rasa; }

    // Další metody
    void VrtetOcasem() const { cout << "Vrti ocasem...\n"; }
    void PrositOJidlo() const { cout << "Prosi o jidlo...\n"; }

private:
    RASA jehoRasa;
};

int main()
{
    Pes fido;
    fido.Promluvit();
    fido.VrtetOcasem();
    cout << "Fidovi je " << fido.ZjistitVek() << " let\n";
    return 0;
}
Zvuk savce!
Vrti ocasem...
Fidovi je 2 let

Konstruktory a destruktory

Když se vytváří Fido, nejprve se zavolá jeho bázový konstruktor, tedy vytvoří se Savec. Potom je zavolán konstruktor Pes. Fido se skládá ze dvou částí - ze savce a ze psa, a proto neexistuje, dokud není složen.
Při destrukci objektu se nejdříve volá destruktor Pes a poté Savec.
Příklad:
//Volání konstruktorů a destruktorů

#include <iostream>
enum RASA { ZLATY_RETRIEVER, PUDL, JEZEVCIK, OVCAK, DOBRMAN, LABRADOR };

class Savec
{
public:
    // Konstruktory
    Savec();
    ~Savec();

    // Přístupové metody
    int ZjistitVek() const { return jehoVek; }
    void ZadatVek(int vek) { jehoVek = vek; }
    int ZjistitVahu() const { return jehoVaha; }
    void ZadatVahu(int vaha) { jehoVaha = vaha; }

    // Další metody
    void Promluvit() const { std::cout << "Zvuk savce!\n"; }
    void Spat() const { std::cout << "Psssst. Prave spim.\n"; }


protected:
    int jehoVek;
    int jehoVaha;
};

class Pes : public Savec
{
public:

    // Konstruktory
    Pes();
    ~Pes();

    // Přístupové metody
    RASA ZjistitRasu() const { return jehoRasa; }
    void ZadatRasu(RASA rasa) { jehoRasa = rasa; }

    // Další metody
    void VrtetOcasem() const { std::cout << "Vrti ocasem...\n"; }
    void PrositOJidlo() const { std::cout << "Prosi o jidlo...\n"; }

private:
    RASA jehoRasa;
};

Savec::Savec():
jehoVek(1),
jehoVaha(5)
{
    std::cout << "Konstruktor Savec...\n";
}

Savec::~Savec()
{
    std::cout << "Destruktor Savec...\n";
}

Pes::Pes():
jehoRasa(ZLATY_RETRIEVER)
{
    std::cout << "Konstruktor Pes...\n";
}

Pes::~Pes()
{
    std::cout << "Destruktor Pes...\n";
}
int main()
{
    Pes fido;
    fido.Promluvit();
    fido.VrtetOcasem();
    std::cout << "Fidovi je " << fido.ZjistitVek() << " let\n";
    return 0;
}
Konstruktor Savec...
Konstruktor Pes...
Zvuk savce!
Vrti ocasem...
Fidovi je 1 let
Destruktor Pes...
Destruktor Savec...

Předávání argumentů bázovým konstruktorům

Pokud bychom chtěli přetížit bázový konstruktor třídy Savec tak, aby přebíral věk a konstruktor třídy Pes, aby přebíral nějakou rasu, musíme zajistit předání parametrů správnému konstruktoru.
Inicializaci bázové třídy lze vykonat během inicializace třídy zápisem názvu bázové třídy a následně parametrů, které daná bázová třída očekává.
Příklad:
//Přetěžování konstruktorů v odvozených třídách

#include <iostream>
using namespace std;

enum RASA { ZLATY_RETRIEVER, PUDL, JEZEVCIK, OVCAK, DOBRMAN, LABRADOR };

class Savec
{
public:
    // Konstruktory
    Savec();
    Savec(int vek);
    ~Savec();

    // Přístupové metody
    int ZjistitVek() const { return jehoVek; }
    void ZadatVek(int vek) { jehoVek = vek; }
    int ZjistitVahu() const { return jehoVaha; }
    void ZadatVahu(int vaha) { jehoVaha = vaha; }

    // Další metody
    void Promluvit() const { cout << "Zvuk savce!\n"; }
    void Spat() const { cout << "Psssst. Prave spim.\n"; }


protected:
    int jehoVek;
    int jehoVaha;
};

class Pes : public Savec
{
public:

    // Konstruktory
    Pes();
    Pes(int vek);
    Pes(int vek, int vaha);
    Pes(int vek, RASA rasa);
    Pes(int vek, int vaha, RASA rasa);
    ~Pes();

    // Přístupové metody
    RASA ZjistitRasu() const { return jehoRasa; }
    void ZadatRasu(RASA rasa) { jehoRasa = rasa; }

    // Další metody
    void VrtetOcasem() const { cout << "Vrti ocasem...\n"; }
    void PrositOJidlo() const { cout << "Prosi o jidlo...\n"; }

private:
    RASA jehoRasa;
};

Savec::Savec():
jehoVek(1),
jehoVaha(5)
{
    cout << "Konstruktor Savec...\n";
}

Savec::Savec(int vek):
jehoVek(vek),
jehoVaha(5)
{
    cout << "Konstruktor Savec(int)...\n";
}

Savec::~Savec()
{
    cout << "Destruktor Savec...\n";
}

Pes::Pes():
Savec(),
jehoRasa(ZLATY_RETRIEVER)
{
    cout << "Konstruktor Pes...\n";
}

Pes::Pes(int vek):
Savec(vek),
jehoRasa(ZLATY_RETRIEVER)
{
    cout << "Konstruktor Pes(int)...\n";
}

Pes::Pes(int vek, int vaha):
Savec(vek),
jehoRasa(ZLATY_RETRIEVER)
{
    jehoVaha = vaha;
    cout << "Konstruktor Pes(int, int)...\n";
}

Pes::Pes(int vek, int vaha, RASA rasa):
Savec(vek),
jehoRasa(rasa)
{
    jehoVaha = vaha;
    cout << "Konstruktor Pes(int, int, RASA)...\n";
}

Pes::Pes(int vek, RASA rasa):
Savec(vek),
jehoRasa(rasa)
{
    cout << "Konstruktor Pes(int, RASA)...\n";
}

Pes::~Pes()
{
    cout << "Destruktor Pes...\n";
}
int main()
{
    Pes fido;
    Pes punta(5);
    Pes bastr(6,8);
    Pes lassie(3,ZLATY_RETRIEVER);
    Pes hasky(4,20,DOBRMAN);
    fido.Promluvit();
    punta.VrtetOcasem();
    cout << "Lassie je " << lassie.ZjistitVek() << " let\n";
    cout << "Hasky vazi ";
    cout << hasky.ZjistitVahu() << " kilogramu\n";
    return 0;
}

Překrývání funkcí

Třída Pes má přístup ke všem metodám bázové třídy Savec. Překrytí funkce znamená změnu implementace funkce v bázové třídě. Pokud chceme překrýt funkci, musí mít stejný název a seznam parametrů. Návratové typy se mohou lišit. Funkce mají stejný podpis! Narozdíl od přetěžování, kde mají funkce různé podpisy!
V příkladu překryjeme funkci Promluvit()
Příklad:
//Překrytí metody bázové třídy v odvozené třídě

#include <iostream>
using std::cout;

enum RASA { ZLATY_RETRIEVER, PUDL, JEZEVCIK, OVCAK, DOBRMAN, LABRADOR };

class Savec
{
public:
    // Konstruktory
    Savec() { cout << "Konstruktor Savec...\n"; }
    ~Savec() { cout << "Destruktor Savec...\n"; }

    // Další metody
    void Promluvit() const { cout << "Zvuk savce!\n"; }
    void Spat() const { cout << "Psssst. Prave spim.\n"; }


protected:
    int jehoVek;
    int jehoVaha;
};

class Pes : public Savec
{
public:

    // Konstruktory
    Pes(){ cout << "Konstruktor Pes...\n"; }
    ~Pes(){ cout << "Destruktor Pes...\n"; }

    // Další metody
    void VrtetOcasem() const  { cout << "Vrti ocasem...\n"; }
    void PrositOJidlo() const  { cout << "Prosi o jidlo...\n"; }
    void Promluvit() const { cout << "Haf!\n"; }

private:
    RASA jehoRasa;
};

int main()
{
    Savec velkeZvire;
    Pes fido;
    velkeZvire.Promluvit();
    fido.Promluvit();
    return 0;
}
Konstruktor Savec...
Konstruktor Savec...
Konstruktor Pes...
Zvuk savce!
Haf!
Destruktor Pes...
Destruktor Savec...
Destruktor Savec...

Skrytí metody bázové třídy

Pokud překryjeme metodu Promluvit() bázové třídy Savec, pak ji skryjeme metodou Promluvit() třídy Pes.
Pokud by bázová třída obsahovala přetížené funkce a byla by překryta jen jedna z nich, pak se automaticky skryjí všechny metody se stejným názvem a je obtížné k nim přistupovat.
Příklad:
//Skrývání metod

#include <iostream>


class Savec
{
public:
    void Pohnout() const { std::cout << "Savec se posune o krok\n"; }
    void Pohnout(int vzdalenost) const
    {
        std::cout << "Savec se posune o ";
        std::cout << vzdalenost <<" kroku.\n";
    }
protected:
    int jehoVek;
    int jehoVaha;
};

class Pes : public Savec
{
public:
    // Můžete obdržet varování, že skrýváte nějakou funkci!
    void Pohnout() const { std::cout << "Pes se posune o 5 kroku.\n"; }
};

int main()
{
    Savec velkeZvire;
    Pes fido;
    velkeZvire.Pohnout();
    velkeZvire.Pohnout(2);
    fido.Pohnout();
    // fido.Pohnout(10);
    return 0;
}
Pokud chceme používat metodu Pohnout(int vzdalenost) const musíme ji také překrýt, protože překrytím metody bez parametru byla tato metoda skryta a nelze použít. Proto je řádek vykomentován - způsobil by chybu kompilace.

Volání bázové metody

Pokud je bázová funkce překryta, lze ji zavolat její plnou kvalifikací. Zápisem bázového názvu, dvou dvojteček a názvu metody:
fido.Savec::Pohnout(10);

Příklad:
//Volání bázové metody z překryté metody

#include <iostream>
using namespace std;

class Savec
{
public:
    void Pohnout() const { cout << "Savec se posune o krok\n"; }
    void Pohnout(int vzdalenost) const
    {
        cout << "Savec se posune o " << vzdalenost;
        cout << " kroku.\n";
    }

protected:
    int jehoVek;
    int jehoVaha;
};

class Pes : public Savec
{
public:
    void Pohnout() const;

};

void Pes::Pohnout() const
{
    cout << "V pohybu psa...\n";
    Savec::Pohnout(3);
}

int main()
{
    Savec velkeZvire;
    Pes fido;
    velkeZvire.Pohnout(2);
    fido.Savec::Pohnout(6);
    return 0;
}
Savec se posune o 2 kroku.
Savec se posune o 6 kroku.

Virtuální metody

//Použití virtuálních metod

#include <iostream>
using std::cout;

class Savec
{
public:
    Savec():jehoVek(1) { cout << "Konstruktor Savec...\n"; }
    virtual ~Savec() { cout << "Destruktor Savec...\n"; }
    void Pohnout() const { cout << "Savec se posune o krok\n"; }
    virtual void Promluvit() const { cout << "Savec hovori!\n"; }
protected:
    int jehoVek;

};

class Pes : public Savec
{
public:
    Pes() { cout << "Konstruktor Pes...\n"; }
    virtual ~Pes() { cout << "Destruktor Pes...\n"; }
    void VrtetOcasem() { cout << "Vrti ocasem...\n"; }
    void Promluvit()const { cout << "Haf!\n"; }
    void Pohnout()const { cout << "Pes se posune o 5 kroku...\n"; }
};

int main()
{

    Savec *pPes = new Pes;
    pPes->Pohnout();
    pPes->Promluvit();

    return 0;
}
Konstruktor Savec...
Konstruktor Pes...
Savec se posune o krok.
Haf!
Deklarování metody jako virtuální značí, že metoda bude překryta jinou metodou ve vlastní třídě.
Byl vytvořen ukazatel na Savec nazvaný pPes, jemuž se přiřadila adresa objektu Pes, což je platné přiřazení, protože pes je savec.
Při volání metody pPes->Pohnout() je vyhledána metoda ve třídě Savec. Stejně jako u volání metody Promluvit(), ale protože promluvit je virtuální, je nahrazena metodou promluvit ze třídy Pes.

Příklad:
//Více postupně volaných virtuálních funkcí

#include <iostream>
using namespace std;

class Savec
{
public:
    Savec():jehoVek(1) {  }
    virtual ~Savec() { }
    virtual void Promluvit() const { cout << "Savec hovori!\n"; }
protected:
    int jehoVek;
};

class Pes : public Savec
{
public:
    void Promluvit()const { cout << "Haf!\n"; }
};


class Kocka : public Savec
{
public:
    void Promluvit()const { cout << "Mnau!\n"; }
};


class Kun : public Savec
{
public:
    void Promluvit()const { cout << "Iihaha!\n"; }
};

class Vepr : public Savec
{
public:
    void Promluvit()const { cout << "Chrocht!\n"; }
};

int main()
{
    Savec* toPole[5];
    Savec* ukz;
    int volba, i;
    for ( i = 0; i<5; i++)
    {
        cout << "(1)pes (2)kocka (3)kun (4)vepr: ";
        cin >> volba;
        switch (volba)
        {
        case 1: ukz = new Pes;
                break;
        case 2: ukz = new Kocka;
                break;
        case 3: ukz = new Kun;
                break;
        case 4: ukz = new Vepr;
                break;
        default: ukz = new Savec;
                break;
        }
        toPole[i] = ukz;
    }
    for (i=0;i<5;i++)
        toPole[i]->Promluvit();
    return 0;
}
(1)pes (2)kocka (3)kun (4)vepr: 1
(1)pes (2)kocka (3)kun (4)vepr: 2
(1)pes (2)kocka (3)kun (4)vepr: 3
(1)pes (2)kocka (3)kun (4)vepr: 4
(1)pes (2)kocka (3)kun (4)vepr: 5
Haf!
Mnau!
Iihaha!
Chrocht!
Savec hovori!
Během kompilace není možné určit, které metody se budou volat. Dochází k dynamickému vázání za běhu programu.

Pozn.: Je možné označovat virtuální metody i v odvozených třídách, ale není to nutné.

Jak fungují virtuální funkce

Když jsou deklarovány virtuální funkce, musí je kompilátor sledovat. Vytvoří si v-tabulku v níž má uloženy ukazatele na správné virtuální metody. Díky těmto tabulkám jsou pokaždé zavolány správné metody.

Omezování

Virtuální funkce lze použít jen u ukazatelů a odkazů. Předání objektu hodnotou znemožní použití virtuálních funkcí.
Příklad:
//Data slistd:std::cing s předáváním hodnotou

#include <iostream>

class Savec
{
public:
    Savec():jehoVek(1) {  }
    virtual ~Savec() { }
    virtual void Promluvit() const { std::cout << "Savec hovori!\n"; }
protected:
    int jehoVek;
};

class Pes : public Savec
{
public:
    void Promluvit()const { std::cout << "Haf!\n"; }
};

class Kocka : public Savec
{
public:
    void Promluvit()const { std::cout << "Mnau!\n"; }
};

void HodnotovaFunkce (Savec);
void UkazatelovaFunkce (Savec*);
void OdkazovaFunkce (Savec&);
int main()
{
    Savec* ukz=0;
    int volba;
    while (1)
    {
        bool fKonec = false;
        std::cout << "(1)pes (2)kocka (0)Konec: ";
        std::cin >> volba;
        switch (volba)
        {
        case 0: fKonec = true;
                break;
        case 1: ukz = new Pes;
                break;
        case 2: ukz = new Kocka;
                break;
        default: ukz = new Savec;
                break;
        }
        if (fKonec)
            break;
        UkazatelovaFunkce(ukz);
        OdkazovaFunkce(*ukz);
        HodnotovaFunkce(*ukz);
    }
    return 0;
}

void HodnotovaFunkce (Savec HodnotaSavec)
{
    HodnotaSavec.Promluvit();
}

void UkazatelovaFunkce (Savec * pSavec)
{
    pSavec->Promluvit();
}

void OdkazovaFunkce (Savec & rSavec)
{
    rSavec.Promluvit();
}
(1)pes (2)kocka (0)konec: 1
Haf!
Haf!
Savec hovori!
(1)pes (2)kocka (0)konec: 2
Mnau!
Mnau!
Savec hovori!
(1)pes (2)kocka (0)konec: 0

Virtuální destruktory

Pokud jsou ve třídě nějaké virtuální funkce, pak musí být virtuální také destruktor!

Virtuální kopírovací konstruktory

Konstruktory nemohou být virtuální. V určitých situacích je nutné předat ukazatel na bázový objekt a získat kopii správného odvozeného objektu. Řešením je vytvoření metodu Klonovat() v bázové třídě a učinit ji virtuální. Metoda Klonovat() vytváří novou kopii objektu aktuální třídy a vrací tento objekt.
Protože každá odvozená třída překrývá metodu Klonovat(), dojde k vytvoření kopie dané odvozené třídy.
Příklad:
//Virtuální kopírovací konstruktor

#include <iostream>
using namespace std;

class Savec
{
public:
    Savec():jehoVek(1) { cout << "Konstruktor Savec...\n"; }
    virtual ~Savec() { cout << "Destruktor Savec...\n"; }
    Savec (const Savec & rhs);
    virtual void Promluvit() const { cout << "Savec hovori!\n"; }
    virtual Savec* Klonovat() { return new Savec(*this); }
    int ZjistitVek()const { return jehoVek; }
protected:
    int jehoVek;
};

Savec::Savec (const Savec & rhs):jehoVek(rhs.ZjistitVek())
{
    cout << "Kopirovaci konstruktor Savec...\n";
}

class Pes : public Savec
{
public:
    Pes() { cout << "Konstruktor Pes...\n"; }
    virtual ~Pes() { cout << "Destruktor Pes...\n"; }
    Pes (const Pes & rhs);
    void Promluvit()const { cout << "Haf!\n"; }
    virtual Savec* Klonovat() { return new Pes(*this); }
};

Pes::Pes(const Pes & rhs):
Savec(rhs)
{
    cout << "Kopirovaci konstruktor Pes...\n";
}

class Kocka : public Savec
{
public:
    Kocka() { cout << "Konstruktor Kocka...\n"; }
    ~Kocka() { cout << "Destruktor Kocka...\n"; }
    Kocka (const Kocka &);
    void Promluvit()const { cout << "Mnau!\n"; }
    virtual Savec* Klonovat() { return new Kocka(*this); }
};

Kocka::Kocka(const Kocka & rhs):
Savec(rhs)
{
    cout << "Kopirovaci konstruktor Kocka...\n";
}

enum ZVIRATA { SAVEC, PES, KOCKA};
const int pocetTypuZvirat = 3;
int main()
{
    Savec *toPole[pocetTypuZvirat];
    Savec* ukz;
    int volba, i;
    for ( i = 0; i<pocetTypuZvirat; i++)
    {
        cout << "(1)pes (2)kocka (3)Savec: ";
        cin >> volba;
        switch (volba)
        {
        case PES:    ukz = new Pes;
                    break;
        case KOCKA:    ukz = new Kocka;
                    break;
        default:    ukz = new Savec;
                    break;
        }
        toPole[i] = ukz;
    }
    Savec *JinePole[pocetTypuZvirat];
    for (i=0;i<pocetTypuZvirat;i++)
    {
        toPole[i]->Promluvit();
        JinePole[i] = toPole[i]->Klonovat();
    }
    for (i=0;i<pocetTypuZvirat;i++)
        JinePole[i]->Promluvit();
    return 0;
}
(1)pes (2)kocka (3)savec: 1
Konstruktor Savec...
Konstruktor Pes...
(1)pes (2)kocka (3)savec: 2
Konstruktor Savec...
Konstruktor Kocka...
(1)pes (2)kocka (3)savec: 3
Konstruktor  Savec...
Haf!
Kopirovaci konstruktor Savec...
Kopirovaci konstruktor Pes...
Mnau!
Kopirovaci konstruktor Savec...
Kopirovaci konstruktor Kocka...
Savec hovori!
Kopirovaci konstruktor Savec...
Haf!
Mnau!
Savec hovori!
Metoda Savec::Klonovat() vrací ukazatel na nový objekt Savec voláním kopírovacího konstruktoru, přičemž objekt předává sáme sebe jako konstantní odkaz.

Náklady na virtuální metody

S používáním virtuálních metod je spojeno vytváření v-tabulky. Jakmile učiníme jednu metodu virtuální, pak už jsme zaplatili většinu nákladů spojených s v-tabulkou. A musíme učinit virtuální destruktory a je vhodné, aby i ostatní metody byly virtuální.
Pokud tedy vytvoříme malou třídu, a neočekáváme, že z ní bude nějaká třída odvozena, pak je zbytečné používat virtuální metody!

13 - Pole a propojené seznamy

Pole

Pole je kolekce míst v úložišti dat, z nichž každé obsahuje stený typ dat. Každé místo označujeme za prvek (element) pole.
Pole se deklaruje zadáním typu, názvu a subskriptu. Subskript = index - počet prvků pole uzavřený v hranatých závorkách.
Příklad:
long PoleDlouhych[25];
deklaruje pole dlouhých celých čísel o velikosti 25 prvků.

Prvky pole

K jednotlivým prvkům se přistpuje prostřednictvím zadání odsazení (indexu) od názvu pole. Prvky se indexují od nuly. Prvním prvkem pole je tedy PoleDlouhych[0] a posledním prvkem je PoleDlouhych[24]
Příklad:
#include <iostream>

int main()
{
	int mojePole[5];
	int i;
	for ( i=0; i<5; i++)  // 0-4
	{
		std::cout <<"Hodnota mojePole[" << i <<"]: ";
		std::cin >> mojePole[i];
	}
	for (i = 0; i<5; i++)
		std::cout << i <<": " << mojePole[i] <<"\n";
	return 0;
}

Zápis za konec pole

Pokud se zapisuje za konec pole, např. PoleDlouhych[100], kompilátor spočítá podle velikosti prvků jak daleko za první prvek má data zapsat a přepíše jakoukoliv hodnotu v dané části paměti.
Takovýto zápis může mít nepředvídatelné následky!!!

Příklad: Zápis za konec pole - SPUŠTĚNÍ MŮŽE ZPŮSOBIT ZHROUCENÍ SYSTÉMU
// Ukazuje, co se stane při zápisu za konec pole

#include <iostream>
using namespace std;

int main()
{
	// hlídky
	long hlidkaJedna[3];
	long CilovePole[25]; // naplňované pole
	long hlidkaDve[3];
	int i;
	for (i=0; i < 3; i++)
		hlidkaJedna[i] = hlidkaDve[i] = 0;

	for (i=0; i<25; i++)
		CilovePole[i] = 0;

	cout <<"Test 1: \n";  // testování aktuálních hodnot (měly by být 0)
	cout <<"CilovePole[0]: " << CilovePole[0] <<"\n";
	cout <<"CilovePole[24]: " << CilovePole[24] <<"\n\n";

	for (i = 0; i<3; i++)
	{
		cout <<"hlidkaJedna[" << i <<"]: ";
		cout << hlidkaJedna[i] <<"\n";
		cout <<"hlidkaDve[" << i <<"]: ";
		cout << hlidkaDve[i]<<"\n";
	}

	cout <<"\nPrirazuji...";
	for (i = 0; i<=25; i++)
		CilovePole[i] = 20;

	cout <<"\nTest 2: \n";
	cout <<"CilovePole[0]: " << CilovePole[0] <<"\n";
	cout <<"CilovePole[24]: " << CilovePole[24] <<"\n";
	cout <<"CilovePole[25]: " << CilovePole[25] <<"\n\n";
	for (i = 0; i<3; i++)
	{
		cout <<"hlidkaJedna[" << i <<"]: ";
		cout << hlidkaJedna[i]<<"\n";
		cout <<"hlidkaDve[" << i <<"]: ";
		cout << hlidkaDve[i]<<"\n";
	}

	return 0;
}
Chyba zápisu za konec pole je tak obvyklá, že má své vlastní jméno. Označuje se jako chyba sloupků u plotu. Jedná se o problém výpočtu, kolik sloupků potřebujeme na 10ti metrový plot, když na jeden metr je potřeba jeden sloupek. Je potřeba 11 sloupků.

Inicializování polí

Pole lze inicializovat, již při první deklaraci, za název zapíšeme rovnítko a ve složených závorkách se uvede seznam hodnot, oddělených čárkami.
int PoleCelych[5]={10,20,30,40,50};
Pokud chceme zjistit velikost pole, lze ji vypočítat:
const unsigned shor int DelkaPole=sizeof(PoleCelych)/sizeof(PoleCelych[0]];

Deklarovaní polí

Pole mohou mít libovolný platný název, nemohou ale mít stejný název jako jiná proměnná či pole v jejich oboru platnosti. Nelze tedy současně používat pole MojeKocky[5] a proměnnou MojeKocky.

Velikost pole lze dimenzovat konstantou nebo výčtem.
Příklad:
// Dimenzování polí konstantami a výčty

#include <iostream>
int main()
{
	enum DnyTydne { Ned, Pon, Ute,
					Str, Crt, Pat, Sob, DnyVTydnu };
	int PoleTydne[DnyVTydnu] = { 10, 20, 30, 40, 50, 60, 70 };

	std::cout << "Hodnota v utery je: " << PoleTydne[Ute];
	return 0;
}
Hodnota v utery je: 30
POLE
Pro deklaraci pole napíšeme typ uloženého objektu, název pole a index s počtem objektů obsažených v daném poli.
Příklad 1:
int MojeCelociselnePole[90];
Příklad2:
long * PoleUkazateluNaDlouhaCisla[100];
Pro přístup k členům pole, slouží operátor indexu.
Příklad 1:
int devateCeleCislo=MojeCelociselnePole[8];
Příklad 2:
long * plong = PoleUkazateluNaDlouhaCisla[8];
Pole se počítají od nuly. Pole s počtem n položek se čísluje od 0 do n-1.

Pole Objektů

Do pole lze uložit libovolný objekt. Při deklaraci pole sdělíme kompilátoru typ objektu a počet objektů, pro které si má vyhradit místo.
Z deklarace dané třídy si kompilátor zjistí, kolik prostoru je zapotřebí pro jednotlivé objekty. Třída musí mít nějaký výchozí konstruktor bez argumentů, aby mohly být vytvořeny příslušné objekty při definování pole.
Přístup ke členským datům v poli objektů je proces se dvěma kroky: člen pole se určí pomocí operátoru indexu [] a navíc se přidá operátor členu (.) při přistupu k určíté členské proměnné.
Příklad:
// Pole objektů

#include <iostream>
using namespace std;

class KOCKA
{
public:
	KOCKA() { jejiVek = 1; jejiVaha=5; }
	~KOCKA() {}
	int ZjistitVek() const { return jejiVek; }
	int ZjistitVahu() const { return jejiVaha; }
	void ZadatVek(int vek) { jejiVek = vek; }

private:
	int jejiVek;
	int jejiVaha;
};

int main()
{
	KOCKA Kosik[5];
	int i;
	for (i = 0; i < 5; i++)
	Kosik[i].ZadatVek(2*i +1);

	for (i = 0; i < 5; i++)
	{
		cout << "Kocka cislo " << i+1<< ": ";
		cout << Kosik[i].ZjistitVek() << endl;
	}
	return 0;
}
Kocka cislo 1: 1
Kocka cislo 2: 3
Kocka cislo 3: 5
Kocka cislo 4: 7
Kocka cislo 5: 9
Analýza: Třída KOCKA musí mít výchozí konstruktor. Jestliže vytvoříme jiný konstruktor, pak kompilátor výchozí konstruktor nevytvoří, ale musíme jej udělat sami!!!

Vícerozměrná pole

Je možné mít pole více rozměrů. Každý rozměr je představován jako určitý subskript v poli. Dvojrozměrné pole má 2 indexy, trojrozměrné 3 indexy atd. . Příkladem dvojrozměrného pole je šachovnice.
CTVEREC Sachovnice[8][8];
deklaruje dvojrozměrné pole čtverců (třída CTVEREC musí být vytvořena).
Nebo lze šachovnici deklarovat jako pole 64 čtverců:
CTVEREC Sachovnice[64];
V tomto poli si ale težko představíme rozestavění figur. Král na začátku hry stojí na čtvrtém poli v první řadě, tedy:
Sachovnice[0][3];
pokud první index vyjadřuje řádek a druhý sloupec.

Inicializování vícerozměrných polí

Mějme pole:
int toPole[5][3];
Pak pokud chceme inicializovat pole hodnotami, tak se první tři hodnoty uloží do toPole[0], další tři do toPole[1] atd.
int toPole[5][3]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
Pro zřetelnost lze seskupit inicializace složenými závorkami:
int toPole[5][3]={ {1,2,3},
                   {4,5,5},
                   {7,8,9},
                   {10,11,12},
                   {13,14,15} };
Kompilátor vnitří závorky ignoruje. Pouze zlepšují přehlednost zápisu. Všechny prvky pole musí být odděleny čárkami, bez ohledu na vnitřní závorky!!
Celá inicializace musí končit středníkem.

Příklad: Dvojrozměrné pole, první rozměr je množina čísel 0..4, druhý rozměr je tvořen dvojnásobkem jednotlivých hodnot v prvním rozměru.
// Vytváření vícerozměrného pole

#include <iostream>
using namespace std;

int main()
{
	int nejakePole[5][2] = { {0,0}, {1,2}, {2,4}, {3,6}, {4,8}};
	for (int i = 0; i<5; i++)
		for (int j=0; j<2; j++)
		{
			cout << "nejakePole[" << i << "][" << j << "]: ";
			cout << nejakePole[i][j]<< endl;
		}

	return 0;
}
nejakePole[0][0]: 0
nejakePole[0][1]: 0
nejakePole[1][0]: 1
nejakePole[1][1]: 2
nejakePole[2][0]: 2
nejakePole[2][1]: 4
nejakePole[3][0]: 3
nejakePole[3][1]: 6
nejakePole[4][0]: 4
nejakePole[4][1]: 8

POLE Ukazatelů

Prozatím popisovaná pole ukládají své členy do zásobníku. Paměť zásobníku je ale oproti volnému úložišti výrazně menší.
Je možné deklarovat objekty ve volném úložišti a do pole pak uložit pouze ukazatel na příslušný objekt. Tím se výrazně omezí množství spotřebovávané paměti zásobníku.

Příklad:
// Pole ukazatelů na objekty

#include <iostream>
using namespace std;

class KOCKA
{
public:
	KOCKA() { jejiVek = 1; jejiVaha=5; }
	~KOCKA() {}                                 // Destruktor
	int ZjistitVek() const { return jejiVek; }
	int ZjistitVahu() const { return jejiVaha; }
	void ZadatVek(int vek) { jejiVek = vek; }

private:
	int jejiVek;
	int jejiVaha;
};

int main()
{
	KOCKA * Rodina[500];
	int i;
	KOCKA * pKocka;
	for (i = 0; i < 500; i++)
	{
		pKocka = new KOCKA;
		pKocka->ZadatVek(2*i +1);
		Rodina[i] = pKocka;
	}

	for (i = 0; i < 500; i++)
	{
		cout << "Kocka cislo " << i+1 << ": ";
		cout << Rodina[i]->ZjistitVek() << endl;
	}
	return 0;
}
Kocka cislo 1: 1
Kocka cislo 2: 3
Kocka cislo 3: 5
Kocka cislo 5: 7
...
Kocka cislo 498: 995
Kocka cislo 499: 997
Kocka cislo 500: 999
Analýza: Deklarujeme pole Rodina, které obsahuje 500 ukazatelů na objekty KOCKA.
V první smyčce se vytvoří 500 nových objektů KOCKA ve volném úložišti a věk každé kočky se nastaví na dvojnásobek indexu plus 1.
Nakonec se příslušný ukazatel přidá do pole. !! Do pole se vkládá přímo ukazatel a ne zrušený odkaz hodnoty!!
Druhá smyčka prochází pole a z uložených adres na indexech zjišťuje věk dané kočky.


V tomto příkladu jsou pole Rodina a všechny ukazatele uloženy v zásobníku. Všech 500 vytvořených koček se nachází ve volném úložišti. Výsledkem je ukazatel na oblas volného úložiště, ide se pole nachází. Např.:
KOCKA *Rodina = new KOCKA[500];
deklaruje Rodina jako ukazatel na první polozku v poli 500 koček.
Jinými slovy, Rodina ukazuje na Rodina[0].
Výhodou použití Rodia tímto způsobem je to, že pro přístup k jednotlivým členům rodiny můžete používat aritmetiku ukazatelů.
Lze například zapsat:
KOCKA *Rodina = new KOCKA[500];
KOCKA *pKocka=Rodina;        //pKocka ukazuje na Rodina[0]
pKocka->ZadatVek(10);=       //nastavit Rodina[0] na 10
pKocka++;                    //postoupit na Rodina[1]
pKocka->ZadatVek(20);        //nastavit Rodina[1] na 20

UKAZATEL NA POLE VS POLE UKAZATELů

Mějme deklarace:
1: KOCKA RodinaJedna[500];
2: KOCKA * RodinaDve[500];
3: KOCKA * RodinaTri=new KOCKA[500];
RodinaJedna je pole 500 kocek.
RodinaDve je pole 500 ukazatelů na kočky.
RodinaTri je ukazatel na pole 500 koček.
RodinaTri je určitou variantou RodinaJedna, ale je velmi odlišná od RodinaDve.

Jak souvisí pole s ukazateli? Ve třetím případě je RodinaTri ukazatelem na pole. To znamená, že adresa RodinaTri je adresou první položky v tomto poli. To je přesně případ RodinaJedna.

UKAZATELE a názvy polí

V jazyce C++ je pole konstantním ukazatelem na první prvek pole. Proto v deklaraci
KOCKA Rodina[50];
je Rodina ukazatel na &Rodina[0], což je adresa prvního prvku pole Rodina. Je povoleno pracovat s názvy polí jako s konstantními ukazateli a naopak. Proto je Rodina + 4 platným způsobem přistupu k datům v Rodina[4].
O aritmetiku sčítání a odčítání se stará kompilátor. Neposune se o 4B od začátku pole, ale o 4*velikost objektu v poli od začátku.
Příklad:
// Pole na volném úložišti

#include <iostream>

class KOCKA
{
public:
	KOCKA() { jejiVek = 1; jejiVaha=5; }
	~KOCKA();
	int ZjistitVek() const { return jejiVek; }
	int ZjistitVahu() const { return jejiVaha; }
	void ZadatVek(int vek) { jejiVek = vek; }

private:
	int jejiVek;
	int jejiVaha;
};

KOCKA :: ~KOCKA()
{
	// cout << "Zavolán destruktor!\n";
}

int main()
{
	KOCKA * Rodina = new KOCKA[500];
	int i;

	for (i = 0; i < 500; i++)
	{
		Rodina[i].ZadatVek(2*i +1);
	}

	for (i = 0; i < 500; i++)
	{
		std::cout << "Kocka cislo " << i+1 << ": ";
		std::cout << Rodina[i].ZjistitVek() << std::endl;
	}

	delete [] Rodina;

	return 0;
}
Analýza: Celé  pole se vytvoří  ve volném úložišti  pomocí volání 
new KOCKA[500].

ODSTRAŇOVÁNÍ polí na volném úložišti

Pro odstranění pole slouží operátor delete[], který automaticky vrátí veškerou pamět, vyhrazenou poli.
!! NESMÍ BÝT OPOMENUTY HRANATÉ ZÁVORKY!!

Stejně jako platí, že vytváříme-li novou položku pomocí new, pak ji rušíme pomocí delete, platí také, že vytváříme-li pole pomocí new [velikost], tak jej odstraníme pomocí delete[].
Hranaté závorky signalizují kompilátoru, že se vymazává pole.
Pokud vynecháme hranaté závorky, dojde k odstranění jen prvního prvku a dojde k paměťovému úniku!

POLE Znaků

Řetězec ve stylu jazyka C je pole znaků ukončený hodnotou null.
Příklad nepojmenované řetězcové konstanty:
cout << "Ahoj, svete.\n";
Deklarovat a inicializovat řetězec C můžeme stejným způsobem jako kterékoliv jiné pole.
Příklad:
char    Pozdrav[]=['A',    'h',    'o',    'j',    ',',    '   ', 's','v','e','t','e','.','\0'];
Poslední znak je znak null, který mnoho funkcí jazyka C++ rozpoznává jako ukončovač řetězce ve stylu jazyka C.
Lze použít i zkratkovou formu zápisu:
char Pozdrav[]="Ahoj, svete.";
Místo jednotlivých znaků vymezených apostrofy, oddělených čárkami a uzavřených ve složených závorkách je řetezec vymezený uvozovkami, bez čárek a bez složených závorek. Nemusí se přidávat znak null, je doplněn kompilátorem.
Řetězec "Ahoj, světe" ve stylu jazyka C má 12 bajtů. Jeden bajt zabere znak null.

Lze také vytvořit neinicializovaná znaková pole. Musí se hlídat, aby se nevložilo více znaků než je vyhrazeno. Příklad:
//Vyrovnávací paměť pole znaků

#include <iostream>

int main()
{
	char buffer[80];
	std::cout << "Zadejte retezec: ";
	std::cin >> buffer;
	std::cout << "Zde mame buffer:  " << buffer << std::endl;
	return 0;
}
Zadejte retezec: Ahoj, svete
Zde mame buffer: Ahoj,
Analýza:  Program vykazuje  dva problémy.  Jestliže uživatel zadá 
více než 79 znaků, pak cin zapíše za konec bufferu.
Pokud  uživatel zadá  nějakou mezeru,  cin ji  považuje za  konec 
řetězce a ukončí zápis do vyrovnávací paměti.
Pozn.:  Je součástí  syntaxe cin,  aby zapsal  ukončovací nulu do 
vyrovnávací paměti po zadání řetězce.
Pro řešení těchto dvou problémů lze použít speciální metodu cin zvanou get().
metoda cin.get() přebírá tři parametry: naplňovaní buffer maximální počet přebíraných znaků oddělovač ukončující daný řetězec výchozím oddělovačem je znak nového řádku.
Příklad:
//Použití cin.get()

#include <iostream>
using namespace std;

int main()
{
	char buffer[80];
	cout << "Zadejte retezec: ";
	cin.get(buffer, 79);       // převzít až 79 znaků nebo po nový řádek
	cout << "Zde mame buffer:  " << buffer << endl;
	return 0;
}
Zadejte retezec: Ahoj, svete
Zde mame buffer: Ahoj, svete
Analýza:  Prvním  parametrem  metody  cin.get()  je námi naplněný 
buffer.
Maximální  počet přebíraných  znaků je  79, aby  zbylo místo  pro 
ukončovací nulu.
Ukončovací znak není potřeba zadávat, protože znak ukončení řádku 
vyhovuje.

Příaz strcpy() a strncpy()

Jazyk C++ zdědil od jazyka C knihovnu funkcí pro práci s řetězci ve stylu C string.h. Funkce strcpy() a strncpy() slouží ke kopírování jednoho řetězce do druhého.
strcpy() - kopíruje celý obsah jednoho řetězce do zadaného bufferu.
Příklad:
//Použití strcpy()

#include <iostream>
#include <string.h>
using namespace std;

int main()
{
	char Retezec1[] = "Zadny clovek neni ostrov";
	char Retezec2[80];

	strcpy(Retezec2,Retezec1);

	cout << "Retezec1: " << Retezec1 << endl;
	cout << "Retezec2: " << Retezec2 << endl;
	return 0;
}

Retezec1: Zadny clovek neni ostrov
Retezec2: Zadny clovek neni ostrov
Analýza:  strcpy(cíl,  zdroj);  zkopíruje  řetězec  zdroj do cíl. 
Pokud je zdroj větší než cíl, dojde k zápisu za konec vyrovnávací 
paměti.
strncpy() - kopíruje do cílového bufferu až po první znak null nebo po nejvyšší zadaný počet znaků.
Příklad:
// Použití strncpy()
#include <iostream>
#include <string.h>

int main()
{
	const int MaxDelka = 80;
	char Retezec1[] = " Zadny clovek neni ostrov";
	char Retezec2[MaxDelka+1];


	strncpy(Retezec2,Retezec1,MaxDelka);

	std::cout << "Retezec1: " << Retezec1 << std::endl;
	std::cout << "Retezec2: " << Retezec2 << std::endl;
	return 0;
}
Retezec1: Zadny clovek neni ostrov
Retezec2: Zadny clovek neni ostrov
Analýza:  strncpy(cíl, zdroj, maxDelka);
Buffer Retezec2 je deklarovaný pro MaxDelka+1 znak. Znak navíc je 
pro  nulu,  kterou  jak  strcpy()  tak  i  strncpy()  automaticky 
přidávají na konec řetězce.

Třídy řetězců

Jazyk C++ zdědil znakem null ukončené řetězce ve stylu jazyka C a také knihovnu funkcí, která zahrnuje i funkci strcpy(). Tyto funkce však nejsou integrované do objektově orientovaného rámce.
Standardní knihovna obsahuje třídu String, která poskytuje zapouzdřenou sadu dat a funkcí pro práci s daty a také přístupové funkce, aby byla Data samotná skryta před klienty třídy String.

Nyní jako cvičení vytvoříme třídu Retezec. Jako minimum by měla naše třída překonat základní omezení polí znaků.

Dobrá třída Retezec vyhrazuje jen skutečně potřebný prostor, který vždy přesně postačuje na uložení potřebných dat. Pokud nedokáže třída vyhradit dostatek paměti, měla by kontrolovaně selhat.
// Použití třídy Retezec

#include <iostream>
#include <string.h>
using namespace std;

// Základní řetězcová třída
class Retezec
{
public:
//10
	// Konstruktory
	Retezec();
	Retezec(const char *const);
	Retezec(const Retezec &);
	~Retezec();

	// Přetížené operátory
	char & operator[](unsigned short odsazeni);
	char operator[](unsigned short odsazeni) const;
	Retezec operator+(const Retezec&);
//20
	void operator+=(const Retezec&);
	Retezec & operator= (const Retezec &);

	// Obecné přístupové funkce
	unsigned short PrevzitDelku()const { return jehoDelka; }
	const char * PrevzitRetezec() const { return jehoRetezec; }

private:
	Retezec (unsigned short);         // Soukromý konstruktor
	char * jehoRetezec;
//30
	unsigned short jehoDelka;
};

// Výchozí konstruktor vytváří řetězec o délce 0 bajtů
Retezec::Retezec()
{
	jehoRetezec = new char[1];
	jehoRetezec[0] = '\0';
	jehoDelka=0;
}
//40

// Soukromý (pomocný) konstruktor používaný pouze
// metodami třídy k vytvoření nového řetězce
// požadované velikosti. Naplněný nulami.
Retezec::Retezec(unsigned short len)
{
	jehoRetezec = new char[len+1];
	for (unsigned short i = 0; i<=len; i++)
		jehoRetezec[i] = '\0';
	jehoDelka=len;
//50
}

// Převádí znakové pole na Retezec
Retezec::Retezec(const char * const cRetezec)
{
	jehoDelka = strlen(cRetezec);
	jehoRetezec = new char[jehoDelka+1];
	for (unsigned short i = 0; i<jehoDelka; i++)
		jehoRetezec[i] = cRetezec[i];
	jehoRetezec[jehoDelka]='\0';
//60
}

// Kopírovací konstruktor
Retezec::Retezec (const Retezec & rhs)
{
	jehoDelka=rhs.PrevzitDelku();
	jehoRetezec = new char[jehoDelka+1];
	for (unsigned short i = 0; i<jehoDelka;i++)
		jehoRetezec[i] = rhs[i];
	jehoRetezec[jehoDelka] = '\0';
//70
}

// Destruktor, uvolňuje přiřazenou paměť
Retezec::~Retezec ()
{
	delete [] jehoRetezec;
	jehoDelka = 0;
}

// Operátor rovno, uvolňuje existující paměť
//80
// a pak kopíruje řetězec a délku
Retezec& Retezec::operator=(const Retezec & rhs)
{
	if (this == &rhs)
		return *this;
	delete [] jehoRetezec;
	jehoDelka=rhs.PrevzitDelku();
	jehoRetezec = new char[jehoDelka+1];
	for (unsigned short i = 0; i<jehoDelka;i++)
		jehoRetezec[i] = rhs[i];
//90
	jehoRetezec[jehoDelka] = '\0';
	return *this;
}

// Nekonstantní operátor odsazení, vrací 
// odkaz na znak, aby jej bylo možné
// změnit!
char & Retezec::operator[](unsigned short odsazeni)
{
	if (odsazeni > jehoDelka)
//100
		return jehoRetezec[jehoDelka-1];
	else
		return jehoRetezec[odsazeni];
}

// Konstantní operátor odsazení pro použití
// s konstantními objekty (viz kopírovací konstruktor!)
char Retezec::operator[](unsigned short odsazeni) const
{
	if (odsazeni > jehoDelka)
//110
		return jehoRetezec[jehoDelka-1];
	else
		return jehoRetezec[odsazeni];
}

// Vytváří nový řetězec přidáním aktuálního 
// řetězce k rhs
Retezec Retezec::operator+(const Retezec& rhs)
{
	unsigned short celkovaDelka = jehoDelka + rhs.PrevzitDelku();
//120
	Retezec docasny(celkovaDelka);
	unsigned short i;
	for ( i= 0; i<jehoDelka; i++)
		docasny[i] = jehoRetezec[i];
	for (unsigned short j = 0; j<rhs.PrevzitDelku(); j++, i++)
		docasny[i] = rhs[j];
	docasny[celkovaDelka]='\0';
	return docasny;
}

//130
// Mění aktuální řetězec, nevrací nic
void Retezec::operator+=(const Retezec& rhs)
{
	unsigned short rhsDelka = rhs.PrevzitDelku();
	unsigned short celkovaDelka = jehoDelka + rhsDelka;
	Retezec  docasny(celkovaDelka);
	unsigned short i;
	for (i = 0; i<jehoDelka; i++)
		docasny[i] = jehoRetezec[i];
	for (unsigned short j = 0; j<rhs.PrevzitDelku(); j++, i++)
//140
		docasny[i] = rhs[i-jehoDelka];
	docasny[celkovaDelka]='\0';
	*this = docasny;
}

int main()
{
	Retezec s1("pocatecni test");
	cout << "S1:\t" << s1.PrevzitRetezec() << endl;

//150
	char * docasny = "Ahoj, svete";
	s1 = docasny;
	cout << "S1:\t" << s1.PrevzitRetezec() << endl;

	char docasnyDve[20];
	strcpy(docasnyDve,"; jsem tu rad!");
	s1 += docasnyDve;
	cout << "docasnyDve:\t" << docasnyDve << endl;
	cout << "S1:\t" << s1.PrevzitRetezec() << endl;

//160
	cout << "S1[4]:\t" << s1[4] << endl;
	s1[4]='x';
	cout << "S1:\t" << s1.PrevzitRetezec() << endl;

	cout << "S1[999]:\t" << s1[999] << endl;

	Retezec s2(" Jiny retezec");
	Retezec s3;
	s3 = s1+s2;
	cout << "S3:\t" << s3.PrevzitRetezec() << endl;
//170

	Retezec s4;
	s4 = "Proc tohle funguje?";
	cout << "S4:\t" << s4.PrevzitRetezec() << endl;
	return 0;
}
S1:        pocatecni test
S1:        Ahoj, svete
docasnyDve:           ;jsem tu rad!
S1:        Ahoj, svete;jsem tu rad!
S1[4]:     ,
S1:        Ahojx svete;jsem tu rad!
S1[999]:           !
S3:        Ahojx svete;jsem tu rad! Jiny retezec
S4:        Proc tohle funguje?
Analýza: Řádky 7-31 jsou deklaracemi jednoduché třídy Retezec.
11-13 obsahují tři konstruktory: výchozí konstruktor, kopírovací konstruktor a konstruktor přebírající existující řetězec ukončený znakem null (ve stylu jazyka C).
Třída Retezec přetěžuje operátor odsazení [], operátor plus + a operátor plus-rovno +=.
Operátor odsazení je přetížený dvakrát: jednou jako konstantní funkce vracející nějaký znak a jednou jako nekonstantní funkce vracející odkaz na znak.
Nekonstantní verze se používá v příkazech jako: NejakyRetezec[4]='x'; jak je patrné z řádku 161. To umožňuje přímý přístup ke každému znaku v řetězci. Vrací se odkaz na daný znak, takže s ním volající funkce může manipulovat.

Konstantní veetze se používá, když se jedná o přístup ke konstantnímu objektu Retezec, jako například v implementaci kopírovacího konstruktoru (řádek 63).
Všimněte si, že se přistupuje k rhs[i], přičemž však rhs je deklarovaný jako const String &.
Není povoleno přistupovat k tomuto objektu pomocí nekonstantní členské funkce. Proto je zapotřebí přetížit operátor odazení konstantní přístupovou funkcí.

Kdyby byl vracený objekt velký, pak by bylo vhodné deklarovat návratovou hodnotu jako konstantní odkaz. Protože je však znak jen jediným bajtem, nemá to žádný smysl.

Výchozí konstruktor je implementován na řádcích 33-39.
Vytváří řetězec, jehož délka je 0. Je konvencí této třídy Retezec hlásit délku bez ukončovacího znaku null. Tento výchozí řetezec obsahuje pouze ukončovací nulu.

Kopírovací konstruktor 63-70 nastavuje délku nového řetězce na délku existujícího řetězce - plus 1 pro ukončovací znak null. Kopíruje jednotlivé znaky z existujícího řetězce do nového řetězce a nový řetězec pak ukončuje nulou.

Řádky 53-60 implementují konstruktor, který přebírá existující řetězec ve stylu C. Tento konstruktor se podobá kopírovacímu konstruktoru. Délka existujícího řetězce se určí voláním standardní funkce strlen() třídy String.

Na řádku 28 je další konstrukor, Retezec(unsigned short) deklarovaný jako soukromá členská funkce. Cílem návrháře této třídy je, aby žádná klientská třída nikdy nemohla vytvořit řetězec zadané délky. Tento konstruktor existuje jen proto, aby pomáhal podle potřeby internímu vytváření řetězců například operátorem += na řádku 130.

Konstruktor Retezec(unsigned short) naplňuje všechny členy svého pole hodnotami null. Proto smyčka for kontroluje i<=delka a nikoli <delka

Destruktor implementovaný na řádcích 73-77 odstraňuje znakový řetězec udržovaný třídou. Nezapoměňte u volání operátorů uvést hranaté závorky, aby došlo k odstranění všech členů pole a nikoli jen toho prvního.

Operátor přiřazení nejprve zkontroluje, zda je pravá strana přiřazení stejná jako levá strana. Není-li, pak se odstraní aktuální řetězec a nová řetězec se vytvoří a zkopíruje na místo. Vracen je odkaz, aby bylo možné vytvbářet podobná přiřazení: Retezec=Retezec2=Retezec3

Operátor odsazení je pretížen dvakrát. Základní kontrola hranic se vykonává v obou prípadech. Pokusí-li se uživatel o prístup k nejakému znaku na míste za koncem pole, vrátí se poslední znak, tedy delka-1.

Rádky 117-127 implementují operátor plus + jako operátor spojení. Je pohodlné mít možnost zapsat: Retezec3=Retezec1 + Retezec2; a obdržet Retezec3 jako spojení zbývajících dvou retezcu. Abychom toho dosáhli, pocítá funkce operátoru plus kombinovanou délku obou retezcu a vytvárí docasný retezec docasny. Tím se volá soukromý konstruktor, který prebírá celé císlo a vytvárí retezec naplnení nulami. Znaky null se pak nahradá osbsahem obou retezcu. Nejprve se zkopíruje levý retezec (*this) a pak se zkopíruje pravý retezec (rhs).

První smycka for prochází retezcem vlevo a pridává jednotlivé znaky do nového retezce.
Druhá smycka for prochází pravou stranou. Všimnete si, že i pokracuje v pocítání místa nového retezce, i když j již prochází retezcem rhs.

Operátor plus vrací retezec docasny hodnotou, která se priradí retezci na levé strane prirazení (Retezec1). Operátor += pracuje s existujícím retezcem, tedy s levou stranou príkazu Retezec+=Retezec2. Funguje stejne jako operátor plus, jenom se hodnota docasny priradí aktuálnímu retezci (*this=docasny) na rádku 142.

Funkce main() 145-175 funguje jako testovací program pro tuto trídu.
147 - vytvárí objekt Retezec pomocí konstruktoru, který prebírá retezec ukoncený nulou ve stylu jazyka C.
148 - tiskne jeho obsah pomocí prístupové funkce PrevzitRetezec().
150 - vytvárí další retezec ve stylu C. 151 - testuje operátor prirazení.
152 - tiskne výsledek. 154 - vytvárí tretí retezec ve stylu C nazvaný docasnyDve.
155 - volá strcpy() aby došlo k naplnení bufferu znaky ";jsem tu rad!"
156 - volá operátor += a pripojuje docasnyDve k existujícímu retezci s1.
158 - tiskne výsledek
160 - zjištuje a tiskne pátý znak retezce s1
161 - priradí novou hodnotu na toto místo. Tím se zavolá nekonstantní operátor odsazení [].
162 - tiskne výsledek, který skutecne zachycuje zmenu hodnoty.
164 - se pokouší o prístup ke znaku za koncem pole. Vrátí se poslední znak pole, presne jak je požadováno
166-167 - vytvárejí dva další objekty Retezec
168 - volá operátor prictení.
169 - tiskne výsledek
171 - vytvárí nový objekt Retezec nazvaný s4.
172 - volá operátor prirazení.
173 - tiskne výsledek



21 - operátor prirazení je definován pro prevzetí konstantního odkazu na Retezec, zde však program predává retezec ve stylu jazyka C. Proc je to možné?
Odpovedí je, že kompilátor ocekává Retezec, ale dostane se mu znakové pole. Proto zjistí, zda muže vytvorit Retezec z toho, co má k dispozici. Na rádku 12 je deklarován konstruktor, který vytvárí retezce ze znakových polí. Kompilátor tedy vytvorí docasný objekt Retezec z daného znakového pole a predá jej operátoru prirazení. To se oznacuje za implicitní prevod neboli povýšení. Pokud bychom nedeklarovali konstruktoru, který prebírá pole znaku, toto prirazení by zpusobilo chybu kompilace.


PROPOJENÉ seznamy a další struktury

Pole mají pevnou velikost - zvolíme-li príliš velké pole, zbytecne plýtváme prostorem. Zvolíme-li malý rozsah, nemusí se nám dovnitr vejít vše co potrebujeme.
Jednou z možností rešení tohoto problému jsou propojené seznamy. Propojený seznam je datová struktura, která se skládá z malých "kontejneru" urcených k pospojování podle potreby. Pro každý objekt se vytvorí jeden kontejner a vzájemne se pospojují. Tyto kontejnery se oznacují jako uzly.
První uzel v seznamu je oznacován jako hlava a poslední jako ocas. Existuje nekolik druhu seznamu: lineární, obousmerný, kruhový, strom...
Lineární seznam - každý uzel ukazuje dopredu na svého následníka, nikoliv však zpet. Pri hledání uzlu musíme vycházet z vrcholu.
Obousmerný seznam - uzly ukazují na predchudce i následníka
Kruhový seznam - stejne jako obousmerný + hlava = ocas
Strom - složitá struktura vybudovaná z uzlu, z nichž každý muže smerovat dvema nebo tremi smery.

STUDIE PríKLADU PROPOJENéHO SEZNAMU

Delegování zodpovednosti

Základní myšlenkou objektove orientovaného prístupu je to, že každý objektu dokáže udelat JEDNU vec velmi dobre, a deleguje jiným objektum vše, co není jeho základní podstatou.
Napr. automobil - motor vytvárí energii, kterou rozvádí prevodovka - není to starost motoru. Stejne jako zatácení mají na starost kola a motor s prevodovkou se o to vubec nijak nestarají.

Dobre navržený stroj má mnoho malých cástí, jejichž úcel je dobre definovaný, pricemž každá delá svoji práci a spolecne naplnují vetší cíl.

Dobre navžený program má mnoho tríd, z nichž každá se drží svého, ale spolecne tvorí fungující celek.

SOUcásti komponent

Propojený seznam se bude skládat z uzlu. Samotná trída uzlu bude abstraktní: k našim úcelum použijeme tri podtypy: Ovládací program nemusí o uzlech vedet - pracuje se seznamem.
Seznam toho delá jen málo - deleguje práci uzlum.
Príklad: PROPOJENÝ SEZNAM
// ***********************************************
//    SOUBOR:        Výpis 13.13
//
//    ÚČEL:          Ukázat propojený seznam
//    POZNÁMKY:
//
//  COPYRIGHT:  Copyright (C) 1998 Liberty Associates, Inc.
//                All Rights Reserved
//
// Ukazuje objektově orientovaný přístup k propojeným
// seznamům. Seznam deleguje činnost uzlu. Uzel je
// abstraktní datový typ. Používají se tři typy uzlů:
// uzly hlavy, uzly ocasu a vnitřní uzly. Data
// obsahují pouze vnitřní uzly.
//
// Třída Data je vytvořena, aby sloužila jako objekt
// k uchování v daném propojeném seznamu.
//
// ***********************************************


#include <iostream>
using namespace std;

enum { kJeMensi, kJeVetsi, kJeStejny};

// Třída Data pro vložení propojeného seznamu. Každá 
// třída v propojeném seznamu musí podporovat dvě metody:
// Zobrazit (zobrazí hodnotu) a
// Porovnat (vrátí relativní pozici)
//30
class Data
{
public:
    Data(int hodnota):mojeHodnota(hodnota){}
    ~Data(){}
    int Porovnat(const Data &);
    void Zobrazit() { cout << mojeHodnota << endl; }
private:
    int mojeHodnota;
//40
};

// Porovnat se používá k určení, kam do seznamu
// určitý objekt patří.
int Data::Porovnat(const Data & jinaData)
{
    if (mojeHodnota < jinaData.mojeHodnota)
        return kJeMensi;
    if (mojeHodnota > jinaData.mojeHodnota)
        return kJeVetsi;
//50
    else
        return kJeStejny;
}

// Dopředné deklarace
class Uzel;
class UzelHlavy;
class UzelOcasu;
class VnitrniUzel;

//60
// ADT představující objekt uzlu v seznamu
// Každá odvozená třída musí překrýt Vlozit a Zobrazit
class Uzel
{
public:
    Uzel(){}
    virtual ~Uzel(){}
    virtual Uzel * Vlozit(Data * taData)=0;
    virtual void Zobrazit() = 0;
private:
//70
};

// Toto je uzel, který obsahuje vlastní objekt
// V tomto případě jde o objekt typu Data
// Uvidíme, jak to učinit obecněji, až si budeme
// povídat o šablonách
class VnitrniUzel: public Uzel
{
public:
    VnitrniUzel(Data * taData, Uzel * dalsi);
//80
    ~VnitrniUzel(){ delete mujDalsi; delete mojeData; }
    virtual Uzel * Vlozit(Data * taData);
    // Delegovat!
    virtual void Zobrazit() { mojeData->Zobrazit(); mujDalsi->Zobrazit();}  

private:
    Data * mojeData;    // samotná Data
    Uzel * mujDalsi;    // ukazuje na další uzel v propojeném seznamu
};

//90
// Konstruktor jenom inicializuje
VnitrniUzel::VnitrniUzel(Data * taData, Uzel * dalsi):
mojeData(taData),mujDalsi(dalsi)
{
}

// Základ celého seznamu
// Když do seznamu vložíte nový objekt, 
// předá se uzlu, který určí jeho pozici
// a vloží jej do seznamu
//100
Uzel * VnitrniUzel::Vlozit(Data * taData)
{

    // Je ten nový hoch větší nebo menší než já?
    int vysledek = mojeData->Porovnat(*taData);


    switch(vysledek)
    {
    // Je li stejný jako já, pak podle konvence patří přede mě
//110
    case kJeStejny:        // projít dál
    case kJeVetsi:         // nová Data patří přede mě
        {
            VnitrniUzel * datovyUzel = new VnitrniUzel(taData, this);
            return datovyUzel;
        }

    // Je větší než já, takže jej předáme dalšímu
    // uzlu a ponecháme na NĚM, ať se stará.
    case kJeMensi:
//120
        mujDalsi = mujDalsi->Vlozit(taData);
        return this;
    }
    return this;  // naplnit MSC
}


// Uzel ocasu je jen hlídka

class UzelOcasu : public Uzel
//130
{
public:
    UzelOcasu(){}
    ~UzelOcasu(){}
    virtual Uzel * Vlozit(Data * taData);
    virtual void Zobrazit() { }

private:

};
//140

// Pokud nějaká Data přijdou až ke mně, musí se vložit
// přede mě, protože já jsem ocas a za mnou už není NIC.
Uzel * UzelOcasu::Vlozit(Data * taData)
{
    VnitrniUzel * datovyUzel = new VnitrniUzel(taData, this);
    return datovyUzel;
}

// Uzel hlavy neobsahuje žádná Data, jenom 
//150
// ukazuje na úplný začátek seznamu
class UzelHlavy : public Uzel
{
public:
    UzelHlavy();
    ~UzelHlavy() { delete mujDalsi; }
    virtual Uzel * Vlozit(Data * taData);
    virtual void Zobrazit() { mujDalsi->Zobrazit(); }
private:
    Uzel * mujDalsi;
//160
};

// Jakmile se vytvoří hlava, vytvoří se
// také ocas.
UzelHlavy::UzelHlavy()
{
    mujDalsi = new UzelOcasu;
}

// Před hlavou nic není, takže Data
//170
// jen předáme dalšímu uzlu.
Uzel * UzelHlavy::Vlozit(Data * taData)
{
    mujDalsi = mujDalsi->Vlozit(taData);
    return this;
}

// Mám všechny zásluhy a přitom nic nedělám.
class PropojenySeznam
{
//180
public:
    PropojenySeznam();
    ~PropojenySeznam() { delete mojeHlava; }
    void Vlozit(Data * taData);
    void ZobrazitVse() { mojeHlava->Zobrazit(); }
private:
    UzelHlavy * mojeHlava;
};

// Při zrození vytvořím uzel hlavy.
//190
// Ten vytvoří uzel ocasu, takže
// prázdný seznam ukazuje na hlavu, která
// ukazuje na ocas a mezi nimi nic není.
PropojenySeznam::PropojenySeznam()
{
    mojeHlava = new UzelHlavy;
}

// Deleguji, deleguješ, delegujeme
void PropojenySeznam::Vlozit(Data * pData)
//200
{
    mojeHlava->Vlozit(pData);
}

// Testovací program
int main()
{
    Data * pData;
    int hodnota;
    PropojenySeznam ps;
//210

    // Požádat uživatele a zadání nějakých hodnot
    // a vložit je do seznamu.
    for (;;)
    {
        cout << "Jaka hodnota? (konec = 0): ";
        cin >> hodnota;
        if (!hodnota)
            break;
        pData = new Data(hodnota);
//220
        ps.Vlozit(pData);
    }

    // Nyní projít seznamem a zobrazit Data
    ps.ZobrazitVse();
    return 0;  // ps vypadne z oboru platnosti a je zrušen!
}
Pozn: číslo řádku v komentáři náleží řádku pod tímto číslem!
Jaka hodnota? (konec = 0): 5 
Jaka hodnota? (konec = 0): 8 
Jaka hodnota? (konec = 0): 3 
Jaka hodnota? (konec = 0): 9 
Jaka hodnota? (konec = 0): 2 
Jaka hodnota? (konec = 0): 10 
Jaka hodnota? (konec = 0): 0
2 
3 
5 
8 
9 
10 
Analýza: Nejprve deklarujeme výctovou konstantu, která poskytuje tri konstantní hodnoty:kJeMensi, kJeVetsi, kJeStejny.
Každý objekt, který muže být obsažen v tomto propojeném seznamu, musí podporovat metodu Porovnat(). 
Tyto konstanty budou výslednou hodnotou vrácenou metodou Porovnat().


30-39 Vytvárí trídu Data
41-51 implementace metody Porovnat()
Objekt Data obsahuje nejakou hodnotu a dokáže se porovnat s jinými objekty Data. Rovnež podporuje metodu Zobrazit() sloužící k zobrazení hodnoty daného objektu Data.

Pro pochopení si projdeme príklad, který se seznamem pracuje.
203 - deklarace ovládacího programu
206 - deklarace ukazatele na objekt Data
208 - definice místního propojeného seznamu

Když se vytvorí tento seznam, zavolá se konstruktor na rádku 192. Jedinou prací konstruktoru je alokovat objekt UzelHlavy a priradit adresu tohoto objektu ukazateli obsaženému v seznamu na rádku 185.

Toto alokování UzelHlavy zavolá konstruktor UzelHlavy na rádcích 163-166. Tím se dále alokuje UzelOcasu a jeho adresa se priradí ukazateli mujDalsi uzlu hlavy. Vytvorení UzelOcasu volá konstruktor UzelOcasu na rádku 131, který nic nedelá. Proto prostým alokováním seznamu na zásobníku dojde k vytvorení daného seznamu, k vytvorení uzlu hlavy a ocasu a k jejich propojení:
Propojený seznam ---> Uzel hlavy ---> Uzel ocasu

212 - zacíná nekonecná smycka. Uživatel zadává hodnoty, které mají být pridány do seznamu. Zadávání se ukoncuje vložením 0.

Není-li vložená hodnota nulová, pak na řádku 218 je vytvoren nový objekt Data a řádku 219 je vložen do seznamu. Predpokládejme, že uživatel zadal 15. Tím dojde k zavolání metody Vlozit na rádku 198.

Propojený seznam okamžite deleguje zodpovednost za vložení daného objektu svému uzlu hlavy. Tím se zavolá metoda Vlozit na rádku 170. Uzel hlavy okamžite predá zodpovednost uzlu, na který ukazuje mujDalsi. V tomto prípade ukazuje na uzel ocasu. Proto dojde k vyvolání metody Vlozit na rádku 142. Metoda UzelOcasu:Vlozit ví, že objekt, který jí byl predán, musí vložit bezprostredne pred sebe. To znamená, že nový objekt se bude nacházet v seznamu bezprostredne pred uzlem ocasu. Proto na rádku 144 vytvorí nový objekt VnitrniUzel, kterému predá Data a ukazatel na sebe sama. Tím dojde k zavolání konstruktoru objektu VnitrniUzel, jenž je uvedený na rádku 90.

Konstruktor VnitrniUzel jenom inicializuje svuj další ukazatel Data, adresou jemu predaného objektu Data a inicializuje svuj ukazatel mujDalsi adresou uzlu, která mu byla predána. V tomto prípade bude odkazovaným uzlem uzel ocasu.(uzel ocasu predává svuj vlastní ukazatel this)

Protože byl nyní vytvoren VnitrniUzel, adresa tohoto vnitrního uzlu se priradí ukazateli datovyUzel na rádku 144 a tato adresa je dále vrácena z metody UzelOcasu::Vlozit(). To nás vrací do UzelHlavy::Vlozit(), kde adresa daného objektu VnitrniUzel priradá ukazateli propojeného seznamu, kde dojde na rádku 200 k jeho odhození (nic se nedelá, protože propojený seznam již zná adresu uzlu hlavy).

Proc se trápit s vracením adresy, která se nepoužije? Metoda Vlozit je deklarovaná v bázové tríde Uzel. Návratová hodnota je vyžadována dalšími implementacemi. Zmeníte-li návratovou hodnotu UzelHlavy::Vlozit(), obdržíte chyby kompilace; jednodušší je proste vrátit UzelHlavy a nechat propojený seznam, at tuto adresu odhodí.

Co se stalo? Data byla vložena do seznamu. Seznam je předal hlavě. Hlava je slepě předala tomu objektu, na který zrovna ukazovala. V tomto případě ukazovala hlava na ocas. Ocas okamažitě vytvořil nový vnitřní uzel a inicializoval jej tak, aby ukazoval na ocas. Ocas pak vrátil adresu tohoto nového uzlu hlavě, která upravila svůj ukazatel mujDalsi tak, aby se přesměroval na tento nový uzel.

Po vložení prvního prvku se obnoví řízení programu na řádku 214. Předpokládejme vložení hodnoty 3.
Dojde k vytvoření nového objektu Data na řádku 218 a vložení do seznamu na řádku 219.

Na řádku 200 znovu seznam předá Data svému UzelHlavy. Metoda UzelHlavy::Vlozit() zase předá novou hodnotu objektu, na který právě ukazuje mujDalsi. Nyní směřuje na objekt Data s hodnotou 15.
Dojde tedy k zavolání metody VnitrniUzel::Vlozit() na řádku 99.

Na řádku 103 použije VnitrniUzel svuj ukazatel mojeData a řekne svému objektu Data (s 15) aby zavolal svou metodu Porovnat(), které předá nový objekt Data (s 3). Tak dojde k zavolání metody Porovnat() na řádku 43.

Dojde k porovnání obou hodnot, a protože mojeHodnota bude 15 a jinaData.mojeHodnota bude 3 stane se vrácenou hodnotou kJeVetsi. To způsobí přeskok programu na řádek 1112.

Pro nový objekt Data se vytvoří nový VnitrniUzel. Tento nový uzel bude ukazovat na aktuální objekt VnitrniUzel a adresa nového objektu VnitrniUzel se vrátí z metody VnitrniUzel::Vlozit() do UzelHlavy. Proto se tento nový uzel, hodnota jehož objektu je menší než hodnota objektu aktuálního uzlu, vloží do seznamu před aktuální uzel.

Při třetím vkládání vloží uživatel 8.
Výsledkem porovnání s prvním uzlem (s 3) bude kJeMensi. To způsobí odskok VnitrniUzel::Vlozit() na řádek 119. Místo vytvoření nového uzlu a jeho vložení tak daný VnitrniUzel předá Data metodě Vlozit objektu, na který právě směřuje ukazatel mujDalsi. V tomto případě dojde k zavolání Vlozit objektu VnitrniUzel, jehož objekt má hodnotu 15.

Znovu dojde k porovnání a následně k vytvoření nového objektu VnitrniUzel. Tento nový VnitrniUzel bude ukazovat na VnitrniUzel, hodnotou jehož objektu Data je 15, a jeho adresa se předá zpět uzlu VnitrniUzel, jehož objekt Data má hodnotu 3, jak je patrné z řádku 119.

Cílovým efektem je vložení nového uzlu na správnou pozici v seznamu.


Při tomto OOP je každému jednotlivému objektu dána úzká a dobře definovaná sada zodpovědností.
Seznam odpovídá za správu uzlu hlavy.
Uzel hlavy předá nová data objektu, na který právě ukazuje, ať se jedná o cokoli.
Uzel ocasu vytváří nový uzel a vkládá do něj předaná data. Ví jen jedinou věc: Když se dostala až ke mně, musí se vložit přede mě.
Vnitřní uzly jsou složitější - požadují porovnání existujícího objektu s novým objektem. Podle výsledku buď objekt vloží nebo předají dál.


TříDY POLí

Vytvoříme-li vlastní třídu pole, dosáhneme mnoha výhod oproti zabudované třídě.
Lze ošetřit přetečení pole, lze vytvořit pole s dynamickou velikostí...
Varianty polí:

14 - xx