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

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

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

Podobné prezentace


Prezentace na téma: "Programování v C++ David Bednárek ulita.ms.mff.cuni.cz."— Transkript prezentace:

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

2 Pravidla studia PRG029 2/2 Z,Zk

3 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 Udělení zápočtu  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  Naposledy 2003/2004  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í  2/2 Z, Zk  Povinný  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 Kurs jazyka C Hello, world! Oddělený překlad modulů

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

17 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 Kompilátor Uživatelský.c Přeložený.obj Linker Spustitelný soubor.exe Standardní.obj Standardní.lib

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 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(); } error: Undefined 'B' y.c double B() { return 3.14; } 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 kód 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 kód B Runtime error

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

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

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

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

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

30 Spojování modulů - typy x.c #include "y.h" double A() { return C; } x.obj kód A s čtením naprázdno export A import C y.c #include "y.h" enum T C; y.h enum T { P, Q}; extern 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; 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}; enum T { P, Q}; extern enum T C; enum T { P, Q}; extern enum T D; double A() { return C + D; } error: Type redefinition: 'T' y.h #include "t.h" extern enum T C; z.h #include "t.h" extern enum T D;

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 #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; } y.h #include "t.h" extern enum T C; 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 Kompilátor 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 Kompilátor Linker Privátní.h Veřejné.h Knihovna.dll Loader

37 Přípony souborů MicrosoftUnix.c.c.c.c Zdrojový kód C.cpp.C.cc.cpp Zdrojový kód C++.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.exebez 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 A x[] 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 - 1 // 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 struct STR { int a, b; STR * next; }; STR * p;

48 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 zleva >= relace uspořádání zleva == != relace rovnosti zleva & bitové AND zleva ^ bitové XOR zleva | bitové OR zleva && booleovské AND zleva || booleovské OR zleva ? : podmíněný výraz zprava = *= /= %= += -= &= ^= |= >= přiřazení kombinované přiřazení zleva, sekvence 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 zleva + - aditivní operátory zleva > bitové posuny

50 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 = b  je ekvivalent a = a b  hodnotou výrazu je tedy výsledná hodnota levé strany

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

52 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 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 Vztah ukazatelů a polí  Nulový ukazatel  #define NULL 0  Vyžaduje #include  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 #include 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)); if ( ! p ) { /* chyba */ }  Uvolnění pole objektů free( p);  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]; if ( ! p ) { /* chyba */ }  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 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; } argv++, argc--; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /*... */ Příklad užití ukazatelů a polí - pHello A.TX -n 0 0 C :\MYPROG.EXE0 0 0T B.TX0T argv 5 argc

65 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; } argv++, argc--; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /*... */ Příklad užití ukazatelů a polí - pHello A.TX -n 0 0 C :\MYPROG.EXE0 0 0T B.TX0T (*argv)[0] 4 argc opt_p 0 opt_n 0 *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; } argv++, argc--; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /*... */ - pHello A.TX -n 0 0 C :\MYPROG.EXE0 0 0T B.TX0T (*argv)[0] 3 argc opt_p 1 opt_n 0 *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; } argv++, argc--; } if ( argc != 2 ) goto error; do_something( argv[ 0], argv[ 1], opt_n, opt_p); /*... */ - pHello A.TX -n 0 0 C :\MYPROG.EXE0 0 0T B.TX0T argv[0] 2 argc opt_p 1 opt_n 0 argv[1] argv

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 }; puts( gv);  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 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 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 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 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 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 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; else 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++ - ladicí funkce (makro assert) - klasifikace znaků (isalpha, isspace,...) - chybové kódy (ENOMEM,...), proměnná errno - vlastnosti a limity reálných typů (DBL_MAX,...) - limity celočíselných typů (INT_MAX,...) - přizpůsobení národnímu prostředí - matematické funkce (sin,...) - meziprocedurální skoky (setjmp, longjmp) - signály operačního systému - makra pro funkce s proměnným počtem argumentů - užitečné typy a konstanty (NULL) - standardní a souborový vstup a výstup - užitečné funkce (malloc,...) - manipulace s řetězci (strcpy,...) - konverze data a času - 16-bitové řetězce (wchar_t) - klasifikace 16-bitových znaků

