Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

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

Podobné prezentace


Prezentace na téma: "David Bednárek ulita.ms.mff.cuni.cz"— Transkript prezentace:

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

2 Pravidla studia PRG029 2/2 Z,Zk

3 Elektronický zápis do jednotlivých skupin
Zápis na cvičení Elektronický zápis do jednotlivých skupin 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...

4 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í pokusy v září 2-3 hodiny v laboratoři

5 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

6 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++

7 Obsah

8 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ů

9 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

10 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

11 Literatura

12 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)

13 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)

14 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++

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

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

17 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)

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

19 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

20 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

21 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)

22 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

23 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

24 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

25 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

26 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

27 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

28 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'

29 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

30 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

31 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'

32 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;

33 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

34 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

35 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

36 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

37 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

38 Kurs jazyka C Datové typy

39 Čí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 [ ] unsigned char velmi malé nezáporné celé číslo [0..255] [signed] short [int] malé celé číslo [ ] unsigned short [int] malé nezáporné celé číslo [ ] [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

40 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)

41 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é

42 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 ! */

43 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 []);

44 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ší

45 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 */

46 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 // 6 }; Variantní záznam struct POLY { enum E t; union UNI u; };

47 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;

48 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;

49 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

50 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

51 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é

52 & - 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;

53 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

54 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

55 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' ) /*...*/

56 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 */ }

57 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

58 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;

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

60 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

61 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

62 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

63 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

64 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

65 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

66 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

67 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

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

69 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

70 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á

71 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

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

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

74 Ř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);

75 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); // ??? }

76 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é

77 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)

78 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)

79 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")

80 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ů

81 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 *);

82 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 *);

83 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 *);

84 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

85 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í

86 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

87 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

88 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

89 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í

90 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

91 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:

92 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, ...

93 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

94 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

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

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

97 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++

98 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)

99 Třída a objekt Class and Object

100 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)

101 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

102 Dědičnost Inheritance

103 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

104 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ů

105 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

106 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

107 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

108 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í)

109 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í

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

111 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

112 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

113 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

114 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

115 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

116 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

117 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

118 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

119 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

120 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

121 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í

122 Metody Member Functions

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

124 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í

125 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í

126 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

127 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

128 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

129 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

130 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

131 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

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

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

134 Virtuální metody Virtual functions

135 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

136 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

137 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

138 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

139 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

140 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

141 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

142 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;

143 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;

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

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

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

147 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

148 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());

149 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;

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

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

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

153 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

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

155 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)

156 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 &

157 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

158 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 ) // ...

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

160 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

161 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é

162 Konstruktory a destruktory
Constructors and Destructors

163 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í

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

165 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)

166 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

167 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

168 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

169 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;

170 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;

171 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

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

173 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

174 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();

175 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í

176 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

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

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

179 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ů.

180 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() {

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

182 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í

183 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;

184 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

185 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

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

187 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í

188 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í.

189 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

190 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

191 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

192 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 ->

193 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.

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

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

196 Šablony Templates

197 Š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;

198 Š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

199 Š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

200 Š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> &) };

201 Š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.

202 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(/*...*/)

203 Š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>;

204 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) { /* ... */ }

205 Standardní knihovny C++

206 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

207 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ů

208 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í)

209 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)

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

211 Standard Template Library
STL Standard Template Library

212 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

213 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

214 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()); /* ... */

215 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; /* ... */

216 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); /* ... */

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

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

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

220 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]); */

221 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() /* ... */

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

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

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

225 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,...)

226 Nepoužité slajdy

227 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

228 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

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

230 ***** 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

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

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

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

234 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;

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

236 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;

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

238 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) */ }

239 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.)

240 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ů

241 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

242 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čí

243 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ěď

244 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í

245 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í

246


Stáhnout ppt "David Bednárek ulita.ms.mff.cuni.cz"

Podobné prezentace


Reklamy Google