David Bednárek ulita.ms.mff.cuni.cz

Slides:



Advertisements
Podobné prezentace
A1PRG - Programování – Seminář Ing. Michal Typová konverze, oblast platnosti, paměťové třídy 9 Verze
Advertisements

Standardní knihovní funkce pro práci s textovými řetězci
(instance konkrétní třídy)
VISUAL BASIC Práce s řetězci Použitá literatura: Kvoch: Visual Basic 4.
Programování v C jazyku - SEMINÁŘ
Dynamické dokumenty na straně klienta Informatika pro ekonomy II.
Přednáška 11 Jiří Šebesta
Programování 2 Cvičení 5.
ÚVOD DO CPP 7 Dědičnost - pokračování
BLIŽŠÍ POHLED NA TŘÍDY, DĚDIČNOST - úvod
PJV151 Vnořené a vnitřní členy mohou být členy tříd a interfejsů. Je-li X obalem Y a Y je obalem Z, pak Z získá jméno X$Y$Z - kompilací vzniknou classy.
C# pro začátečníky Mgr. Jaromír Osčádal
Programování v C++ Cvičení.
Principy překladačů Běhová podpora Jakub Yaghob. Běhová podpora Statická podpora jazyka Překladač Interface na knihovny Hlavičkové soubory Dynamická podpora.
Principy překladačů Mezikód Jakub Yaghob.
Algoritmizace a programování
C++0x stručný náhled na nadcházející revizi standardu programovacího jazyka C++ (ISO/IEC 14882) Jan Ringoš.
Programování PA - 2.
Materiály k přednášce Úvod do programování Ondřej Čepek.
Generování mezikódu Jakub Yaghob
State. State – kontext a problém Kontext  chování objektu má záviset na jeho stavu, který se typicky mění za běhu Neflexibilní řešení  metody obsahují.
Informatika I 3. přednáška
Práce se soubory. * soubory patří k základním datovým prvkům v počítači * převážná většina programovacích jazyků má podporu určité formy práce se soubory.
A1PRG - Programování – Seminář Ing. Michal Standardní knihovní funkce pro práci se soubory 13 Verze
Vyučovací hodina 1 vyučovací hodina: Opakování z minulé hodiny 5 min Nová látka 20 min Procvičení nové látky 15 min Shrnutí 5 min 2 vyučovací hodiny: Opakování.
OSNOVA: a) Řetězce v C b) Funkce stdio.h pro řetězce c) Funkce string.h pro řetězce d) Příklad Jiří Šebesta Ústav radioelektroniky, FEKT VUT v Brně Počítače.
A1PRG - Programování – Seminář Ing. Michal Operátory (2. část) 4 Verze
Seminář C cvičení STL, Trolltech Ing. Jan Mikulka.
Objektové programování
Sémantická analýza Jakub Yaghob
6. cvičení Polymorfismus
A1PRG - Programování – Seminář Ing. Michal Ukazatele a pole 10 Verze
Počítače a programování 1
Cvičení.
VISUAL BASIC PRALG.
KIV/PPA1 cvičení 8 Cvičící: Pavel Bžoch. Osnova cvičení Objekty v Javě Třída Konstruktor Metody Metody a proměnné třídy x instance Program sestávající.
OSNOVA: a) Úvod do OOPb) Třídy bez metod c) Třídy s metodamid) Konstruktory a destruktory e) Metody constf) Knihovní třídy g) Třídy ve tříděh) Přetížení.
Algoritmizace a programování Objektově orientované programování - 16 Mgr. Josef Nožička IKT Algoritmizace a programování
Algoritmizace a programování Binární soubory - 14 Mgr. Josef Nožička IKT Algoritmizace a programování
IB111 Programování a algoritmizace
Návrh a tvorba WWW Přednáška 5 Úvod do jazyka PHP.
Dědičnost - inheritance dědičnost je jednou z forem znovupoužitelnosti dědičnost je jednou z forem znovupoužitelnosti B A Třída A je předkem třídy B Třída.
7. Typ soubor Souborem dat běžně rozumíme uspořádanou množinu dat, uloženou mimo operační paměť počítače (na disku). Pascalský soubor je abstrakcí skutečného.
Orbis pictus 21. století Tato prezentace byla vytvořena v rámci projektu.
Počítače a programování 1 7.přednáška. Základy Pole ve třídách a metodách Pole Arrays.
Pokročilé programování v C++ (část B)
Ukazatele, řetězce Přednáška č. 3. Ukazatele  Ukazatel (pointer) – typ o velikosti 4 bajty (v 32bit. systémech) pro uložení adresy objektu na který ukazuje.
Soubory BI-PA1 Programování a algoritmizace 1, ZS Katedra teoretické informatiky © Miroslav Balík Fakulta informačních technologií České vysoké.
Jazyk C A0B36PRI - PROGRAMOVÁNÍ Část II.
Jazyk C A0B36PRI - PROGRAMOVÁNÍ Část I.
Pokročilé datové typy (struktury, unie, dynamické proměnné)
Programování v jazyce C++
Překladače 6. Sémantická analýza
Programování v jazyce C++ Speciality jazyka C++, úvod do OOP.
Makra v Excelu syntaxe. Zápis nahraného makra SubEnd Sub O klíčová slova Sub a End Sub – začátek a konec makra O komentáře O text za znakem ', až do konce.
Počítače a programování 2
Y36PJC Programování v jazyce C/C++
Moduly.
Programování ENUM, SWITCH,pole jednorozměrná a vícerozměrná, deklarace, inicializace, kopírování, porovnání Erik Král.
Vícerozměrná pole (1) Jazyk C povoluje, aby pole mělo více rozměrů (dimenzí) než jeden Z vícerozměrných polí bývá nejčastěji použí-váno pole dvourozměrné.
Výukový materiál zpracován v rámci projektu
Y36PJC Programování v jazyce C/C++
Výukový materiál zpracován v rámci projektu
Programovací jazyk C Autorem materiálu a všech jeho částí, není-li uvedeno jinak, je Ing. Jitka Vlčková. Dostupné z Metodického portálu ISSN.
Programování v jazyce C++
Operační systémy 9. Spolupráce mezi procesy
Funkce výstupu (2) Funkce printf: deklarována v: stdio.h
Příkazy cyklu (1) Umožňují vícekrát (nebo ani jednou) pro-vést určitý příkaz Jazyk C rozlišuje příkaz cyklu: s podmínkou na začátku: obecný tvar: while.
Oblast platnosti identifikátoru (1)
C# přehled vlastností.
Transkript prezentace:

David Bednárek ulita.ms.mff.cuni.cz Programování v C++ David Bednárek ulita.ms.mff.cuni.cz

Pravidla studia PRG029 2/2 Z,Zk

Elektronický zápis do jednotlivých skupin Zápis na cvičení Elektronický zápis do jednotlivých skupin www.mff.cuni.cz/vnitro/is/sis Grupíček Skupiny jsou již naplněny podle příslušnosti ke studijním skupinám Část příslušníků skupin ovšem již nestuduje Asi 20% studentů žije mimo studijní skupiny a nejsou tedy zapsáni Zapsáni musejí být všichni Do konce února Kdo se neobjeví na prvním ani druhém cvičení, může být vyškrtnut Kapacita skupin je omezena, kdo dřív přijde... Udělit zápočet může jen cvičící, ke kterému je student zapsán Kdo nebude včas zapsán...

Podmínky udělení zápočtu určuje cvičící Cvičící může podmínky individuálně upravit, pokud se s ním student na začátku semestru dohodne Vzorové podmínky Přiměřená účast na cvičeních Úspěšné odevzdání alespoň 2 ze 3 domácích úkolů Úspěšné složení zápočtového testu 1. a 2. pokusy ve zkouškovém období ... 3. pokusy v září 2-3 hodiny v laboratoři

Pravidla pro budoucí neúspěšné Zkouška Pokud letos složíte zkoušku se známkou výborně nebo velmi dobře a nedostanete zápočet, bude vám příští rok automaticky uznána Tento mechanismus je implementován zkoušejícími, nikoliv studijním oddělěním Zápočet Pokud letos splníte zápočet a nesložíte zkoušku, bude vám příští rok automaticky uznán Pokud nedostanete zápočet, budete příští rok opakovat ty části, které jste letos nesplnili Podmínky splněné letos se automaticky uznávají V příštím roce se musíte na začátku semestru přihlásit na některé cvičení a dohodnout se s jeho cvičícím na konkrétních podmínkách Mohou být vypsána speciální cvičení pro repetenty

Porovnání studijních programů Staré Mgr. a Bc. programy PRG012 Programování v C++ 2/2 Z, Zk Povinný ZS 2. ročníku Naposledy 2003/2004 Krátký kurz C Přelet nad C++ Zápočtový program PRG020 Objektové programování v C++ 2/0 Zk Nepovinný LS 2. ročníku Pokročilé C++ Nové Bc. programy PRG029 Programování v C++ 2/2 Z, Zk Povinný LS 1. ročníku Poprvé 2003/2004 Kurz C První část kurzu C++ Objektově orientované programování ZS 2. ročníku Poprvé 2004/2005 Zápočtový program Druhá část kurzu C++

Obsah

Obsah cvičení Střídavě v S7 a v laboratoři SW1 Zajímavé a záludné vlastnosti jazyka C Přehled standardních knihoven jazyka C Práce s datovými strukturami, triky C++ pouze na požádání Laboratoř Microsoft Visual Studio .NET 2002(3) Praktické programování Ladění programů

Obsah přednášky Kurs jazyka C Programování není zápis algoritmů Vztah programu a operačního systému Přenositelnost programu na jiné OS a HW Udržovatelnost programu Popis základních částí jazyka C++ Třídy, dědičnost Virtuální funkce, polymorfismus

Obsah přednášky Kurs jazyka C Základní struktura programů Překlad programů, oddělený překlad a spojování Řídící konstrukce Datové typy, deklarace Struktury, unie, výčtové typy Operátory Pole a ukazatelé Práce s řetězci

Literatura

Literatura Bjarne Stroustrup: The C++ Programming Language (1985) James O. Coplien: Advanced C++ Programming Styles and Idioms (1992) Bruce Eckel: C++ Inside & Out (1993) Bruce Eckel: Thinking in C++ (2000) Myslíme v jazyku C++ (Grada 2000) Herb Sutter: Exceptional C++ Miroslav Virius: Pasti a propasti jazyka C++ Miroslav Virius: Programování v C++ (ČVUT 2001)

Normy "K&R", dnes nepoužívané: Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language (1978) De-facto norma C++: Bjarne Stroustrup: The C++ Programming Language (1985) Nejnovější: ISO/IEC 14882, ANSI: Programming languages - C++ (1998) ISO/IEC 9899: Programming languages - C (1999)

Nedoporučená literatura Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language (1978) Programovací jazyk C (1988) Popisuje K&R verzi C, nyní nepoužívanou Jan Brodský, Luděk Skočovský: Operační systém Unix a jazyk C (1989) Popisuje K&R verzi C, navíc jen část jazyka Martin Beran: Učebnice Borland C++ Jan Pokorný: Rukověť uživatele Borland C++ Popisují Borland C++, které je zastaralé a odlišné od normy Vladimír Rudolf: ABC programátora v C++ neúplné, zastaralé Dalibor Kačmář: Jazyk C — učebnice pro střední školy chyby Eric Gunnerson: Začínáme programovat v C# C# není C++

Hello, world! Oddělený překlad modulů Kurs jazyka C Hello, world! Oddělený překlad modulů

Hello, world! hello.c #include <stdio.h> #include <conio.h> int main( int argc, char * * argv) { printf( "Hello, world!\n"); getch(); return 0; }

Program se skládá z modulů Struktura programu Program se skládá z modulů Překládány samostatně kompilátorem Spojovány linkerem Modul z pohledu programátora Soubor s příponou .c (.cpp) Hlavičkové soubory Soubory s příponou .h Deklarují identifikátory používané ve více modulech Vkládány do modulů direktivou include Direktivu zpracovává preprocesor čistě textově Preprocesor je integrován v kompilátoru jako první fáze překladu Modul z pohledu kompilátoru Výsledek práce preprocesoru Lze vyžádat zhmotnění v souboru (s příponou .i)

Překlad jednoduchého programu Standardní .h Standardní .obj Standardní .lib Uživatelský .c Kompilátor Přeložený .obj Linker Spustitelný soubor .exe

Oddělený překlad a spojování modulů Uživatelské .h Standardní .h Kompilátor Uživatelské .c Přeložené .obj Linker Spustitelný soubor .exe Standardní .obj Standardní .lib

Struktura programu Modul (z pohledu překladače) je posloupnost globálních deklarací a definic V C++ je tato vlastnost narušena konstrukcemi extern "C" { ... } a namespace X { ... } Tyto konstrukce pouze aplikují společnou vlastnost na více deklarací či definic, které je stále možné považovat za globální Deklarace Oznámení, že identifikátor existuje Určení některých vlastností (typ, parametry) Definice Doplnění zbývajících vlastností (kód funkce, obsah struktury) Překladač vygeneruje kód funkce, vyhradí místo pro proměnnou

Neexistuje "hlavní blok" apod. Struktura programu Neexistuje "hlavní blok" apod. Běh programu začíná vyvoláním funkce main Před funkcí main běží inicializace běhového prostředí, po ní úklid Funkce main musí mít tuto hlavičku: int main( int argc, char * * argv)

Spojování modulů - problém x.c double A() { return B(); } y.c double B() { return 3.14; } error: Undefined 'B' y.obj kód B export B

Spojování modulů - externí deklarace x.c double B(); // deklarace double A() // definice { return B(); } x.obj kód A s voláním naprázdno export A import B y.c double B() // definice { return 3.14; } y.obj kód B export B p.exe kód A s voláním B

Spojování modulů - nekonzistence x.c double B(); // deklarace double A() // definice { return B(); } x.obj kód A s voláním naprázdno export A import B y.c int B( int q) // definice { return q + 1; } y.obj kód B export B p.exe kód A s voláním B Runtime error

Spojování modulů s hlavičkovými soubory x.c #include "y.h" double A() { return B( 7); } y.h int B( int q); y.c int B( int q) { return q + 1; } int B( int q); double A() { return B( 7); } x.obj kód A s voláním naprázdno export A import B y.obj kód B export B

Spojování modulů - nekonzistence x.c #include "y.h" double A() { return B( 7); } y.h int B( int q); y.c double B() { return 3.14; } int B( int q); double A() { return B( 7); } x.obj kód A s voláním naprázdno export A import B y.obj B: [.][.][.][.][.] export B

Spojování modulů - správné řešení x.c #include "y.h" double A() { return B( 7); } y.h int B( int q); y.c #include "y.h" double B() { return 3.14; } int B( int q); double A() { return B( 7); } int B( int q); double B() { return 3.14; } x.obj kód A s voláním naprázdno export A import B error: Redefinition of 'B' with different type