81 stdio.h  Otevírání a zavírání souborů 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 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 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 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ů 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 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 = '/'; #else 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 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 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 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 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 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 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 Dědičnost  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 Dědičnost  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 Dědičnost  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 xy 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 { public: int u; }; int g( B * pb) { return pb->u * f( pb); } B A xyu pbpa A xy

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

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

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í B A u xy C A xz B A xy C A xz D A x

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 B A u xy C A xz B A xy C z D A 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 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 { public: 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 { public: 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 { public: virtual int f() { return 2; } int g() { return f(); // pozdní vazba } };  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 { public: virtual int f() { return 2; } int g() { return A::f(); // přímá vazba } };  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 { public: virtual int f() { return 2; } }; B y A x f g A::f A::g A x f g B::f

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 { public: virtual int f() { return 2; } }; B A x f g A::f A::g A x f g stub y 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 { public: 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 : public AbstractGraphicObject { public: virtual void paint() { 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; Color fg; };

143 Příklad užití virtuálních funkcí class AbstractGraphicObject { public: virtual void paint() = 0; AbstractGraphicObject * next; }; class SingleColorObject : public AbstractGraphicObject { public: 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 : public SingleColorObject { public: virtual void paint() { 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 { public: void setFg( Color fg); Color getFg() const; private: Color _fg; }; class Rectangle : public SingleColorObject { public: 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í cc AbstractGraphicObject SingleColorObject RectangleCircle FilledRectangle AbstractGraphicObject SingleColorObject RectangleCircle DoubleColorObject FilledRectangle

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

147 Příklad užití virtuálních funkcí cc AbstractGraphicObject SingleColorObject BaseRectBaseCirc DoubleColorObject FilledRectangleFilledCircle AbstractGraphicObject SingleColorObject RectangleCircle DoubleColorObject FilledRectangleFilledCircle BaseFRectBaseFCirc RectangleCircle

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 { public: 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 { public: virtual void paint() { 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 { public: 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 { public: virtual void paint() { 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 { public: 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) { AbstractGraphicObject * p; for ( p = first; p; p = p->next) { 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 { public: 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) { AbstractGraphicObject * p; for ( p = first; p; p = p->next) { 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 { public: virtual SingleColorObject * getSingleColor() { 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) { AbstractGraphicObject * p; for ( p = first; p; p = p->next) { 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 BaseRectBaseCirc DoubleColorObject FilledRectangleFilledCircle BaseFRectBaseFCirc RectangleCircle

154 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 Reference  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; * ukazatel = 2; // data = 2 int data2; 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 Reference  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 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) { return a < b ? a : 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 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ř funkceuvnitř třídy (C++) bez specifikacestatická alokace, export mimo modul viz autosoučást každé instance třídy auto zakázánozásobník, případně registr zakázáno register zakázánozásobník, přednostně v registru zakázáno 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 zakázáno

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

169 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 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 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 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 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á To nemusí jít kvůli ochraně přístupu  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 { public: 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 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 class XXX { 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 String() { s = 0; } // Destructor ~String() { destroy(); } // Copy constructor String( const String & str) { fill( str.s); } // Operator = const String & operator = ( const String & str) { destroy(); 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); else 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() { f(); /* A::f */ }; void g() { f(); /* A/B::f */ }; class B : public A { public: virtual void f(); B() { /* A::A */ f();/* B::f */ }; ~B() { f();/* B::f */ };/* A::~A */ void g() { f();/* B::f */ };

181 Ochranné prostředky private/protected/public

182 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); }; namespace X { 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ů  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 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  Operátor ->  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 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á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 template class Array { T p[ n]; T dummy; public: T & operator[]( int x) { return x 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  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 X { X( const X &) };

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 void X ::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 Šablony tříd – triky  Dopředná deklarace šablony template class X; /*... */ template class X { /*... */ };  Specializace funkce pro konkrétní typ  Překrývá generické tělo template class X { /*... */ void f(/*...*/); }; template void X ::f(/*...*/) { /*...*/} void X ::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 X { /*... */ void f(/*...*/); };  Nepublikovaný hlavičkový soubor XBody.h  Generická těla metod #include "X.h" template void X ::f(/*...*/) { /*...*/ }  Knihovní modul XBodyInt.cpp  Instanciace pro typ int #include "XBody.h" template X ;

204 Šablony funkcí  Šablona funkce je generická globální funkce prefixovaná konstrukcí template 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 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 T max( Array a) { /*... */ }

205 Standardní knihovny C++

206  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++ - ladicí funkce (makro assert) - klasifikace znaků (isalpha, isspace,...) - chybové kódy (ENOMEM,...), proměnná errno - vlastnosti a limity reálných typů (DBL_MAX,...) - limity celočíselných typů (INT_MAX,...) - přizpůsobení národnímu prostředí - matematické funkce (sin,...) - meziprocedurální skoky (setjmp, longjmp) - signály operačního systému - makra pro funkce s proměnným počtem argumentů - užitečné typy a konstanty (NULL) - standardní a souborový vstup a výstup - užitečné funkce (malloc,...) - manipulace s řetězci (strcpy,...) - konverze data a času - 16-bitové řetězce (wchar_t) - klasifikace 16-bitových znaků

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

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

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

211 STL Standard Template Library

212 STL – Kontejnery  - fronta s přidáváním a odebíráním z obou stran  - obousměrně vázaný seznam  - asociativní pole, tj. parciální zobrazení  - fronta  - množina  - zásobník  - 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 typedef std::deque 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 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 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 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 vector {... */ protected: A allocator; };

218 STL – vector – naivní implementace – hlavička template class oriented_iterator; template class const_oriented_iterator; template 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 iterator; typedef const_oriented_iterator const_iterator; typedef oriented_iterator reverse_iterator; typedef const_oriented_iterator const_reverse_iterator; /*... */ private: size_type _n; T * _p; };

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

220 STL – vector – naivní implementace - konstrukce template vector ::vector() { _n = 0; _p = 0; } template vector ::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 vector ::vector( const vector & x) { _n = x._n; _p = new T[ _n]; /* nevolat konstruktory !!! */ for ( int i = 0; i < _n; i++) { /* zavolat konstruktory !!! */ /* _p[ i].T( x._p[ i]); */ }

221 STL – vector – naivní implementace – push/pop template void vector ::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 void vector ::pop_back() { /*... */ }

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

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

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

225 STL – Ostatní  - užitečné algoritmy (sort, next_permutation,...)  - podpora funktorů  - podpora iterátorů  - alokátory pro konterjnery  - jednoduchá matematika na prvcích kontejnerů  - pomocné konstrukce (pair,...)

226 Nepoužité slajdy

227 Preprocesor a překlad Code... push call... 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 /*...*/ int printf( const char *,...); /*...*/ hello.c #include int main( int argc, char * * argv) { printf( “Hello\n”); getch(); return 0; } conio.h int getch(); int putch(); /*...*/

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

229 Spustitelný program a spuštěný proces... push call... call... ret ‘H’,’e’,’l’,’l’, ’o’,10,0 hello... call... call entry point... syscall... Code Data 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( const void * key, 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 "Programování v C++ David Bednárek ulita.ms.mff.cuni.cz."

Podobné prezentace


Reklamy Google