Spojování modulů - duplicitní data x.c #include "y.h" double A() { return C; } y.h int C; y.c #include "y.h" int C; int C; double A() { return C; } int C; int C; x.obj kód A místo pro C export A, C y.obj místo pro C export C linker error: Duplicate symbol 'C'

Spojování modulů - data x.c #include "y.h" double A() { return C; } y.h extern int C; y.c #include "y.h" int C; extern int C; double A() { return C; } extern int C; int C; x.obj kód A s čtením naprázdno export A import C y.obj místo pro C export C

Spojování modulů - typy x.c #include "y.h" double A() { return C; } y.h enum T { P, Q}; extern enum T C; y.c #include "y.h" enum T C; enum T { P, Q}; extern enum T C; double A() { return C; } enum T { P, Q}; extern enum T C; int C; x.obj kód A s čtením naprázdno export A import C y.obj místo pro C export C

Spojování modulů - duplicita typů x.c #include "y.h" #include "z.h" double A() { return C + D; } t.h enum T { P, Q}; y.h #include "t.h" extern enum T C; enum T { P, Q}; extern enum T C; enum T { P, Q}; extern enum T D; double A() { return C + D; } z.h #include "t.h" extern enum T D; error: Type redefinition: 'T'

Spojování modulů - #ifndef x.c #include "y.h" #include "z.h" double A() { return C + D; } t.h #ifndef _T_H #define _T_H enum T { P, Q}; #endif y.h #include "t.h" extern enum T C; #ifndef _T_H #define _T_H enum T { P, Q}; #endif extern enum T C; #ifndef _T_H #define _T_H enum T { P, Q}; #endif extern enum T D; double A() { return C + D; } z.h #include "t.h" extern enum T D;

make Uživatelské .h Standardní .h Kompilátor Uživatelské .c Přeložené .obj Linker Spustitelný soubor .exe Standardní .obj Standardní .lib Make makefile

Integrované prostředí Uživatelské .h Standardní .h Kompilátor Uživatelské .c Přeložené .obj Linker Spustitelný soubor .exe Standardní .obj Standardní .lib Editor projekt Debugger

Uživatelské knihovny Uživatelské .h Standardní .h Kompilátor Uživatelské .c Přeložené .obj Linker Spustitelný soubor .exe Standardní .obj Standardní .lib Knihovní .obj Knihovna .lib Knihovní .c Librarian Privátní .h Veřejné .h

Dynamicky linkované knihovny Uživatelské .h Standardní .h Kompilátor Uživatelské .c Přeložené .obj Linker Spustitelný soubor .exe Standardní .obj Standardní .lib Stub .obj Stub .lib Knihovní .c Privátní .h Veřejné .h Knihovna .dll Loader

Přípony souborů Microsoft Unix .c Zdrojový kód C .cpp .C .cc .cpp .h Hlavičkový soubor C .h .hpp nebo bez přípony .h .H .hh .hpp nebo bez přípony Hlavičkový soubor C++ .i (Výstup preprocesoru) .asm .s (Kód v assembleru) .obj .o Objektový modul .lib .a Knihovna .dll .so Dynamicky linkovaná knihovna .exe bez přípony Spustitelný program

Kurs jazyka C Datové typy

Číselné typy bool, _Bool C++, C99: false, true char znak základní sady (např. ASCII) wchar_t stddef.h: znak rozšířené sady (např. Unicode) signed char velmi malé celé číslo [-128..127] unsigned char velmi malé nezáporné celé číslo [0..255] [signed] short [int] malé celé číslo [-32768..32767] unsigned short [int] malé nezáporné celé číslo [0..65535] [signed] int celé číslo (16/32 bit) unsigned [int] nezáporné celé číslo (16/32 bit) [signed] long [int] velké celé číslo (32 bit) unsigned long [int] velké nezáporné celé číslo (32 bit) long long [int] C99: extra velké celé číslo (64 bit) unsigned long long [int] C99: extra velké nezáporné celé číslo (64 bit) float malé “reálné” číslo double střední “reálné” číslo long double velké “reálné” číslo

Typové konstrukce A x[ n] pole n prvků typu A, n je konstantní výraz pole neznámého počtu prvků typu A (povoleno pouze v určitých kontextech) A * x ukazatel na typ A void * x ukazatel na neurčený typ A const * x const A * x ukazatel na konstantní hodnotu typu A A * const x konstantní ukazatel na typ A A & x C++: reference na typ A A const & x const A & x C++: reference na konstantní hodnotu typu A A x() funkce vracející typ A C: bez určení parametrů C++: bez parametrů A x( param) funkce s určenými parametry A x( void) funkce bez parametrů void x( param) funkce bez návratové hodnoty (procedura)

Kombinace typových konstrukcí A * x[10] pole ukazatelů A (* x)[10] ukazatel na pole - zbytečné A * x() funkce vracející ukazatel A (* x)() ukazatel na funkci A x[10]() pole funkcí - zakázáno A (* x[10])() pole ukazatelů na funkci A x()[10] funkce vracející pole - zakázáno A (* x())[10] funkce vracející ukazatel na pole - zbytečné

Použití typových konstrukcí Globální a lokální deklarace a definice unsigned long * p, x, y[ 5]; definuje ukazatel p, číslo x a pole y tentýž specifikátor typu (unsigned long) se využije vícekrát deklarátor, tj. konstrukce *,[ ] nebo ( ), platí pouze pro jeden identifikátor častá chyba: unsigned long * p1, p2; /* p2 není ukazatel ! */

Použití typových konstrukcí Parametry funkce int f( unsigned long * p, unsigned long x, unsigned long y[]) specifikátor typu se musí opakovat pro každý parametr identifikátory parametrů mohou být vynechány, pokud nejsou zapotřebí (u hlavičky funkce bez těla apod.) int f( unsigned long *, unsigned long, unsigned long []);

Použití typových konstrukcí Pole jako parametr funkce int f( unsigned long * p, unsigned long x, unsigned long y[]) Pole se vždy předávají odkazem předává se ukazatel na první prvek nepředává se velikost nemá smysl udávat velikost pole v deklaraci funkce uvedená hlavička funkce je zcela ekvivalentní int f( unsigned long * p, unsigned long x, unsigned long * y) ukazatelová aritmetika zajistí, že k takto předanému poli je možno přistupovat konstrukcí y[i] otázka, zda ukazatel ukazuje na jediný prvek (p) nebo na začátek celého pole (y), se řeší pouze úmluvou, překladač ji neřeší

Použití typových konstrukcí Abstraktní deklarátor Typ se v určitých kontextech popisuje konstrukcí, která odpovídá deklaraci bez deklarovaného identifikátoru unsigned long void * int [10] void (*)(void) /* ukazatel na proceduru bez parametrů */ void (*)(void *, int) /* ukazatel na proceduru s dvěma parametry */ použití: int f(void *); /* nepojmenovaný parametr */ n = sizeof( int [10]); /* změření velikosti typu */ q = (int *)p; /* explicitní přetypování */ p = new int * [10]; /* C++: dynamická alokace */

Struktury, unie, výčtové typy Struktura struct STR { int a, b, * c; struct STR * next; }; Použití struktury struct STR x, y, * p; Unie elementy se překrývají neobsahuje informaci, který element platí union UNI { int i; double d; char s[ 10]; void * p; }; Výčtový typ enum E { I, // 0 D = 7, // 7 S, // 8 P = D - 1 // 6 }; Variantní záznam struct POLY { enum E t; union UNI u; };

Pojmenování struktur, unií a výčtových typů V jazyce C Jméno (tag) struktury/unie/výčtového typu je možno používat pouze s příslušným prefixem struct/union/enum struct STR { int a, b; struct STR * next; }; struct STR * p; V jazyce C++ Jméno třídy/struktury/unie/výčtového typu je možno používat i bez prefixu class/struct/union/enum STR * next; STR * p;

Trik pro rekurzivní struktury Pojmenování typů Typedef typedef int * IPTR; Deklarace s prefixem typedef nedeklaruje proměnnou, ale typ   Trik pro rekurzivní struktury typedef struct STR T_STR; /* dopředná deklarace struct STR */ T_STR * p; /* OK */ T_STR a; /* chyba: kompilátor ještě nezná obsah struct T_STR */ struct STR { /* definice struct STR */ int a, b; IPTR c; T_STR * next; }; T_STR x, y, * p;

Operátory, asociativita a priorita postfix (C++) ++ -- ( ) [ ] -> . :: post-in(de)-krementace volání funkce index přístup k položce kvalifikace identifikátoru prefix (C++) ++ -- ! ~ + - & * sizeof ( ) new delete pre-in(de)-krementace booleovská a bitová negace unární +/- reference, dereference měření velikosti přetypování dynamická alokace dynamická dealokace zleva (C++) .* ->* dereference member-pointeru zleva * / % multiplikativní operátory + - aditivní operátory << >> bitové posuny zleva < <= > >= relace uspořádání == != relace rovnosti & bitové AND ^ bitové XOR | bitové OR && booleovské AND || booleovské OR ? : podmíněný výraz zprava = *= /= %= += -= &= ^= |= <<= >>= přiřazení kombinované přiřazení , sekvence

Přiřazení = je binární operátor, který má Zajímavé operátory Přiřazení = je binární operátor, který má vedlejší efekt - modifikaci levého operandu levým operandem musí být "modifikovatelná l-hodnota" lhodnota (lvalue) - "někde bydlí" není označena const hodnotu - hodnotu (a typ) levého operandu po modifikaci nemusí odpovídat hodnotě pravého operandu kvůli konverzím Operátory += atd. a <op>= b je ekvivalent a = a <op> b hodnotou výrazu je tedy výsledná hodnota levé strany

Prefixové operátory ++, -- Zajímavé operátory Prefixové operátory ++, -- ++a je ekvivalent a = a + 1 hodnotou je tedy nová hodnota proměnné Postfixové operátory ++, -- a++ ((a = a + 1) - 1) hodnotou je původní hodnota proměnné

& - Reference - získání adresy Zajímavé operátory & - Reference - získání adresy int v; int * p; p = & v; * - Dereference - přístup přes ukazatel int v2; * p = 1; v2 = * p;

Přístup k prvkům struktury Zajímavé operátory Přístup k prvkům struktury struct STR { int x, y; }; struct STR a; přístup do proměnné a.x struct STR * p; přístup přes ukazatel p->x je ekvivalentní (*p).x

Pravidla vyhodnocování a( b, c, d) - volání funkce: vedlejší efekty parametrů jsou vyhodnoceny před zavoláním a && b - AND: je-li první operand nulový, druhý se nevyhodnocuje a || b - OR: je-li první operand nenulový, druhý se nevyhodnocuje a ? b : c - podmíněný výraz: po úplném vyhodnocení podmínky a se vyhodnotí buď b nebo c a , b - sekvenční vyhodnocení: po úplném vyhodnocení a se vyhodnotí b Žádná další pravidla nejsou: ostatní operátory v jednom výraze mohou být vyhodnocovány v libovolném pořadí a jejich vedlejší efekty se mohou projevit kdykoliv během výpočtu výrazu

Pravidla vyhodnocování - triky a chyby Fungující triky Test ukazatele while ( p && p->v < v ) p = p->next; Volání funkce s vedlejším efektem while ( (c = getchar()) != EOF && c != '\n' ); Kopie řetězce while ( *a++ = *b++ ); Chyby Vícenásobný výskyt modifikované proměnné p[ i++] = i; Nadbytečné volání funkce s vedlejším efektem if ( getchar() == 'A' || getchar() == 'B' ) /*...*/ if ( getchar() == 'A' && getchar() == 'B' ) /*...*/

Pole a ukazatelé int z[ 10]; /* pole 10 prvků typu int */   int main( int argc, char * * argv) { int * p, * q; int b; p = &z[ 2]; /* reference */ q = &z[ 9]; p = p + 2; /* ukazatel + celé číslo */ q = q - 2; /* ukazatel - celé číslo */ *p = *q; /* dereference: z[ 4] = z[ 7] */ b = q - p; /* ukazatel - ukazatel: b = 3 */ }

Automatické konverze pole-ukazatel Vztah ukazatelů a polí Nulový ukazatel #define NULL 0 Vyžaduje #include <stddef.h> V C++ nebývá zvykem konstantu NULL používat Automatické konverze pole-ukazatel Je-li výraz typu pole na místě, kde typ pole nemůže být, automaticky se konvertuje na ukazatel na nultý prvek tohoto pole. p = z je ekvivalentní p = &z[0] Operátor [ ] a[b] je ekvivalentní *(a+b) Pole nelze přiřazovat ani předávat hodnotou

Dynamická alokace C C++ Pozor: toto není 100% ekvivalent #include <stdlib.h> ELEM * p; Alokace jednoho objektu p = (ELEM *)malloc( sizeof( ELEM)); if ( ! p ) { /* chyba */ } Uvolnění jednoho objektu free( p); Alokace pole objektů int n = 100; p = (ELEM *)malloc( n * sizeof( ELEM)); Uvolnění pole objektů C++ Pozor: toto není 100% ekvivalent ELEM * p; Alokace jednoho objektu p = new ELEM; if ( ! p ) { /* chyba */ } Uvolnění jednoho objektu delete p; Alokace pole objektů int n = 100; p = new ELEM[ n]; Uvolnění pole objektů delete[] p;

Organizace paměti procesu Kódový segment Datový segment Heap Zásobník (stack segment) IP R0 R1 ... SP

Organizace paměti procesu Kódový segment Připraven kompilátorem – součást spustitelného souboru Kód uživatelských i knihovních funkcí Obvykle chráněn proti zápisu Datový segment Heap Zásobník (stack segment) IP R0 R1 ... SP

Organizace paměti procesu Kódový segment Datový segment Připraven kompilátorem – součást spustitelného souboru Explicitně nebo implicitně (nulami) inicializované globální proměnné Řetězcové konstanty Data knihoven Pomocná data generovaná kompilátorem Heap Zásobník (stack segment) IP R0 R1 ... SP

Organizace paměti procesu Kódový segment Datový segment Heap Vytvářen startovacím modulem knihoven Neinicializovaná dynamicky alokovaná data malloc/free C++: new/delete Obsazené bloky různé velikosti + seznam volných bloků Knihovny mohou též požádat OS o zvětšení segmentu Zásobník (stack segment) IP R0 R1 ... SP

Organizace paměti procesu Kódový segment Datový segment Heap Zásobník (stack segment) Připraven op. systémem, knihovny mohou požádat OS o zvětšení Explicitně inicializované nebo neinicializované lokální proměnné Pomocné proměnné generované kompilátorem Návratové adresy Aktivační záznamy procedur IP R0 R1 ... SP

Příklad užití ukazatelů a polí int main( int argc, char * * argv) { int opt_n = 0; char * opt_p = ""; argv++, argc--; while ( argc && **argv == '-' ) { switch ( (*argv)[ 1] ) { case 'n': opt_n = 1; if ( (*argv)[ 2] ) goto error; break; case 'p': opt_p = *argv + 2; break; default: goto error; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /* ... */ - p H e l o A . T X n C : \ M Y P R O G E B argv 5 argc

Příklad užití ukazatelů a polí int main( int argc, char * * argv) { int opt_n = 0; char * opt_p = ""; argv++, argc--; while ( argc && **argv == '-' ) { switch ( (*argv)[ 1] ) { case 'n': opt_n = 1; if ( (*argv)[ 2] ) goto error; break; case 'p': opt_p = *argv + 2; break; default: goto error; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /* ... */ - p H e l o A . T X n C : \ M Y P R O G E B (*argv)[0] 4 argc opt_p opt_n *argv (*argv)[1] (*argv)[2] **argv

Příklad užití ukazatelů a polí int main( int argc, char * * argv) { int opt_n = 0; char * opt_p = ""; argv++, argc--; while ( argc && **argv == '-' ) { switch ( (*argv)[ 1] ) { case 'n': opt_n = 1; if ( (*argv)[ 2] ) goto error; break; case 'p': opt_p = *argv + 2; break; default: goto error; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /* ... */ - p H e l o A . T X n C : \ M Y P R O G E B (*argv)[0] 3 argc opt_p 1 opt_n *argv (*argv)[1] **argv *argv+2 argv

Příklad užití ukazatelů a polí int main( int argc, char * * argv) { int opt_n = 0; char * opt_p = ""; argv++, argc--; while ( argc && **argv == '-' ) { switch ( (*argv)[ 1] ) { case 'n': opt_n = 1; if ( (*argv)[ 2] ) goto error; break; case 'p': opt_p = *argv + 2; break; default: goto error; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /* ... */ argc 2 opt_n 1 argv opt_p argv[0] argv[1] B . T X T A . T X T - p H e l l o - n C : \ M Y P R O G . E X E

Práce s řetězci v C Řetězcové konstanty puts( "BUBAK"); Řetězcová konstanta je ekvivalentem globální proměnné typu char[ ], inicializované obsahem konstanty static char gv[] = "BUBAK"; puts( gv); Výjimka: řetězcová konstanta v inicializaci pole typu char[ ] je ekvivalentem inicializace jednotlivých prvků pole static char gv[] = { 'B', 'U', 'B', 'A', 'K', 0 }; Inicializace polí Je-li pole inicializováno, nemusí být udána jeho velikost a kompilátor ji dopočte z počtu inicializátorů. Výsledek je ekvivalentní poli s udanou velikostí static char gv[ 6] = { 'B', 'U', 'B', 'A', 'K', 0 };

Předávání řetězců do funkcí Práce s řetězci v C Předávání řetězců Jazyk C/C++ nedovoluje kopírování pole, tj. není možné pole přiřazovat, předávat hodnotou jako parametr ani vracet z funkce Kopírování řetězců Knihovní funkce (string.h) char * strcpy( char * dst, const char * src); Nekontroluje přetečení cílového pole ! Vrací dst Předávání řetězců do funkcí Předává se vždy odkazem Parametr typu const char * Vracení řetězců z funkcí Nahrazuje se "návratovým" parametrem Parametr typu char * Je vhodné jej doplnit parametrem typu size_t, určujícím velikost pole

Problém vracení řetězců Práce s řetězci v C Problém vracení řetězců char * cele_jmeno( const char * jmeno, const char * prijmeni) { char buf[ 100]; strcpy( buf, jmeno); strcat( buf, " "); strcat( buf, prijmeni); return buf; } Toto řešení je naprosto chybné Nekontroluje přetečení pole buf Vrací odkaz na lokální proměnnou, která v okamžiku návratu zaniká

Problém vracení řetězců Práce s řetězci v C Problém vracení řetězců char * cele_jmeno( const char * jmeno, const char * prijmeni) { static char buf[ 100]; strcpy( buf, jmeno); strcat( buf, " "); strcat( buf, prijmeni); return buf; } Toto řešení je chybné Nekontroluje přetečení pole buf Používá statickou proměnnou, která zbytečně zabírá místo i v době, kdy funkce není vyvolána opakovaná volání ji sdílejí: if ( strcmp( cele_jmeno( j1, p1), cele_jmeno( j2, p2)) ) tato podmínka nikdy nebude splněna, protože strcmp vždy dostane dva stejné ukazatele na totéž pole buf

Problém vracení řetězců Práce s řetězci v C Problém vracení řetězců void cele_jmeno( char * buf, const char * jmeno, const char * prijmeni) { strcpy( buf, jmeno); strcat( buf, " "); strcat( buf, prijmeni); } Toto řešení je funkční, ale nebezpečné Nekontroluje přetečení pole buf Pokud volající nemá spolehlivý horní odhad velikostí jména a příjmení, nemůže tuto funkci bezpečně volat void tisk( const char * jmeno, const char * prijmeni) char buf[ 100]; cele_jmeno( buf, jmeno, prijmeni); puts( buf);

Práce s řetězci v C Správné řešení int cele_jmeno( char * buf, size_t bufsize, const char * jmeno, const char * prijmeni) { size_t l_jmeno = strlen( jmeno); size_t l_prijmeni = strlen( prijmeni); if ( l_jmeno + l_prijmeni + 2 > bufsize ) { /* error */ return -1; } memcpy( buf, jmeno, l_jmeno); buf[ l_jmeno] = ' '; memcpy( buf + l_jmeno + 1, prijmeni, l_prijmeni); buf[ l_jmeno + l_prijmeni + 1] = 0; return l_jmeno + l_prijmeni + 1; } void tisk( const char * jmeno, const char * prijmeni) { enum { N = 100 }; char buf[ N]; cele_jmeno( buf, N, jmeno, prijmeni); puts( buf);

Řešení s dynamickou alokací Práce s řetězci v C Řešení s dynamickou alokací char * cele_jmeno( const char * jmeno, const char * prijmeni) { size_t l_jmeno = strlen( jmeno); size_t l_prijmeni = strlen( prijmeni); char * buf = (char *)malloc( l_jmeno + l_prijmeni + 2); if ( ! buf ) { /* error */ return 0; } memcpy( buf, jmeno, l_jmeno); buf[ l_jmeno] = ' '; memcpy( buf + l_jmeno + 1, prijmeni, l_prijmeni); buf[ l_jmeno + l_prijmeni + 1] = 0; return buf; } void tisk( const char * jmeno, const char * prijmeni) char * buf = cele_jmeno( jmeno, prijmeni); puts( buf); free( buf);

Problém řešení s dynamickou alokací Práce s řetězci v C Problém řešení s dynamickou alokací Dynamicky alokovaný výsledek musí být uvolněn Míchání se staticky alokovanými řetězci je zdrojem chyb void tisk( const char * jmeno, const char * prijmeni) { char * buf; if ( * jmeno ) buf = cele_jmeno( jmeno, prijmeni); else if ( * prijmeni ) buf = prijmeni; buf = "N.N."; puts( buf); free( buf); // ??? }

Vazba programu na operační systém Proces je izolován od ostatních procesů a jádra OS Virtuální adresový prostor a/nebo ochrana paměti Přímá komunikace s I/O zařízeními není možná Přímá komunikace s jinými procesy by byla možná technikou sdílené paměti Není ovšem všude dostupná a standardizována Její použití je efektivní ale obtížné a nebezpečné

Vazba programu na operační systém Veškerá komunikace je vedena systémovými voláními Systémové volání zajišťuje: Přechod z uživatelského režimu do privilegovaného a zpět Možnost suspendování procesu uvnitř systémového volání Konkrétní technika systémového volání závisí na HW a OS Softwarové přerušení, brány, speciální volání, falešné výjimky Obvykle není možné volání přímo z C/C++ Systémové volání je relativně pomalé Množina systémových volání je definována OS Může být přímo zpřístupněna knihovnou (Unix, "io.h") Nemusí být zveřejněna (Microsoft)

Vazba programu na operační systém Zveřejněné rozhraní operačního systému "C-API" Zpřístupněno knihovnami pro C (výjimečně C++) Nejtypičtějsí část je standardizována Většina je závislá na OS Knihovní funkce obvykle provádějí více než jedno systémové volání Některé knihovny mohou zcela zatemňovat původní logiku systémových volání Soubory: Buffering, překlad znakových sad, statefull/stateless Spouštění procesů: spawn = fork + exec Vnitřek knihovních funkcí může záviset na verzi OS Připojovány jako DLL v okamžiku startu procesu (Microsoft)

Vazba programu na operační systém Standardizovaná rozhraní operačního systému stdio.h - souborový vstup a výstup Přístup "s ukazovátkem" Sjednocení přístupu k souborům a rourám stdin, stdout, stderr Buffering - snížení počtu systémových volání Následky při pádu programu - fflush Textový/binární mód Překlad do jednotné formy - oddělovač řádků "\n" Neřeší adresářové služby signal.h, stdlib.h - řízení procesu Vyvolání/příjem signálu (podle Unixového vzoru) Ukončení procesu system( "winword my.doc")

Základní knihovny C a C++ <assert.h> <cassert> - ladicí funkce (makro assert) <ctype.h> <cctype> - klasifikace znaků (isalpha, isspace, ...) <errno.h> <cerrno> - chybové kódy (ENOMEM, ...), proměnná errno <float.h> <cfloat> - vlastnosti a limity reálných typů (DBL_MAX, ...) <limits.h> <limits> <climits> - limity celočíselných typů (INT_MAX, ...) <locale.h> <locale> <clocale> - přizpůsobení národnímu prostředí <math.h> <cmath> - matematické funkce (sin, ...) <setjmp.h> <csetjmp> - meziprocedurální skoky (setjmp, longjmp) <signal.h> <csignal> - signály operačního systému <stdarg.h> <cstdarg> - makra pro funkce s proměnným počtem argumentů <stddef.h> <cstddef> - užitečné typy a konstanty (NULL) <stdio.h> <cstdio> - standardní a souborový vstup a výstup <stdlib.h> <cstdlib> - užitečné funkce (malloc, ...) <string.h> <cstring> - manipulace s řetězci (strcpy, ...) <time.h> <ctime> - konverze data a času <wchar.h> <cwchar> - 16-bitové řetězce (wchar_t) <wctype.h> <cwctype> - klasifikace 16-bitových znaků

stdio.h Otevírání a zavírání souborů Manipulace se soubory FILE * fopen(const char *, const char *); "rt" - čtení textového souboru "wt" - zápis textového souboru "at" - připojení na konec textového souboru "rb" - čtení binárního souboru "wb" - zápis binárního souboru "rb+" - čtení a zápis binárního souboru FILE * tmpfile(void); int fflush(FILE *); int fclose(FILE *); Manipulace se soubory int remove(const char *); int rename(const char *, const char *);

stdio.h Stav souboru Posun ukazovátka I (binární soubory) int feof(FILE *); int ferror(FILE *); void clearerr(FILE *); Posun ukazovátka I (binární soubory) long ftell(FILE *); int fseek(FILE *, long, int); enum { SEEK_SET, SEEK_CUR, SEEK_END }; Posun ukazovátka II int fgetpos(FILE *, fpos_t *); int fsetpos(FILE *, const fpos_t *);

stdio.h Čtení po znacích Zápis po znacích int fgetc(FILE *); int getchar(void); int ungetc(int, FILE *); Zápis po znacích int fputc(int, FILE *); int putchar(int); Čtení a zápis bloků (binárních souborů) size_t fread(void *, size_t, size_t, FILE *); size_t fwrite(const void *, size_t, size_t, FILE *); Čtení a zápis po řádkách char * fgets(char *, int, FILE *); int fputs(const char *, FILE *); void perror(const char *);

stdio.h Formátované čtení a zápis %s - char * - řetězec int fscanf(FILE *, const char *, ...); int scanf(const char *, ...); int fprintf(FILE *, const char *, ...); int printf(const char *, ...); %s - char * - řetězec %c - char - znak %d - int - decimálně %6d - zarovnat doprava na 6 znaků %x - int - šestnáctkově %08X - 8 šestnáctkových číslic, velká písmena %f - double - s desetinnou tečkou %7.3f - 3 desetinná místa, 7 znaků celkem %g - double - s exponentem int vfprintf(FILE *, const char *, va_list); int vprintf(const char *, va_list); viz stdarg.h

stdio.h Formátování řetězců Nebezpečné: nekontroluje přetečení int sscanf(const char *, const char *, ...); int sprintf(char *, const char *, ...); int vsprintf(char *, const char *, va_list); Nebezpečné: nekontroluje přetečení

Užitečné knihovní funkce string.h Užitečné knihovní funkce char * strcpy( char * dst, const char * src); Kopie řetězce - nekontroluje přetečení ! char * strcat( char * dst, const char * src); Připojení řetězce - nekontroluje přetečení ! size_t strlen( const char * src); Počet znaků řetězce (size_t je celočíselný typ definovaný v stdlib.h) int strcmp( const char * a, const char * b); Porovnání řetězců Vrací číslo > 0, pokud a je lexikograficky větší, 0 při shodě, číslo < 0 pokud a je menší než b Nerespektuje lokální znakové sady (porovnává kódy znaků) Pozor: if ( a < b ) porovnává ukazatele, nikoliv obsah řetězců ! char * strchr( const char * a, int c); char * strstr( const char * a, const char * b); Vrací ukazatel na první výskyt znaku c/řetězce b v řetězci a, 0, pokud se nevyskytuje

Vlákna Pro realizaci serverů i některých GUI aplikací Je-li třeba souběžně vykonávat více činností Nebo čekat na více událostí různého druhu Proces může mít více vláken (threads) Všechna vlákna žijí uvnitř společného adresového prostoru Každé vlákno má vlastní zásobník (a tedy jiný SP) První vlákno je spuštěno při spuštění procesu Další vlákna vznikají na pokyn již existujících vláken Vlákna běží kvazi-paralelně, na multiprocesorech paralelně Všechny moderní OS vlákna podporují Způsoby implementace se mohou výrazně lišit Lze je též implementovat na úrovni knihoven bez vědomí OS Norma C ani C++ o vláknech nehovoří Neexistuje přenositelný způsob práce s vlákny Existují pokusy o unifikaci prostřednictvím nestandardních knihoven

Vlákna Vícevláknové aplikace vyžadují synchronizaci vláken pro udržení konzistence dat: kritické sekce, zámky (spinlocks) pro čekání na události: semafory, události (events), rendez-vous Prostředky pro synchronizaci vláken obdoba synchronizace procesů jednodušší, nevyžadují pojmenování apod. některé synchronizační akce lze dělat bez použití OS, tedy rychleji Neexistuje standardní rozhraní Existuje poměrně jednotná terminologie převzatá z teorie OS Programování s vlákny je obtížnější Nešikovná vzájemná komunikace vláken může zdržovat i zablokovat Využití vláken na multiprocesorech vyžaduje zvláštní opatrnost

Přenositelnost mezi platformami Různé platformy se mohou lišit Vlastnostmi hardware Velikostí adresového prostoru a velikostí ukazatelů Pořadím ukládání vícebajtových hodnot (little/big endian) Dostupnými formáty celých a reálných čísel Vlastnostmi operačního systému Znakovou sadou (ASCII/EBCDIC, Win/ISO), oddělovači řádků Konvencemi jmen souborů (oddělovače, povolené znaky, délka) Dalšími vlastnostmi souborového systému (sémantika delete, links, ACL) Základními službami a konvencemi OS (environment, registry) Konvencemi (/usr/bin, .exe, $HOME) Vlastnostmi překladače Volbou velikosti základních aritmetických typů Způsobem zarovnání položek struktur Rozpoznávanou pod-/nad-množinou jazyka Chybami v diagnostice a generovaném kódu Vlastnostmi knihoven Dostupností, pojmenováním a sémantikou funkcí

Přenositelnost mezi platformami Ideál: Program přenositelný bez úpravy zdrojového textu Zakáz konstrukcí závislých na vlastnostech hardware a překladače int x; char * p = (char *)&x; struct { char a; int b; } S; fwrite( &S, 1, sizeof( S), fp); Užívání základních typů prostředníctvím typedef typedef unsigned long UINT32; Opatrné užívání pokročilých konstrukcí (member-pointers, templates) Přednostní používání funkcí definovaných normou jazyka Nelze-li jinak, užívání direktiv #ifdef #ifdef _MSC_VER // Microsoft C/C++ typedef __int64 INT64; const char delimiter = '\\'; #else typedef long long INT64; #ifdef UNIX const char delimiter = '/'; #endif

Udržovatelné zdrojové texty Základní pravidla čitelnosti a udržovatelnosti Logické rozdělení do modulů a hlavičkových souborů Jasné oddělení rozhraní od implementace Oddělení obsluhy uživatelského rozhraní od vlastní logiky aplikace Minimum globálních proměnných Komentáře Indentace, omezená délka řádek Pojmenovávací konvence, logicky zvolené a dlouhé identifikátory Buďto my_big_array nebo MyBigArray Obvykle typy a konstanty začínají velkými písmeny, proměnné malými GetX/SetX konvence pro metody v C++ apod. Pojmenování všech opakujících se konstant Žádné int x[ 100] ani case 3:

Bezpečné programování Základní pravidla Zapnout všechna varování, která je schopen kompilátor vydat Upravit program do podoby, která nezpůsobí žádné varování V odůvodněných případech lze varování vypnout pomocí #pragma Dodržovat pravidla pro přenositelné a vícevláknové programy A to i když přenositelnost ani vícevláknovost nemá smysl Minimum globálních proměnných, pokud možno pouze konstantní Procedura smí číst či měnit pouze objekty, které jsou přímo či nepřímo určeny jejími parametry Důsledné užívání ochranných prostředků kompilátoru const, private, ...

Bezpečné programování Důsledná chybová diagnostika Test úspěšnosti každého malloc, fopen, ... Testy proti interním chybám a špatným parametrům void f( int * p) { assert( p); /*...*/ } Ochrana proti užívání odalokovaných bloků free( p); p = 0; Ochrana proti přetečením polí Všechny funkce manipulující s polem dostávají velikost pole jako parametr Žádné strcat, gets a podobné nebezpečné funkce Největší nepřítel je chyba, která není vždy a ihned smrtelná Dereference nulového ukazatele se pozná ihned Dereference neinicializovaného ukazatele způsobí pád později

Ladění programů debuggerem Typický debugger umí: Spustit program v ladicím režimu (zvláštní funkcí OS) Některé zvládnou i připojení k již běžícímu procesu (JIT Debugging) Ladicí režim nemusí být pro laděný program identický s normálním Většina funkcí debuggeru je možná pouze pro programy přeložené v ladicím nastavení překladače (bez optimalizací) Chybný program se může chovat při ladění jinak než finální verze Krokovat a spouštět program Odchytit chybující program a zobrazit stav těsně před chybou Nedestruktivně zastavit běžící program Nastavovat breakpointy do kódu Mohou být podmíněné Nastavovat breakpointy na data (změna či splnění podmínky) Některé typy mohou o několik řádů zpomalit běh programu Zobrazovat zásobník volání Zobrazovat lokální i globální proměnné Zobrazovat paměť laděného procesu

Ladění programů bez debuggeru Ladicí implementace alokačních funkcí Obložit každý alokovaný blok prostorem vyplněným značkami void * my_malloc( size_t s) { char * p = (char *)malloc( s+sizeof(size_t)+2*AIRBAG_SIZE); if ( ! p ) return 0; *(size_t *)p = s; memcpy( p+sizeof(size_t), AIRBAG_FILL, AIRBAG_SIZE) memcpy( p+sizeof(size_t)+AIRBAG_SIZE+s, AIRBAG_FILL, AIRBAG_SIZE) return p+sizeof(size_t)+AIRBAG_SIZE; } Při dealokaci zkontrolovat neporušenost značek a změnit je void my_free( void * vp) { char * p; if ( ! vp ) ERROR(); p = (char *)vp - (sizeof(size_t)+AIRBAG_SIZE); if ( memcmp( p+sizeof(size_t), AIRBAG_FILL, AIRBAG_SIZE) ) ERROR(); if ( memcmp( p+sizeof(size_t)+AIRBAG_SIZE+*(size_t *)p, AIRBAG_FILL, AIRBAG_SIZE) ) ERROR(); /* ... zmenit znacky ... */ free( p);

C++ Encapsulation - Zapouzdření Inheritance - Dědičnost Polymorphism - Polymorfismus

Obsah přednášky Principy objektového programování v C++ Logika dědičnosti Abstraktní třídy, interface/implementace Polymorfní datové struktury Násobná dědičnost, virtuální dědičnost Třída jako datový typ Nevhodné vlastnosti jazyka C++

Významné vlastnosti jazyka C++ Run-time: Vlastnosti C++ s reprezentací za běhu programu Dědičnost, násobná dědičnost (multiple inheritance) Virtuální dědičnost (virtual inheritance) Virtuální metody (virtual functions) Relativní ukazatelé (member-pointers) Zpracování výjimek (exception handling) Typová informace za běhu (RTTI) Syntactic sugar: Vlastnosti C++ bez reprezentace za běhu Pomůcky (reference, implicitní parametry, přetěžování funkcí) Pro zapomnětlivé (konstruktory, přesnější typová kontrola) Proti šťouralům (zapouzdření, ochrana přístupu, prostory jmen) Pro estéty (přetěžování funkcí a operátorů, uživatelské konverze) Pro líné (šablony) Knihovny (STL)

Třída a objekt Class and Object

Zobecnění pojmu struktura (struct) Třída a objekt Třída (class) Zobecnění pojmu struktura (struct) Rozdíl mezi class a struct v C++ je nepatrný Užívání class místo struct je pouze konvence Deklarace třídy obsahuje Deklarace datových položek (stejně jako v C) Funkce (metody), virtuální funkce a statické funkce Definice výčtových konstant a typů (včetně vnořených tříd)

Zobecnění pojmu struktura (struct) Třída a objekt Třída (class) Zobecnění pojmu struktura (struct) Rozdíl mezi class a struct v C++ je nepatrný Užívání class místo struct je pouze konvence Deklarace třídy obsahuje Deklarace datových položek (stejně jako v C) Funkce (metody), virtuální funkce a statické funkce Definice výčtových konstant a typů (včetně vnořených tříd) Objekt (instance třídy) Běhová reprezentace jednoho exempláře třídy Reprezentace objektu v paměti obsahuje Datové položky Skryté pomocné položky umožňující funkci virtuálních metod, výjimek a RTTI virtuální dědičnosti

Dědičnost Inheritance

Třída (struktura) může mít jednoho nebo více předků Dědičnost Princip dědičnosti: Třída (struktura) může mít jednoho nebo více předků Relace předek-potomek je orientovaný acyklický (nesouvislý) graf Není povinný společný prapředek pro všechny třídy

Třída (struktura) může mít jednoho nebo více předků Dědičnost Princip dědičnosti: Třída (struktura) může mít jednoho nebo více předků Relace předek-potomek je orientovaný acyklický (nesouvislý) graf Není povinný společný prapředek pro všechny třídy Účel dědičnosti: Je možné napsat kód, který pracuje s daty (a dalšími prvky) společného předka několika tříd a to bez závislosti na tom, jaký je typ celého objektu tento kód lze přeložit bez znalosti potomků

Třída (struktura) může mít jednoho nebo více předků Dědičnost Princip dědičnosti: Třída (struktura) může mít jednoho nebo více předků Relace předek-potomek je orientovaný acyklický (nesouvislý) graf Není povinný společný prapředek pro všechny třídy Účel dědičnosti: Je možné napsat kód, který pracuje s daty (a dalšími prvky) společného předka několika tříd a to bez závislosti na tom, jaký je typ celého objektu tento kód lze přeložit bez znalosti potomků Reusabilita: Potomci třídy mají automaticky její datové položky a další vlastnosti a schopnosti

Třída (struktura) může mít jednoho nebo více předků Dědičnost Princip dědičnosti: Třída (struktura) může mít jednoho nebo více předků Relace předek-potomek je orientovaný acyklický (nesouvislý) graf Není povinný společný prapředek pro všechny třídy Účel dědičnosti: Je možné napsat kód, který pracuje s daty (a dalšími prvky) společného předka několika tříd a to bez závislosti na tom, jaký je typ celého objektu tento kód lze přeložit bez znalosti potomků Reusabilita: Potomci třídy mají automaticky její datové položky a další vlastnosti a schopnosti Polymorfismus: Je možné vytvořit datovou strukturu, která sdružuje objekty různých tříd, pokud mají společného předka

Princip implementace dědičnosti v C++: Je-li třída B (přímým či nepřímým) potomkem třídy A, pak: Paměťová reprezentace objektu typu B obsahuje část, která má stejný tvar jako paměťová reprezentace samostatného objektu typu A

Princip implementace dědičnosti v C++: Je-li třída B (přímým či nepřímým) potomkem třídy A, pak: Paměťová reprezentace objektu typu B obsahuje část, která má stejný tvar jako paměťová reprezentace samostatného objektu typu A Z každého ukazatele na typ B je možno odvodit ukazatel na část typu A Tato konverze je implicitní, tj. není třeba ji explicitně uvádět ve zdrojovém kódu Tato konverze může (obvykle pouze při násobné dědičnosti) vyžadovat jednoduchý výpočet (přičtení posunutí)

Princip implementace dědičnosti v C++: Je-li třída B (přímým či nepřímým) potomkem třídy A, pak: Paměťová reprezentace objektu typu B obsahuje část, která má stejný tvar jako paměťová reprezentace samostatného objektu typu A Z každého ukazatele na typ B je možno odvodit ukazatel na část typu A Tato konverze je implicitní, tj. není třeba ji explicitně uvádět ve zdrojovém kódu Tato konverze může (obvykle pouze při násobné dědičnosti) vyžadovat jednoduchý výpočet (přičtení posunutí) Z ukazatele na typ A je možno odvodit ukazatel na typ B za těchto okolností: Konkrétní objekt, do kterého ukazuje ukazatel na typ A, je typu B (nebo jeho potomka) Zodpovědnost za ověření skutečného typu objektu má programátor Tuto konverzi je třeba explicitně vynutit přetypováním Konverze může vyžadovat odečtení posunutí

Dědičnost - reusabilita class A { public: int x, y; }; int f( A * pa) { return pa->x * pa->y; } A x y pa

Dědičnost - reusabilita class A { public: int x, y; }; int f( A * pa) { return pa->x * pa->y; } class B : public A { int u; int g( B * pb) return pb->u * f( pb); A x y pa B A x y u pb pa

Dědičnost - polymorfismus enum Typ { T_B, T_C }; class A { public: Typ t; A * n; }; A * seznam; class B : public A { int x, y; class C : public A { int u; seznam B A t n x y C A u t n B A t n x y

Dědičnost - polymorfismus enum Typ { T_B, T_C }; class A { public: Typ t; A * n; }; A * seznam; class B : public A { int x, y; class C : public A { int u; A * pa = seznam->n->n; if ( pa->t == T_B ) { B * pb = (B *)pa; } seznam B A t n x y C A u t n B A t n x y pb pa

Násobná dědičnost class A { int x; }; class B : public A { int y; class C : public A { int z; } class D : public B, public C { int u; D * dp; // A * ap = dp; chyba: nejednoznačnost A * ap = (B *)dp; // zjednoznačnění dp = (A *)ap; // explicitní přetypování A x B A x y C A x z D B C A A x y x z u

Virtuální dědičnost class A { int x; }; class B : virtual public A { int y; class C : virtual public A { int z; } class D : public B, public C { int u; D * dp; A * ap = dp; // OK // dp = (A *)ap; chyba: nelze najít A x B A y x C A z x D B C A y z u x

Názvosloví dědičnosti Předek - potomek Parent - child Převládající názvosloví v češtině Základní - odvozená třída Base - derived class Lepší názvy, užívané normou C++ Univerzální - specializovaná třída Nejlépe odpovídá typickému použití dědičnosti Koliduje s pojmem specializace šablon Nepoužívá se

Je-li třída B odvozena z třídy A, pak: Užití dědičnosti Je-li třída B odvozena z třídy A, pak: Každý objekt typu B má všechny součásti, vlastnosti a schopnosti typu A Zděděné součásti nelze odebrat Kluzák nemůže být potomkem motorového letadla Vlastnosti a schopnosti lze skrýt, překrýt či ignorovat Skrývání či ignorování je nesystematické a nebezpečné Věznice nemá být potomkem obytného domu Překrývání jiným obsahem téže schopnosti může mít smysl Auto na plyn tankuje jinak

Nesprávné užití dědičnosti Letadlo není potomkem svého motoru Důkaz: Co když má dva motory... Násobná dědičnost? Ne: Je třeba je odlišit Tlačítko není potomkem své ikony Důkaz: Co když má dvě... Kompozice Skládání velkých objektů z malých C++: Třída s datovými položkami

Nesprávné užití dědičnosti Jezevčík umí vyhnat lišku z nory... Myslivec s jezevčíkem tedy také... Myslivec není potomkem svého jezevčíka Důkaz: Nežere granule... Kompozice? Ne: Nerodí se zároveň Tlačítko Start není potomkem MS Windows Delegace Převedení funkčnosti na jiný objekt C++: Ukazatel

Nesprávné užití dědičnosti Mlok není potomkem ryby a savce Důkaz: Nemá dvě hlavy... Virtuální dědičnost? Ne: Nekojí Tlačítko není potomkem obdélníku a textu Společný abstraktní předek Obratlovec Vizuální objekt

Ideální užití dědičnosti Objekty: Jednoduchá dědičnost Postupná specializace tříd, reprezentujících objekty Živočich-obratlovec-obojživelník-mlok Rozhraní: Virtuální násobná dědičnost Kombinování protokolů (tříd, reprezentujících rozhraní) Sjednocení množin schopností Fyzikář+Matikář C++ pro oba odlišné účely využívá tytéž mechanismy Některé jazyky tyto účely rozlišují

Metody Member Functions

Metoda = Funkce uvnitř třídy class A { Statická metoda (static member function) static int f( int a, int b); Metoda (member function) int f( int a, int b); Virtuální metoda (virtual function) virtual int f( int a, int b); Čistě virtuální metoda (pure-virtual function) virtual int f( int a, int b) = 0; }

Metoda = Funkce uvnitř třídy Efekt umístění funkce uvnitř třídy Všechny druhy Zapouzdření a přístupová práva Obyčejné, virtuální a čistě virtuální metody Vyvolání na konkrétní instanci třídy Virtuální a čistě virtuální metody Pozdní vazba (late binding) Čistě virtuální metody Definice rozhraní

Metoda = Funkce uvnitř třídy Efekt umístění funkce uvnitř třídy Zapouzdření a přístupová práva Identifikátor funkce je schován uvnitř třídy K přístupu k prvkům třídy stačí krátká jména Funkce může být chráněna proti volání zvenčí Funkce může přistupovat k chráněným prvkům třídy Tyto efekty mají všechny druhy metod U statických metod žádný další efekt není

Metoda = Funkce uvnitř třídy Globální funkce class A { public: int x, y; }; int f( A * pa) { return pa->x * pa->y; } A oa; A * pa = & oa; int v = f( pa); Statická metoda class A { public: int x, y; static int f( A * pa) { return pa->x * pa->y; } }; A oa; A * pa = & oa; int v = A::f( pa); Kvalifikované jméno třída::prvek

Metoda = Funkce uvnitř třídy Efekt umístění funkce uvnitř třídy Vyvolání na konkrétní instanci třídy Při volání funkce je explicitně či implicitně určen objekt, na kterém je funkce vyvolána Ukazatel na tento objekt je funkci předán jako skrytý parametr Uvnitř funkce je tento objekt přístupný přes ukazatel this Datové položky tohoto objektu jsou přímo přístupné Tyto efekty mají obyčejné, virtuální a čistě virtuální metody

Metoda = Funkce uvnitř třídy Statická metoda class A { public: int x, y; static int f( A * pa) { return pa->x * pa->y; } }; A oa; A * pa = & oa; int v = A::f( pa); Kvalifikované jméno třída::prvek Obyčejná metoda class A { public: int x, y; int f() { return x * y; } }; A oa; A * pa = & oa; int u = oa.f(); int v = pa->f(); Zpřístupněné jméno objekt.prvek ukazatel->prvek

Metoda = Funkce uvnitř třídy Statická metoda class A { public: int x, y; static int f( A * pa) { return pa->x * pa->y; } static int g( A * pa) return f( pa); }; Zpřístupnění prvků třídy Krátké jméno Obyčejná metoda class A { public: int x, y; int f() { return x * y; } int g() return f(); }; Zpřístupnění prvků objektu Krátké jméno Automatické použití odkazu na objekt

Metoda = Funkce uvnitř třídy Statická metoda class A { public: int x, y; static int f( A * pa) { return pa->x * pa->y; } static int g( A * pa) return f( pa); }; Obyčejná metoda class A { public: int x, y; static int f() { return x * y; } int g() return f( this); }; this

Metoda = Funkce uvnitř třídy Statická metoda class A { public: int x, y; static int f( const A * pa) { return pa->x * pa->y; } }; const A * pa; pa->f(); Obyčejná metoda class A { public: int x, y; int f() const { return x * y; } }; const A * pa; pa->f(); konstantní metoda nemodifikuje objekt, na kterém je vyvolána lze aplikovat na konstantní objekt

Metoda = Funkce uvnitř třídy Tělo uvnitř třídy class A { public: int x, y; static int f( A * pa) { return pa->x * pa->y; } }; Inline tělo vně třídy Ekvivalent těla uvnitř Kompilátor se pokusí rozvinout tělo místo volání Tělo v hlavičkovém souboru class A { public: int x, y; static int f( A * pa); }; inline int A::f( A * pa) { return pa->x * pa->y; }

Metoda = Funkce uvnitř třídy Tělo uvnitř třídy class A { public: int x, y; static int f( A * pa) { return pa->x * pa->y; } }; Tělo vně třídy Ekvivalent externí deklarace a definice globální funkce Tělo v .cpp souboru class A { public: int x, y; static int f( A * pa); }; int A::f( A * pa) { return pa->x * pa->y; }

Virtuální metody Virtual functions

Metoda = Funkce uvnitř třídy Efekt umístění funkce uvnitř třídy Pozdní vazba (late binding; virtual call) Je-li metoda nějaké třídy virtuální či čistě virtuální, pak všechny metody se stejným jménem, počtem a typy parametrů deklarované v potomcích třídy jsou považovány za různé implementace téže funkce Která implementace se vybere, tedy které tělo bude zavoláno, se rozhoduje až za běhu programu podle skutečného typu celého objektu Použije se tělo z posledního potomka, který definuje tuto funkci a je součástí celého objektu Pozdní vazba má smysl pouze u vyvolání na objektu určeném odkazem Tyto efekty mají virtuální a čistě virtuální metody

Metoda = Funkce uvnitř třídy Obyčejná metoda class A { public: int f() { return 1; } }; class B : public A { virtual int f() { return 1; } A oa; A * paa = & oa; B ob; B * pbb = & ob; A * pab = pbb; paa->f(); // A::f pbb->f(); // B::f pab->f(); // A::f Virtuální metoda class A { public: virtual int f() { return 1; } }; class B : public A { virtual int f() { return 2; } A oa; A * paa = & oa; B ob; B * pbb = & ob; A * pab = pbb; // konverze paa->f(); // A::f pbb->f(); // B::f pab->f(); // B::f

Metoda = Funkce uvnitř třídy Virtuální metoda class A { public: virtual int f() { return 1; } int g() { return f(); // pozdní vazba } }; class B : public A { virtual int f() { return 2; } Překrývání stejnojmenných metod A oa; B ob; oa.g(); // A::g, přímá vazba ob.g(); // B::g, přímá vazba-překrytí oa.f(); // A::f, přímá vazba ob.f(); // B::f, přímá vazba-překrytí B * pbb = & ob; A * paa = & oa; A * pab = pbb; // konverze paa->g(); // A::g, přímá vazba pab->g(); // A::g, přímá vazba pbb->g(); // B::g, přímá vazba-překrytí Pozdní vazba virtuálních metod paa->f(); // A::f, pozdní vazba pab->f(); // B::f, pozdní vazba pbb->f(); // B::f, pozdní vazba

Metoda = Funkce uvnitř třídy Virtuální metoda class A { public: virtual int f() { return 1; } int g() { return A::f(); // přímá vazba } }; class B : public A { virtual int f() { return 2; } Užití kvalifikovaného jména Průnik k překrytému jménu A oa; B ob; ob.A::g(); // A::g, přímá vazba ob.A::f(); // A::f, přímá vazba B * pbb = & ob; A * paa = & oa; A * pab = pbb; // konverze pbb->A::g(); // A::g, přímá vazba Zrušení pozdní vazby paa->A::f(); // A::f, přímá vazba pab->A::f(); // A::f, přímá vazba pbb->A::f(); // A::f, přímá vazba pbb->B::f(); // B::f, přímá vazba

Metoda = Funkce uvnitř třídy Princip implementace class A { public: int x; virtual int f() { return 1; } virtual int g() { return 1; } }; class B : public A { virtual int f() { return 2; } A x f A::f g A::g B A x y f B::f g

Metoda = Funkce uvnitř třídy Princip implementace class A { public: int x; virtual int f() { return 1; } virtual int g() { return 1; } }; class B : public A { virtual int f() { return 2; } A x f A::f g A::g B A y x f stub g B::f A::f this B::f this

Metoda = Funkce uvnitř třídy Čistě virtuální metoda Deklarována bez definování těla Je možné ji volat Tělo bude doplněno později u potomka Abstraktní třída Třída obsahující nějaké čistě virtuální metody (přímo či v předcích), jejichž tělo ještě nebylo definováno... Takovou třídu nelze instanciovat Lze používat ukazatele na tuto třídu a vyvolávat metody této třídy Typicky neobsahuje žádné datové položky

Příklad užití virtuálních funkcí class AbstractGraphicObject { public: virtual void paint() = 0; AbstractGraphicObject * next; }; class Scene { void paintAll() { AbstractGraphicObject * p; for ( p = first; p; p = p->next) p->paint(); } AbstractGraphicObject * first; class Circle : public AbstractGraphicObject { public: virtual void paint() { DrawCircle( x, y, r, fg); } double x, y, r; Color fg; }; class Rectangle DrawLine( x1, y1, x2, y1, fg); DrawLine( x2, y1, x2, y2, fg); DrawLine( x2, y2, x1, y2, fg); DrawLine( x1, y2, x1, y1, fg); double x1, y1, x2, y2;

Příklad užití virtuálních funkcí class AbstractGraphicObject { public: virtual void paint() = 0; AbstractGraphicObject * next; }; class SingleColorObject : public AbstractGraphicObject { void setFg( Color fg) { _fg = fg; } Color getFg() const return _fg; private: Color _fg; class Circle : public SingleColorObject { public: virtual void paint() { DrawCircle( x, y, r, getFg()); } double x, y, r; }; class Rectangle DrawLine( x1, y1, x2, y1, getFg()); DrawLine( x2, y1, x2, y2, getFg()); DrawLine( x2, y2, x1, y2, getFg()); DrawLine( x1, y2, x1, y1, getFg()); double x1, y1, x2, y2;

Příklad užití virtuálních funkcí class AbstractGraphicObject { public: virtual void paint() = 0; AbstractGraphicObject * next; }; class SingleColorObject : public AbstractGraphicObject { void setFg( Color fg); Color getFg() const; private: Color _fg; class Rectangle : public SingleColorObject { virtual void paint(); double x1, y1, x2, y2; class FilledRectangle : public Rectangle { public: virtual void paint() { DrawRectangle( x1, y1, x2, y2, bg); Rectangle::paint(); } Color bg; };

Příklad užití virtuálních funkcí AbstractGraphicObject AbstractGraphicObject SingleColorObject SingleColorObject Rectangle Circle Rectangle Circle FilledRectangle DoubleColorObject FilledRectangle

Příklad užití virtuálních funkcí AbstractGraphicObject AbstractGraphicObject SingleColorObject SingleColorObject Rectangle Circle Rectangle Circle DoubleColorObject DoubleColorObject FilledRectangle FilledRectangle FilledCircle

Příklad užití virtuálních funkcí AbstractGraphicObject AbstractGraphicObject SingleColorObject SingleColorObject BaseRect BaseCirc Rectangle Circle DoubleColorObject Rectangle Circle FilledRectangle FilledCircle DoubleColorObject BaseFRect BaseFCirc FilledRectangle FilledCircle

Příklad užití virtuálních funkcí class BaseRect { public: void doPaint( Color fg); double x1, y1, x2, y2; }; class BaseFRect : public BaseRect { void doPaint( Color fg, Color bg); { DrawRectangle( x1, y1, x2, y2, bg); BaseRect::doPaint( fg); } class Rectangle : public SingleColorObject, public BaseRect { public: virtual void paint() { doPaint( getFg()); } }; class FilledRectangle : public DoubleColorObject, public BaseFRect { doPaint( getFg(), getBg());

Příklad užití virtuálních funkcí class BaseRect { public: void doPaint( Color fg); double x1, y1, x2, y2; }; class BaseFRect : public BaseRect { void doPaint( Color fg, Color bg); { DrawRectangle( x1, y1, x2, y2, bg); BaseRect::doPaint( fg); } class Rectangle : public SingleColorObject { public: virtual void paint() r.doPaint( getFg()); } BaseRect r; }; class FilledRectangle : public DoubleColorObject r.doPaint( getFg(), getBg()); BaseFRect r;

Příklad užití virtuálních funkcí class AbstractGraphicObject { public: virtual void paint() = 0; AbstractGraphicObject * next; }; class SingleColorObject : public AbstractGraphicObject { void setFg( Color fg) { _fg = fg; } Color getFg() const return _fg; private: Color _fg; class Scene { public: void paintAll() { AbstractGraphicObject * p; for ( p = first; p; p = p->next) p->paint(); } void changeFg( Color fg) if ( /* ??? */ ) { SingleColorObject * p2 = (SingleColorObject *)p; p2->setFg( fg); AbstractGraphicObject * first; };

Příklad užití virtuálních funkcí class AbstractGraphicObject { public: virtual void paint() = 0; virtual void setFg( Color fg) { } virtual Color getFg() const return C_BLACK; AbstractGraphicObject * next; }; class SingleColorObject : public AbstractGraphicObject { virtual void setFg( Color fg); virtual Color getFg() const; private: Color _fg; class Scene { public: void paintAll() { AbstractGraphicObject * p; for ( p = first; p; p = p->next) p->paint(); } void changeFg( Color fg) p->setFg( fg); AbstractGraphicObject * first; };

Příklad užití virtuálních funkcí class SingleColorObject; class AbstractGraphicObject { public: virtual void paint() = 0; virtual SingleColorObject * getSingleColor() { return 0; } AbstractGraphicObject * next; }; class SingleColorObject : public AbstractGraphicObject { { return this; void setFg( Color fg); Color getFg() const; private: Color _fg; class Scene { public: void paintAll() { AbstractGraphicObject * p; for ( p = first; p; p = p->next) p->paint(); } void changeFg( Color fg) SingleColorObject * p2 = p->getSingleColor(); if ( p2 ) p2->setFg( fg); AbstractGraphicObject * first; };

Příklad užití virtuálních funkcí Různé významy užití dědičnosti Rozšíření požadované funkčnosti rozhraní Implementace požadované funkčnosti Rozšíření implementované funkčnosti Využití k implementaci AbstractGraphicObject SingleColorObject BaseRect BaseCirc Rectangle Circle DoubleColorObject BaseFRect BaseFCirc FilledRectangle FilledCircle

Reference, přetěžování funkcí Pomůcky Reference, přetěžování funkcí

Významné vlastnosti jazyka C++ Run-time: Vlastnosti C++ s reprezentací za běhu programu Dědičnost, násobná dědičnost (multiple inheritance) Virtuální dědičnost (virtual inheritance) Virtuální metody (virtual functions) Relativní ukazatelé (member-pointers) Zpracování výjimek (exception handling) Typová informace za běhu (RTTI) Syntactic sugar: Vlastnosti C++ bez reprezentace za běhu Pomůcky (reference, implicitní parametry, přetěžování funkcí) Pro zapomnětlivé (konstruktory, přesnější typová kontrola) Proti šťouralům (zapouzdření, ochrana přístupu, prostory jmen) Pro estéty (přetěžování operátorů, uživatelské konverze) Pro líné (šablony) Knihovny (STL)

C++ definuje vedle deklarátoru ukazatele * deklaráror reference & Z hlediska strojového kódu jsou oba deklarátory ekvivalentní - ukazatel i reference na objekt jsou reprezentovány adresou tohoto objektu - odlišné je však jejich používání ve zdrojovém textu. Deklarátor reference je možno aplikovat na libovolný typ. Výsledný typ je však možno použít jenom jedním z následujících způsobů: Typ proměnné Typ položky třídy Typ parametru funkce Typ návratové hodnoty funkce Výraz typu reference má hodnotu objektu, na nějž reference odkazuje. Tento objekt se určuje při inicializaci reference a odkaz na něj trvá po dobu platnosti referenční proměnné (nebo objektu, jehož je reference součástí). Identita reference a objektu, na který reference odkazuje, je vyjádřena implicitní typovou konverzí T <=> T &

Reference int data; int * ukazatel = & data; data2 = * ukazatel; // data2 = data ukazatel = & data2; void f( int * u) { * u = 2; } f( & data); int * g() { return & data; * g() = 3; // data = 3 int data; int & reference = data; reference = 2; // data = 2 int data2; data2 = reference; // data2 = data // NELZE: & reference = & data2; void f( int & u) { u = 2; } f( data); int & g() { return data; g() = 3; // data = 3

Novinky související s existencí reference Inicializaci nelze nahradit přiřazením Nelze rozlišit parametry předávané hodnotou a odkazem Návratová hodnota funkce může být l-hodnota Zvýšené nebezpečí nekorektních konstrukcí int & f() { int x; return x; } Nulovou referenci je obtížnější vytvořit i testovat int * p = 0; int & r = * p; if ( ! & r ) // ...

Implicitní hodnoty parametrů funkcí C++ dovoluje definování implicitní hodnoty parametrů Definovány musí být pro několik posledních parametrů Definují se u hlavičky funkce U těla funkce se neopakují Implicitní hodnoty řeší kompilátor pouze na straně volání Volaná funkce nezjistí, zda jsou hodnoty parametrů určeny explicitně nebo implicitně Implicitní hodnoty se vyhodnocují jakoby v místě definice Nemohou se odvolávat na lokální proměnné ani předchozí argumenty void f( int a, int b, int c = 7, int d = 26); f( 1, 2, 3, 4); f( 1, 2, 3); // f( 1, 2, 3, 26); f( 1, 2); // f( 1, 2, 7, 26);

Podmínkou je odlišnost v počtu a/nebo typech parametrů Přetěžování funkcí C++ dovoluje existenci více funkcí téhož jména ve stejné oblasti platnosti Podmínkou je odlišnost v počtu a/nebo typech parametrů Odlišnost typu návratové hodnoty nestačí Při volání funkce se konkrétní varianta určuje takto: Vyberou se aplikovatelné varianty funkce podle počtu a typu skutečných parametrů Přitom hrají roli implicitní hodnoty parametrů Určí se ceny typových konverzí parametrů, zjednodušeně: Uživatelská konverze / ztrátová aritmetická konverze jsou nejdražší Konverze potomek -> předek / aritmetická konverze na větší typ Konverze non-const -> const / typ <-> reference jsou nejlevnější Vybere se nejlacinější aplikovatelná varianta Pokud je jich více, kompilátor ohlásí chybu

Přetěžování funkcí int min( int a, int b) { return a < b ? a : b; } double min( double a, double b) min( 1, 2); // min( int, int) - přesná shoda min( 1, 2.0); // min( double, double) - levnější varianta min( 1.0, 2); // min( double, double) - levnější varianta min( 1.0, 2.0); // min( double, double) - přesná shoda void f( int, double); void f( double, int); f( 1, 2); // chyba: obě varianty jsou stejně drahé f( 1.0, 2.0); // chyba: obě varianty jsou stejně drahé

Konstruktory a destruktory Constructors and Destructors

Konstruktory a destruktory Konstruktor třídy XXX je metoda se jménem XXX Typ návratové hodnoty se neurčuje Konstruktorů může být více, liší se parametry Nesmí být virtuální Konstruktor je volán vždy, když vzniká objekt typu XXX Parametry se zadávají při vzniku objektu Některé z konstruktorů mají speciální význam Některé z konstruktorů může generovat sám kompilátor Konstruktor nelze vyvolat přímo Destruktor třídy je metoda se jménem ~XXX Nesmí mít parametry ani návratovou hodnotu Může být virtuální Destruktor je volán vždy, když zaniká objekt typu XXX Destruktor může generovat sám kompilátor Destruktor lze vyvolat přímo pouze speciální syntaxí

Vznik a zánik objektů Lokální proměnné Pro každý lokální objekt je při jeho vzniku, to jest při průchodu řízení jeho deklarací, vyvolán specifikovaný konstruktor. Při zániku lokálního objektu, to jest při opuštění bloku s jeho deklarací (jakýmkoli způsobem včetně příkazů return, break, continue a goto nebo následkem výjimky) je vyvolán jeho destruktor. Deklarace lokálního objektu může být kdekoliv uvnitř těla funkce nebo kteréhokoliv složeného příkazu. Rozsah platnosti objektu je od místa deklarace po konec nejbližšího složeného příkazu. Skoky, které by vstupovaly do bloku a obcházely přitom deklaraci objektu, jsou zakázány. void f() {  XXX a, b; // konstruktor bez parametrů XXX c( 1), d( a); // konstruktory XXX( int), XXX( XXX) XXX e = 1, f = a; // ekvivalentní zápis XXX g( 1, 2, 3); // konstruktor XXX( int, int, int) }

Parametry předávané hodnotou Vznik a zánik objektů Parametry předávané hodnotou Předání parametru je realizováno voláním konstruktoru s jedním parametrem Tento konstruktor je volán na místě volání funkce před jejím zavoláním Kompilátor dokáže tento konstruktor vytvořit vlastními prostředky Destruktor objektu je vyvolán před návratem z funkce void f( XXX a) {  } void g() { XXX b; f( b); // konstruktor XXX( const XXX &) f( 1); // možné, pokud existuje konstruktor XXX( int)

Vznik a zánik objektů Globální proměnné Pro každý globální objekt (a každý lokální statický objekt či statickou položku třídy) je vyvolán konstruktor (pokud je neprázdný) před vstupem řízení do funkce main Po jejím opuštění (nebo po zavolání funkce exit) je pro každý globální objekt vyvolán destruktor. Pořadí vyvolávání je implementačně závislé. XXX a( 1), b, c( 2, 3, 4); XXX d( a); // při vyvolání konstruktoru d nemusí být a inicializováno! f() { static XXX e( 1); // lokální statická proměnná } class C { static XXX h; // statická datová položka - deklarace }; XXX C::h( 1, 2, 3); // statická datová položka - definice

Ukládací třídy (storage classes) - data globální uvnitř funkce uvnitř třídy (C++) bez specifikace statická alokace, export mimo modul viz auto součást každé instance třídy auto zakázáno zásobník, případně registr register zásobník, přednostně v registru static statická alokace, bez exportu mimo modul statická alokace (jediná instance) statická alokace (jediná instance), export mimo modul extern odkaz na globální definici, bez alokace

Ukládací třídy (storage classes) - funkce globální uvnitř funkce uvnitř třídy (C++) bez specifikace export mimo modul zakázáno metoda - implicitní parametr this virtual (C++) virtuální metoda static bez exportu mimo modul bez this, export mimo modul extern odkaz na globální definici

Dynamicky alokované objekty Vznik a zánik objektů Dynamicky alokované objekty Pro dynamickou alokaci slouží dvojice operátorů new a delete. Knihovní funkce malloc a free s nimi není možno kombinovat a neměly by být s výjimkou odůvodněných případů v C++ používány. Operátor new alokuje potřebnou oblast paměti pro objekt specifikovaného typu a vyvolává konstruktor podle zadaných parametrů. Vrací ukazatel na tento objekt. Pokud se z důvodu nedostatku paměti alokace nezdaří: C++ 98: Vyvolá se výjimka std::bad_alloc Starší C++: Konstruktor se nevyvolává a operátor vrací nulový ukazatel Operátor delete vyvolává destruktor objektu a poté dealokuje paměť zabranou objektem. Je odolný proti nulovým ukazatelům XXX * p, * q; p = new XXX; // konstruktor bez parametrů q = new XXX( 1, p); // XXX( int, XXX *) /* ... */ delete p; delete q;

Dynamická alokace polí Vznik a zánik objektů Dynamická alokace polí Vyvolává pouze konstruktory bez parametrů int n; XXX * p; p = new XXX[ n]; // pole n objektů typu XXX XXX * q; q = new XXX *[ n]; // pole n ukazatelů na XXX /* ... */ delete[] p; delete[] q;

Užití jména třídy jako jména funkce v operátoru volání způsobí: Vznik a zánik objektů Dočasné objekty Užití jména třídy jako jména funkce v operátoru volání způsobí: Vyhrazení místa pro tuto třídu na zásobníku, tedy mezi okolními lokálními proměnnými Vyvolání konstruktoru s patřičnými parametry na tomto objektu Použití tohoto objektu jako hodnoty v okolním výraze Vyvolání destruktoru nejpozději na konci příkazu XXX a, b; a = XXX(); // konstruktor bez parametrů + kopie + destruktor b = XXX( 1, 2); // konstruktor s parametry + kopie + destruktor Speciální případ této syntaxe je chápán jako typová konverze (function-style cast) Konstruktor s jedním parametrem je nazýván konverzní konstruktor

Konstruktory a dědičnost Při vytvoření jakékoliv instance třídy je zavolán její konstruktor, při zrušení destruktor. Toto tvrzení platí i pro instance tříd, které jsou součástí jiných, to znamená předky tříd a položky, které jsou třídami. Vyvolání konstruktorů a destruktorů součástí třídy zajistí automaticky kompilátor přidáním příslušného kódu do těla konstruktoru či destruktoru třídy. Konstrukce součástí se děje před konstrukcí celku, destrukce součástí po destrukci celku. Má-li konstrukce součástí probíhat jinými, než konstruktory bez parametrů, musí být jejich parametry specifikovány ve zvláštní sekci před tělem konstruktoru. class X : public Y, public Z { T a, b; X( int p) : Y( p+1, 7), a( p+2) { } };

Konstruktor bez parametrů (default constructor) Speciální metody tříd Konstruktor bez parametrů (default constructor) XXX(); Používán u proměnných bez inicializace Kompilátor se jej pokusí vygenerovat, je-li to třeba a pokud třída nemá vůbec žádný konstruktor: Položky, které nejsou třídami, nejsou generovaným konstruktorem inicializovány Generovaný konstruktor volá konstruktor bez parametrů na všechny předky a položky To nemusí jít např. pro neexistenci takového konstruktoru Kopírovací konstruktor (copy constructor) XXX( const XXX &); Používán pro předávání parametrů a návratových hodnot Kompilátor se jej pokusí vygenerovat, je-li to třeba a pokud třída kopírovací konstruktor nemá: Položky, které nejsou třídami, jsou kopírovány Na předky a položky se volá kopírovací konstruktor To nemusí jít kvůli ochraně přístupu

Operátor přiřazení (assignment operator) Speciální metody tříd Operátor přiřazení (assignment operator) const XXX & operator=( const XXX &); Implementace operátoru = pro typ XXX na levé straně Kompilátor se jej pokusí vygenerovat, je-li to třeba a třída jej nemá: Položky, které nejsou třídami, jsou kopírovány Na předky a položky se volá operátor přiřazení To nemusí jít kvůli ochraně přístupu Destruktor ~XXX(); Používán při zániku objektu Kompilátor se jej pokusí vygenerovat, je-li to třeba a třída jej nemá Pokud je objekt destruován operátorem delete aplikovaným na ukazatel na předka, musí být destruktor v tomto předku deklarován jako virtuální virtual ~XXX();

Speciální metody tříd class UUU { public: virtual ~UUU(); }; class XXX : public UUU { virtual ~XXX(); XXX * px = new XXX; // konverze potomek-předek UUU * pu = px; delete pu; Pokud je objekt destruován operátorem delete aplikovaným na ukazatel na předka, musí být destruktor v tomto předku deklarován jako virtuální

Konverzní konstruktory Speciální metody tříd Konverzní konstruktory class XXX { XXX( YYY); }; Zobecnění kopírovacího konstruktoru Definuje uživatelskou konverzi typu YYY na XXX Je-li tento speciální efekt nežádoucí, lze jej zrušit: explicit XXX( YYY); Konverzní operátory operator YYY() const; Definuje uživatelskou konverzi typu XXX na YYY Vrací typ YYY hodnotou (tedy s použitím kopírovacího konstruktoru YYY, pokud je YYY třída) Kompilátor vždy použije nejvýše jednu uživatelskou konverzi

Třída jako datový typ class String { public: // Default constructor } // Destructor ~String() { destroy(); // Copy constructor String( const String & str) { fill( str.s); // Operator = const String & operator = ( const String & str) fill( str.s); return *this; // Conversion constructor String( const char * _s) { fill( _s); } // Conversion assignment const String & operator=( const char * _s) { destroy(); fill( _s); return * this; // Extraction method const char * get() const { return s ? s : ""; }   private: // Data char * s; // Pomocné funkce void fill( const char * str); void destroy(); };

Třída jako datový typ void String::fill( const char * str) { int len = str ? strlen( str) : 0;   if ( len ) if ( ! (s = new char[ len + 1]) ) ERROR(); else strcpy( s, str); s = 0; } void String::destroy() { if ( s ) { delete[] s; s = 0; }

Vztah virtuálních funkcí a konstruktorů Náplní práce konstruktoru je rovněž vyplnění skrytých položek třidy, zajišťujících funkci virtuálních metod. Toto vyplňování se děje až po konstrukci součástí třídy, což znamená, že v době vyvolání konstruktoru součásti se virtuální funkce vyvolávají tak, jako by tato součást byla sama celkem. Analogický mechanismus funguje i při destrukci objektů.

Vztah virtuálních funkcí a konstruktorů class A { public: virtual void f(); A() { f(); /* A::f */ }; ~A() { void g() { f(); /* A/B::f */ class B : public A { public: virtual void f(); B() { /* A::A */ f(); /* B::f */ }; ~B() { }; /* A::~A */ void g() {

private/protected/public Ochranné prostředky private/protected/public

Položky tříd jsou rozděleny do tří skupin: Ochrana přístupu Položky tříd jsou rozděleny do tří skupin: public: Veřejně přístupné položky protected: Položky přístupné metodám třídy a metodám tříd z této třídy odvozených private: Položky přístupné pouze metodám této třídy Implicitní nastavení: class: private struct, union: public Při dědění je možno přístupová práva dále omezit: public: Práva zůstávají beze změn private: Všechny položky předka jsou v nové třídě privátní

namespace Konstrukce namespace umožňuje uzavřít několik deklarací typů, tříd, globálních proměnných a funkcí do zvláštního prostoru jmen Konstrukci namespace lze otevírat vícenásobně namespace X { typedef char * ptr; ptr f( ptr a, ptr b); }; ptr g(); Uzavřené identifikátory lze mimo tento prostor referencovat kvalifikovaným jménem X::ptr v = X::g(); Celý prostor jmen lze rozbalit do aktuálního bloku nebo modulu konstrukcí: using namespace X;

Oblasti platnosti identifikátorů Modul/namespace: Funkce, proměnné, typy a výčtové konstanty Třída: Metody, položky (příp. typy a výčtové konstanty) Funkce (blok): Parametry a lokální proměnné (příp. typy a výčtové konstanty) Pořadí vyhledávání identifikátoru Nejprve ve funkci: Od nejvnitřnějšího k hlavnímu bloku Poté v třídách (uvnitř metod): Od potomků k předkům Nakonec v modulu resp. aktuálním namespace S uplatněním direktiv using

Vztah vyhledávání identifikátoru a kontroly přístupu Ochrana přístupu Vztah vyhledávání identifikátoru a kontroly přístupu V definovaném pořadí se najde první oblast platnosti, ve které se identifikátor vyskytuje Namespace slité direktivami using se považují za jednu oblast platnosti Jedná-li se o přetížený identifikátor funkce, vybere se v nalezené oblasti odpovídající varianta Neuplatní se tudíž jiné varianty v jiných oblastech, které by jinak byly viditelné Aplikuje se mechanismus ochrany přístupových práv. Pokud tedy nejlépe padnoucí varianta není přístupná, kompilátor ohlásí chybu bez ohledu na případnou existenci jiné aplikovatelné a přístupné varianty

Přetěžování operátorů Operator overloading

Přetěžování operátorů Většinu operátorů jazyka C++ lze definovat pro uživatelské datové typy. Nelze předefinovat tyto operátory: .  .*  ::   ? :  sizeof Alespoň jeden z operandů musí být třída nebo výčtový typ nebo reference na ně Nelze tudíž předefinovat operace na číselných typech a ukazatelích Předefinováním nelze měnit prioritu a asociativitu operátorů Pro předefinované operátory nemusí platit identity definované pro základní typy, např.: ++a nemusí být ekvivalentní a=a+1 a[b] nemusí být ekvivalentní *(a+b) ani b[a] Pro předefinované operátory && a || neplatí pravidla o zkráceném vyhodnocování

Přetěžování operátorů Typy skutečných operandů předefinovaného operátoru nemusejí přesně odpovídat typům formálních parametrů operátoru. Pro výběr správné varianty mezi předefinovanými operátory platí stejná pravidla, jako pro přetížené funkce. Předefinování operátorů se provádí definováním metody se speciálním jménem operatorxxx ve třídě (prvního operandu), pro kterou má být operátor definován. Některé operátory je možno definovat i jako globální funkce s týmž speciálním jménem. Speciální jméno je možno používat i pro explicitní vyvolání této metody či funkce. Operátory, které jsou metodami, jsou s výjimkou operátoru přiřazení dědičné a smějí být virtuální.

Přetěžování operátorů - Binární operátory Binární operátor xxx z množiny + - * / % << >> < > <= >= <<= >>= ^ & | && || == != += -= *= /= %= ^= &= |= ->* lze pro operandy typu B a C předefinovat dvěma způsoby: Globální funkcí A operator xxx( B, C) A operator xxx( B &, C &) A operator xxx( const B &, const C &) Metodou A B::operator xxx( C) A B::operator xxx( const C &) A B::operator xxx( const C &) const Binární operátor [ ] lze předefinovat pouze metodou A B::operator []( C) A B::operator []( C &) A B::operator []( const C &) const

Přetěžování operátorů - Unární operátory Unární operátor xxx z množiny + - * & ~ ! a prefixové operátory ++ -- lze pro operand typu B předefinovat dvěma způsoby: Globální funkcí A operator xxx( B) A operator xxx( B &) A operator xxx( const B &) Metodou A B::operator xxx() A B::operator xxx() const

Přetěžování operátorů - Unární operátory Postfixové operátory ++ a -- lze pro operand typu B předefinovat dvěma způsoby: Globální funkcí A operator xxx( B, int) A operator xxx( B &, int) A operator xxx( const B &, int) Metodou A B::operator xxx( int) A B::operator xxx( int) const

Přetěžování operátorů - Unární operátory je považován za unární operátor a jeho návratovou hodnotou musí být buďto ukazatel na třídu s uvedenou položkou, nebo objekt či referenci na objekt, pro který je znovu definován operátor ->

Přetěžování operátorů - Unární operátory Operátor volání funkce () smí být definován pouze jako metoda třídy a umožňuje používat objekty této třídy jako funkce. Smí mít libovolný počet parametrů a pro výběr konkrétní varianty operátoru se použije podobný mechanismus, jako pro přetížené funkce.

mohou být definovány jako globální nebo uvnitř třídy Operátory new a delete Operátory new a delete mohou být definovány jako globální nebo uvnitř třídy na rozdíl od ostatních operátorů jsou i uvnitř třídy považovány za statické funkce Operátory deklarované uvnitř třídy jsou použity při alokaci resp. dealokaci objektů této třídy (a jejích potomků) Globální operátory jsou používány pro třídy bez těchto operátorů a pro datové typy, které nejsou třídami, včetně polí tříd void * operator new( size_t s); void operator delete( void * p);

Operátor new může mít přídavné parametry a může tak být přetížen Operátory new a delete Operátor new může mít přídavné parametry a může tak být přetížen Typické použití void * operator new( size_t s, void * p) { return p; } void call_constructor( X * p, int param) { new( p) X( param); Vztah operátoru new a konstruktoru X * p = new X(a,b,c); (nekorektní) ekvivalent X * p = (X*)X::operator new(sizeof(X)); if (p) p->X::X(a,b,c); // zakázáno Vztah operátoru delete a destruktoru delete p; ekvivalent if (p) p->X::~X(); // povoleno X::operator delete((void*)p);

Šablony Templates

Šablony tříd - příklad Definice Použití template< int n, class T> class Array { T p[ n]; T dummy; public: T & operator[]( int x) { return x<n ? p[x] : dummy; } }; Použití Array< 5, int> a; Array< 7, int> b; Array< 5 + 2, int> c; Array< 3, Array< 7, int> > d; a[ 3] = b[ 3]; a = b; // chyba !!! b = c; // OK, implicitní copy-constructor d[ 2][ 3] = 1;

Šablony tříd - definice Šablona je generická třída parametrizovaná libovolným počtem formálních parametrů těchto druhů: celé číslo – uvnitř šablony se chová jako konstanta, použitelná jako meze polí ukazatel libovolného typu libovolný typ – deklarováno zápisem class T, identifikátor formálního parametru se chová jako identifikátor typu, použitelný uvnitř šablony v libovolné deklaraci Prefix definice šablony template< formální-parametry> lze použít před několika formami deklarací; oblastí platnosti formálních parametrů je celá prefixovaná deklarace

Šablony tříd - instanciace Instanciace šablony: Šablonu lze použít jako typ pouze s explicitním uvedením skutečných parametrů odpovídajících druhů: celé číslo: celočíselný konstantní výraz ukazatel: adresa globální nebo statické proměnné či funkce kompatibilního typu libovolný typ – jméno typu či typová konstrukce (včetně jiné instanciované šablony) Užití instanciované šablony: Instanciované šablony jsou stejného typu, pokud jsou stejného jména a jejich skutečné parametry obsahují stejné hodnoty konstantních výrazů, adresy stejných proměnných či funkcí a stejné typy

Šablony tříd – pravidla použití Uvnitř těla šablony (nebo jako její předky) je možno užívat libovolné typy včetně instanciace šablon. Všechny instanciace včetně odkazu na právě definovanou šablonu musejí mít uvedeny skutečné parametry (jimiž mohou být formální parametry právě definované šablony) Jediné použití jména šablony bez parametrů je ve jméně konstruktorů a destruktoru šablony Typická šablona s copy-constructorem: template< class T> class X { X( const X< T> &) };

Šablony tříd – pravidla použití Metody šablon mohou mít těla uvnitř třídy nebo vně Vně uvedená těla metod musejí být připojena k šabloně takto: template< class T> void X< T>::f( int a, int b) { /* ... */ } Všechna těla metod musejí být viditelná v okamžiku instanciace šablony, musejí tedy typicky být v témže hlavičkovém souboru. Uvedení těla metody vně tedy u šablon typicky nic nepřináší, může být však vynuceno rekurzivními odkazy mezi šablonami apod.

Dopředná deklarace šablony Šablony tříd – triky Dopředná deklarace šablony template< class T> class X; /* ... */ template< class T> class X { /* ... */ }; Specializace funkce pro konkrétní typ Překrývá generické tělo template< class T> class X { void f(/*...*/); }; template< class T> void X< T>::f(/*...*/) { /*...*/} void X< int>::f(/*...*/)

Šablony tříd – explicitní instanciace Je-li předem známa množina typů (formálních parametrů), pro něž se bude šablona instanciovat, není nutno publikovat těla metod ve zdrojové formě a je možné je předkompilovat do knihovny Veřejný hlavičkový soubor X.h – hlavička třídy template< class T> class X { /* ... */ void f(/*...*/); }; Nepublikovaný hlavičkový soubor XBody.h Generická těla metod #include "X.h" template< class T> void X< T>::f(/*...*/) { /*...*/ } Knihovní modul XBodyInt.cpp Instanciace pro typ int #include "XBody.h" template X< int>;

Skutečné parametry šablony funkce se při volání neuvádějí Šablona funkce je generická globální funkce prefixovaná konstrukcí template< /*formální parametry*/> se stejnými druhy formálních parametrů šablony jako u šablon tříd Všechny formální parametry prefixu funkční šablony musejí být užity v typech formálních parametrů funkce Skutečné parametry šablony funkce se při volání neuvádějí Kompilátor je odvozuje z typů skutečných parametrů funkce Pod stejným identifikátorem může být deklarováno několik různých šablon funkce a navíc několik obyčejných funkcí. Obyčejné funkce mají přednost před generickými template< class T> T max( T a, T b) { return a < b ? b : a; }; char * max( char * a, char * b) { return strcmp( a, b) < 0 ? b : a; }; template< int n, class T> T max( Array< n, T> a) { /* ... */ }

Standardní knihovny C++

Standardní knihovny C++ V novějších implementacích má většina hlavičkových souborů dvě verze Stará konvence soubor vector.h obsahuje šablonu vector Nová konvence soubor vector obsahuje šablonu vector uzavřenou do namespace std je tedy nutné používat identifikátor std::vector Standardní knihovny C++ mají tyto hlavní součásti Základní knihovny převzaté z C, podle nové konvence v přejmenovaných souborech Rozšířené C++ knihovny iostream: Systém znakového a formátovaného vstupu a výstupu STL: Standard Template Library

Základní knihovny C a C++ <assert.h> <cassert> - ladicí funkce (makro assert) <ctype.h> <cctype> - klasifikace znaků (isalpha, isspace, ...) <errno.h> <cerrno> - chybové kódy (ENOMEM, ...), proměnná errno <float.h> <cfloat> - vlastnosti a limity reálných typů (DBL_MAX, ...) <limits.h> <limits> <climits> - limity celočíselných typů (INT_MAX, ...) <locale.h> <locale> <clocale> - přizpůsobení národnímu prostředí <math.h> <cmath> - matematické funkce (sin, ...) <setjmp.h> <csetjmp> - meziprocedurální skoky (setjmp, longjmp) <signal.h> <csignal> - signály operačního systému <stdarg.h> <cstdarg> - makra pro funkce s proměnným počtem argumentů <stddef.h> <cstddef> - užitečné typy a konstanty (NULL) <stdio.h> <cstdio> - standardní a souborový vstup a výstup <stdlib.h> <cstdlib> - užitečné funkce (malloc, ...) <string.h> <cstring> - manipulace s řetězci (strcpy, ...) <time.h> <ctime> - konverze data a času <wchar.h> <cwchar> - 16-bitové řetězce (wchar_t) <wctype.h> <cwctype> - klasifikace 16-bitových znaků

Rozšířené knihovny pro C++ <bitset.h> <bitset> -- šablona pro bitové pole (bitset<N>) <complex.h> <complex> - komplexní čísla různé přesnosti (complex<double>,...) <exception.h> <exception> - nastavení zpracování výjimek (set_unexpected,...) <stdexcept.h> <stdexcept> - standardní výjimky (overflow_error,...) <string.h> <string> - chytřejší implementace řetězce (string) <valarray.h> <valarray> - šablony různě inteligentních polí (valarray,...), vektorové operace (podpora paralelních výpočtů matematických funkcí)

iostream <fstream.h> <fstream> - souborový vstup a výstup (ifstream, ofstream, fstream) <iomanip.h> <iomanip> - manipulátory pro nastavení parametrů formátovaného vstupu a výstupu (setw, setprecision, setfill, setbase, ...) <ios.h> <ios> - základní funkce abstraktního souboru, základní nastavení formátu (hex, left, ...) <iostream.h> <iostream> - standardní vstup a výstup (cin, cout, cerr) <istream.h> <istream> - abstraktní vstupní médium (istream) <ostream.h> <ostream> - abstraktní výstupní médium (ostream) <sstream.h> <sstream> - vnitřní paměť jako médium (istringstream, ostringstream, stringstream) <strstream.h> <strstream> - vnitřní paměť jako médium (istrstream, ostrstream, strstream)

iostream #include <iostream.h> f() { int X, double Y; cin >> X >> Y; cout << "X = " << hex << setw(4) << X << ", Y = " << Y << '\n'; }

Standard Template Library STL Standard Template Library

Všechny kontejnery pracují s kopiemi vkládaných hodnot STL – Kontejnery <deque.h> <deque> - fronta s přidáváním a odebíráním z obou stran <list.h> <list> - obousměrně vázaný seznam <map.h> <map> - asociativní pole, tj. parciální zobrazení <queue.h> <queue> - fronta <set.h> <set> - množina <stack.h> <stack> - zásobník <vector.h> <vector> - vektor, tj. pole Všechny kontejnery pracují s kopiemi vkládaných hodnot Typ hodnot musí mít alespoň copy-constructor a destruktor Hodnoty se přidávají a odebírají metodami odpovídajícími druhu kontejneru K hodnotám je možno přistupovat pomocí iterátoru, který reprezentuje inteligentní ukazatel dovnitř kontejneru Prostřednictvím iterátoru je možno měnit uložené hodnoty

STL – Příklad #include <deque> typedef std::deque< int> my_deque; my_deque the_deque; the_deque.push_back( 1); the_deque.push_back( 2); the_deque.push_back( 3); int x = the_deque.pop_front(); // 1 for ( my_deque::iterator it = the_deque.begin(); it != the_deque.end(); it++) { *it = *it + 3; } int y = the_deque.pop_back(); // 6 int z = the_deque.pop_back(); // 5

STL – vector template<class T, class A = allocator<T> > class vector { public: typedef A allocator_type; typedef A::size_type size_type; typedef A::difference_type difference_type; typedef A::reference reference; typedef A::const_reference const_reference; typedef A::value_type value_type; typedef /*...*/ iterator; typedef /*...*/ const_iterator; typedef /*...*/ reverse_iterator; typedef /*...*/ const_reverse_iterator; explicit vector(const A& al = A()); explicit vector(size_type n, const T& v = T(), const A& al = A()); vector(const vector& x); vector(const_iterator first, const_iterator last, const A& al = A()); /* ... */

STL – vector /* template<class T, class A = allocator<T> > class vector { ... */ iterator begin(); const_iterator begin() const; iterator end(); iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; size_type size() const; bool empty() const; reference at(size_type pos); const_reference at(size_type pos) const; reference operator[](size_type pos); const_reference operator[](size_type pos) const; /* ... */

STL – vector /* template<class T, class A = allocator<T> > class vector { ... */ reference front(); const_reference front() const; reference back(); const_reference back() const; void push_back(const T& x); void pop_back(); void assign(const_iterator first, const_iterator last); void assign(size_type n, const T& x = T()); iterator insert(iterator it, const T& x = T()); void insert(iterator it, size_type n, const T& x); void insert(iterator it, const_iterator first, const_iterator last); iterator erase(iterator it); iterator erase(iterator first, iterator last); void clear(); void swap(vector x); /* ... */

STL – vector /* template<class T, class A = allocator<T> > class vector { ... */ protected: A allocator; };

STL – vector – naivní implementace – hlavička template<class T, int delta> class oriented_iterator; template<class T, int delta> class const_oriented_iterator; template<class T> class vector { public: typedef unsigned int size_type; typedef int difference_type; typedef T & reference; typedef const T & const_reference; typedef T value_type; typedef oriented_iterator< T, 1> iterator; typedef const_oriented_iterator< T, 1> const_iterator; typedef oriented_iterator< T, -1> reverse_iterator; typedef const_oriented_iterator< T, -1> const_reverse_iterator; /* ... */ private: size_type _n; T * _p; };

STL – vector – naivní implementace – přístup template< class T> reference vector< T>::at(size_type pos) { return pos >= 0 && pos < n ? _p[ pos] : _dummy(); } template< class T> const_reference vector< T>::at(size_type pos) const template< class T> reference vector< T>::operator[](size_type pos) { return at( pos); } template< class T> const_reference vector< T>::operator[](size_type pos) const template< class T> reference vector< T>::dummy() const { return *(T*)0; }

STL – vector – naivní implementace - konstrukce template< class T> vector< T>::vector() { _n = 0; _p = 0; } template< class T> vector< T>::vector( size_type n, const T & v) { _n = n; _p = new T[ _n]; /* nevolat konstruktory !!! */ for ( int i = 0; i < _n; i++) { /* zavolat konstruktory !!! */ /* _p[ i].T( v); */ } template< class T> vector< T>::vector( const vector & x) _n = x._n; /* _p[ i].T( x._p[ i]); */

STL – vector – naivní implementace – push/pop template< class T> void vector< T>::push_back(const T& x) { T * p = _p; _n = _n + 1; _p = new T[ _n + 1]; /* nevolat konstruktory !!! */ for ( int i = 0; i < _n - 1; i++) { /* zavolat konstruktory !!! */ /* _p[ i].T( p[ i]); */ } template< class T> void vector< T>::pop_back() /* ... */

STL – vector – vylepšená implementace - konstrukce void * operator new( size_t s, void * p) { return p; } template< class T> vector< T>::vector( size_type n, const T & v) { _n = n; _p = reinterpret_cast< T *>( new char[ _n * sizeof( T)]); for ( int i = 0; i < _n; i++) new( _p[ i]) T( v); } template< class T> vector< T>::~vector() _p[ i]->T::~T(); delete[] reinterpret_cast< char *>( _p);

STL – vector – naivní implementace – iterátory template< class T, int delta> class oriented_iterator { public: T & operator *() const { return _i < _v->_n ? *( T *)0 : _v->_p[ _i]; } const oriented_iterator< T, delta> & operator ++() /* prefix */ { if ( _i < _v->_n ) _i++; return * this; oriented_iterator< T, delta> operator ++( int) /* postfix */ { oriented_iterator< T, delta> old = * this; operator ++(); return old; bool operator ==( const oriented_iterator< T, delta> & b) const { return _i == _b->_i; } private: friend class vector< T>; oriented_iterator( vector< T> * v, int i) { _v = v; _i = i; } vector< T> * _v; int _i; };

STL – vector – naivní implementace – iterátory template< class T> iterator vector< T>::begin() { return iterator( this, 0); } template< class T> const_iterator vector< T>::begin() const { return const_iterator( this, 0); } template< class T> iterator vector< T>::end() { return iterator( this, _n); } template< class T> iterator vector< T>::end() const { return const_iterator( this, _n); }

STL – Ostatní <algorithm.h> <algorithm> - užitečné algoritmy (sort, next_permutation, ...) <functional.h> <functional> - podpora funktorů <iterator.h> <iterator> - podpora iterátorů <memory.h> <memory> - alokátory pro konterjnery <numeric.h> <numeric> - jednoduchá matematika na prvcích kontejnerů <utility.h> <utility> - pomocné konstrukce (pair,...)

Nepoužité slajdy

Preprocesor a překlad stdio.h hello.o conio.h hello.i hello.c Code ... push call ret Import _printf _getch Export _main Data ‘H’,’e’,’l’,’l’, ’o’,10,0 hello.o hello.i /*...*/ int printf( const char *, ...); int getch(); int putch(); int main( int argc, char * * argv) { printf( “Hello\n”); getch(); return 0; } stdio.h hello.c #include <stdio.h> #include <conio.h> conio.h

Spojování modulů cstart.o printer.o hello.o creader.o exit.o Code ... push call ret Import _printf _getch Export _main Data ‘H’,’e’,’l’,’l’, ’o’,10,0 hello.o _exit entry point cstart.o printer.o creader.o syscall ... exit.o

Spustitelný program a spuštěný proces ... push call ret ‘H’,’e’,’l’,’l’, ’o’,10,0 hello entry point syscall ... Code Data 00000000 Heap Stack Adresový prostor IP R0 R1 FFFFFFFF

***** C++ ***** Identifikátory a kontexty Zapouzdření Dědičnost Přetěžování funkcí Předefinování operátorů Objekty Konstruktory a destruktory Pozdní vazba (late binding) Virtuální funkce Neviditelné konverze Drobná vylepšení Striktnější typová kontrola oproti C new a delete Komentáře Šablony Zpracování výjimek Knihovny Streams

Virtuální funkce – Specializace datové struktury class HashTable { public: void add( const void * key, int keylen, const void * data, int datalen); protected: virtual long hash( int keylen); private: SmartArray _tbl; }; class StringTable : public HashTable { public: void add( const char * key, const void * data, int datalen); protected: virtual long hash( const void * key, int keylen); };

Virtuální funkce – Užití datové struktury class Catalog : private StringTable { public: void add( const char * isbn, const char * author, const char * title); private: virtual long hash( const void * key, int keylen); }; class Catalog { public: void add( const char * isbn, const char * author, const char * title); private: StringTable _t; };

Virtuální funkce - Řešení v C-stylu Abstraktní třída struct File { int handle; int (* readf)( File * p); void (* writef)( File * p, int x); }; int read( File * p) { return p->readf( p); }    Specializace   int bread( File * p) { /* ... */ } File * bopen() { File * p = new File; p->handle = /*...*/; p->readf = bread; p->writef = bwrite; return p; }   

Virtuální funkce - Řešení v C-stylu Jiná specializace struct TFile { File f; int info; };    int tread( File * p) { TFile * tp = (TFile *)p; /* ... */ } File * topen() { TFile * tp = new TFile; tp->f.handle = /* ... */; tp->f.readf = tread; tp->f.writef = twrite; tp->info = /* ... */ return &tp->f;  

Virtuální funkce - Řešení v C-stylu Lepší implementace struct VTFile { int (* readf)( File * p); void (* writef)( File * p, int x); }; struct File { int handle; const VTFile * vt; const VTFile VTBFile = { bread, bwrite }; const VTFile VTTFile = { tread, twrite };  

Virtuální funkce - Řešení v C++ Abstraktní třída class File { int handle; public: virtual int readf() = 0; virtual void writef( int x) = 0; }; int read( File * p) { return p->readf(); } Specializace class BFile : public File { virtual int readf(); virtual void writef( int x); }; int BFile::readf() { /* ... */ } File * bopen() { BFile * p = new BFile; p->handle = /* ... */; return p;

Virtuální funkce - Řešení v C++ Jiná specializace class TFile : public File { int info; virtual int readf(); virtual void writef( int x); }; int TFile::readf() { /* ... */ } File * topen() { TFile * p = new TFile; p->handle = /* ... */; p->info = /* ... */; return p; }

Statické a dynamické volání virtuálních metod void x() { A a; /* A::A */ B b; /* B::B */ A & raa = a; A & rab = b; B & rbb = b; A * paa = &a; A * pab = &b; B * pbb = &b; a.f(); /* A::f (c) */ b.f(); /* B::f (c) */ raa.f(); /* A::f (r) */ rab.f(); /* B::f (r) */ rbb.f(); /* B::f (r) */ paa->f(); /* A::f (r) */ pab->f(); /* B::f (r) */ pbb->f(); /* B::f (r) */ b.A::f(); /* A::f (c) */ b.B::f(); /* B::f (c) */ paa->A::f(); /* A::f (c) */ pab->A::f(); /* A::f (c) */ pbb->A::f(); /* A::f (c) */ pbb->B::f(); /* B::f (c) */ raa.A::f(); /* A::f (c) */ rab.A::f(); /* A::f (c) */ rbb.A::f(); /* A::f (c) */ rbb.B::f(); /* B::f (c) */ }

Vazba programu na operační systém Moderní technologie - Userland services Část funkcí OS je vykonávána jinými procesy Tyto procesy jsou z pohledu jádra OS uživatelské Jádro zajišťuje komunikaci (RPC apod.) Z pohledu C-API nemusí být odlišitelné od služeb jádra Mohou však mít C nebo C++ rozhraní vyšší úrovně Specializace univerzálních prostředků RPC, COM+ Mohou vyžadovat speciální formu užívajícího programu Předepsané formy tříd užívaných v rozhraní IDL (užití definice rozhraní v jiném jazyce při kompilaci) Registrace komponenty při instalaci programu (GUID apod.)

Meziprocesová komunikace Synchronizace Semafory Vzájemné vyloučení (mutexes) Události (events) Předávání dat Sdílená paměť Synchronizace + předávání dat Zprávy (messages; UDP) Kanály, roury (sockets; TCP) Vzdálená volání (Remote Procedure Call) nejbližší logice programovacích jazyků

Meziprocesová komunikace RPC - vzdálené volání procedur Mezi procesy jednoho stroje i po síti Ideál: Neodlišitelnost od volání uvnitř procesu Problém: Určení správného příjemce Rozhraní a funkce musejí mít identifikaci (UUID apod.) Volaný se musí předem zaregistrovat Server musí mít port-mapper Volající musí předem navázat spojení Klient musí mít lokátor Problém: Složitější typy parametrů Problém: Různé architektury klienta a serveru

RPC - vzdálené volání procedur int f( double a, int b); // uživatelský kód void my_client() { int y; y = f( 3.14, 7); printf( "%d", y); } // client-stub int f( double a, int b) { int z; start_RPC( ID_f); send_double( a); send_int( b); wait_for_reply(); receive_int( & z); return z; Implementace na straně volajícího procesu: Volaná funkce má uvnitř volajícího procesu zástupce: client stub Client stub je funkce stejného jména i parametrů jako volaná funkce Volající volá client stub stejně, jako kdyby přímo volal volanou funkci Client stub zařídí sbalení parametrů a odeslání prostředky OS Po návratu client stub rozbalí návratové hodnoty a skončí

RPC - vzdálené volání procedur void dispatcher() { for (;;) { wait_for_RPC(); id = receive_id(); switch ( id ) { case ID_f: { // server stub double a; int b, z; receive_double( & a); receive_int( & b); z = f( a, b); send_int( z); } /* ... */ // uživatelský kód int f( double a, int b) return log( a) * b; Implementace na straně volaného procesu: Volaná funkce je volána z pomocné funkce: server stub Server stub je volán z dispečeru běžícího ve věčném cyklu Server stub zařídí rozbalení parametrů a zavolá volanou funkci Po návratu z funkce sbalí návratové hodnoty a odešle odpověď

RPC - vzdálené volání procedur void f( char * s, int n, int * x); void m() { int x[ N]; f( "Hello", N, x); } Problém: parametry předávané jako ukazatel Ukazatel je platný jen v kontextu volajícího procesu Zpřístupnění jinému procesu je možné pouze okopírováním dat Z kódu v C++ nelze zjistit rozsah dat potřebný směr kopírování

RPC - vzdálené volání procedur void f( char * s, int n, int * x); void m() { int x[ N]; f( "Hello", N, x); } Řešení: Popis ve speciálním jazyce IDL - Interface Definition Language (MIDL,...) interface MyInterface { f( [in, string] char * s, int n, [out, size_is( n)] int x[]); } Z popisu v IDL se vygenerují client a server stuby Další pomocné jazyky se užívají pro identifikaci rozhraní