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

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

PRG029 Programování v C a C++ (LS 2005/06) RNDr. Filip Zavoral, Ph.D. Katedra softwarového inženýrství

Podobné prezentace


Prezentace na téma: "PRG029 Programování v C a C++ (LS 2005/06) RNDr. Filip Zavoral, Ph.D. Katedra softwarového inženýrství"— Transkript prezentace:

1 PRG029 Programování v C a C++ (LS 2005/06) RNDr. Filip Zavoral, Ph.D. Katedra softwarového inženýrství -> Výuka :48

2 Studijní povinnosti Zápočet není podmínkou složení zkoušky Požadavky na zápočet SIS – Grupíček – každý musí být v nějaké skupině Platí i pro 'pokročilé', repetenty, externisty a jakékoliv jiné úchylky Zkontrolujte, resp. přihlašte se do volné skupiny Termín Ověřte platnost u – Hladání studenta -> Změna údajů Účast na cvičeních ’Pokročilí’ programátoři – domluvit se s vyučujícím na začátku semestru Během semestru 3 'domácí úkoly' (krátké prográmky) DÚ lze nahradit jedním větším zápočťákem Závěrečný test Odladit krátký program v omezeném čase (v labu) Konkrétní požadavky určuje a jejich plnění hodnotí cvičící Vše nestandardní předem domluvit s cvičícím

3 Pravidla pro budoucí neúspěšné Zkouška Pokud letos složíte zkoušku se známkou 1 nebo 2 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. Zápočet Pokud letos splníte zápočet, bude vám příští rok automaticky uznán Pokud nedostanete zápočet, budete příští rok opakovat nesplněné části 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 cvičícím na konkrétních podmínkách

4 PRG025 - Programování v C a C++ PRG035 - OOP LS 1. roč ZS 2. roč Obsah předmětu C++ C Nejdůležitější: vlastní praxe Na přednáškách se nikdo nikdy programovat nenaučil C

5 Obsah přednášky Kurz jazyka C Přesněji: část C++ shodná s C Překlad programů, spojování Základní vlastnosti C, odlišnosti od jiných programovacích jazyků Datové typy, operátory a řídící konstrukce C Pole a ukazatele v jazyce C, práce s řetězci Vstup a výstup, standardní knihovy C Programování není (jen) zápis algoritmů Úvod do C++ Zbývající cca 2/5 semestru Třídy a objekty, dědičnost, virtuální funkce, polymorfismus Povídání o C++ bude plynule pokračovat v 2. ročníku

6 Obsah cvičení Střídavě v S7 a v laboratoři SW2 Začínáte tam, kde to máte napsané v rozvrhu Základní vlastnosti jazyka C Práce s datovými strukturami, triky Standardní knihovy C Zajímavé a záludné vlastnosti C Cvičení z C++ až v 2. ročníku (pro ty, kteří přežijí) Laboratoř SW2 Microsoft Visual Studio.NET 2005 Praktické programování Ladění programů (!) Prosíím, já jsem napsal program a ono to řeklo 'Váš program provedl neplatnou instrukci a bude ukončen '. Co mám dělat?

7 Literatura Základní učebnice a popis jazyka Andrew Koenig, Barbara E. Moo: Rozumíme C++ (C++ Accelerated) Miroslav Virius: Programování v C++ (ČVUT 2001) Miroslav Virius: Pasti a propasti jazyka C++ Bruce Eckel: Myslíme v jazyku C++ Thinkinkg in C++ 2nd ed. - volně stáhnutelné... C++ In-depth Alexandrescu, Sutter: C programovacích technik (C++ Coding Standards) Meyers: Effective C++ (2 nd ed.), More Effective C++, Effective STL Sutter: Exceptional C++, More Exceptional C++, Exceptional C++ Style Josuttis: The C++ Standard Library Josuttis: Object-Oriented Programming in C++ Jak správně C++ používat pro středně pokročilé

8 Literatura Pro velmi pokročilé Alexandrescu: Modern C++ Design Generic Programming and Design Patterns Applied Vandevoorde, Josuttis: C++ Templates Abrahams, Gurtovoy: C++ Template Metaprogramming Normy ISO/IEC 14882, ANSI: Programming languages - C++ (1998, 2003) C TR1 (2005) C++0x ISO/IEC 9899: Programming languages - C (1999) WWW Eckel: Thinking in C++... and more v češtině: Moderní programování v C++

9 Nevhodná literatura - nepoužívat! Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language Martin Beran: Učebnice Borland C++ - hrubé chyby Jan Pokorný: Rukověť uživatele Borland C++ - staré, BC 3.1 Vladimír Rudolf: ABC programátora v C++ - neúplné, zastaralé Dirk Louis: C und C++ — Programierung und Referenz - chyby Dalibor Kačmář: Jazyk C — učebnice pro střední školy – chyby Brodský, Skočovský: Operační systém Unix a jazyk C – neúplné, zastaralé Eric Gunnerson: Začínáme programovat v C# – C# není C++

10 Pascal vs. C a C++ Programování I, II heslo: programování = zápis algoritmů algoritmické myšlení, algoritmizace problému soustředění se na řešení problému formulace algoritmu a jeho zápis v nějakém formalismu (jazyku) základní datové a řídící struktury nedůležité: kontrola vstupů, uživatelské rozhraní, obedněnost, vazba na OS a HW, přenositelnost, optimalizace, udržovatelnost  výuka (v) Pascalu dobrý jazyk pro zápis algoritmů nezatěžuje technickými detaily (alokace paměti, vazba na OS,...) slabá podpora pro kontrolu vstupů, uživatelské

11 Pascal vs. C a C++ Programování v C a C++, OOP heslo: programování = vývoj software důležité: kontrola vstupů, uživatelské rozhraní, obedněnost, vazba na OS a HW, přenositelnost, optimalizace, udržovatelnost zvládnutí knihoven a vývojových nástrojů  výuka (v) C++ standardní jazyk pro vývoj software další jazyky vycházejí z C++ (Java, C#, ale i PHP,...) dobrá podpora pro kontrolu vstupů, uživatelské nutnost zvládnout technické detaily (alokace paměti, vazba na OS..) velké množství hotového kódu (knihovny, komponenty,...) 'Vše' již bylo naprogramováno

12 Historie první verze C, společný vývoj s UNIXem 1973přepsání jádra UNIXu do C 1978Kerninghan, Ritchie: The C Programming Language 1980standardy – ANSI X3J11, od r ISO AT&T - "C with Classes" 1983poprvé název C++ (Rick Mascitti) 1985Stroustrup: The C++ Programming Language 1989ANSI X3J16 norma C nejnovější ISO/ANSI norma C C TR1 rozšíření knihoven (částečně podporováno GCC 4.0) založeno na knihovně Boost plán nové normy C++ Normy se vyvíjí, aktuální překladače o několik let zpět Implementace novinek často nekorektní nebo neefektivní

13 Part I - C C přesněji společná část C a C++ se zdůrazněním odlišností

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

15 hello.c #include #include int main( int argc, char ** argv) { printf( "Hello\n"); getch(); return 0; } tělo funkce hlavička funkce příkaz deklarace knihovních funkcí direktiva preprocesoru vložení souboru formální parametry název funkce typ návratové hodnoty skutečné parametry volání funkce bez parametrů

16 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.cpp (.c) Hlavičkové soubory Soubory s příponou.h Deklarují (a někdy i definují) 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 Samostatná jednotka překladu Výsledek práce preprocesoru

17 Překlad jednoho modulu a sestavení.cpp.h CC.objLink.exe.obj.lib kompilace spojování (linkování) knihovny standardní i jiné knihovní headery objektový modul (přeložený kód) spustitelný program

18 Překlad více modulů – oddělený překlad.c.h CC.objLink.exe.obj.lib.obj kompilace jednoho modulu knihovny knihovní headery vlastní headery.cpp.c.cpp další moduly

19 Modul - ucelená funkčí jednotka modul.cpp - implementace modul.h - definice rozhraní Oddělený překlad - dělení na moduly fotbal.cpp fotbal.h hriste.cpphrac.cppmic.cpp hriste.hhrac.hmic.h rozdělení projektu do modulů a vytváření headerů je umění, nedá se to naučit na přednášce

20 Překladače a vývojová prostředí Windows - překladač součástí integrovaného prostředí MS Visual Studio - Visual C++ (.Net2005) integrovaný make, linker, debugger klikabilní konfigurace další překladače - Borland C++ Builder, Intel, Watcom Unix (Linux) - samostatné programy, příkazová řádka gcc make - pro 'opravdové' programátory pokusy o vývojová prostředí (kDevelop,...) nepoužívat ! vývoj ukončen

21 Integrované vývojové prostředí.c.h CCLink.exe.obj.lib.obj.c.cpp debugger projekt editor

22 make.c.h CCLink.exe.obj.lib.obj.c.cpp make makefile

23 Program a modul Program v C++ nemá (vnořovanou) blokovou strukturu Neexistuje "hlavní blok" 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( parametry příkazové řádky) Modul: posloupnost globálních deklarací a definic Deklarace Oznámení překladači, ž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

24 Příklad - malá násobilka #include int vynasob( int c) { int i = 1, v; if( c 10) return -1; while( i <= 10) { v = i * c; printf( "%d * %d = %d\n", i, c, v); i = i + 1; } return 0; } int main() { int cislo = 7; vynasob( cislo); return 0; } inicializovaná celočíselná proměnná neinicializovaná proměnná hlavička funkce, formální parametr deklarace knihovních funkcí,... konec cyklu začátek cyklu kontrola parametrů okanžitý návrat z funkce 2 * 7 = 14 ++i konec, OK hlavní program ignorování návratové hodnoty volání funkce se skutečným parametrem The END

25 Funkce Základní programová jednotka je funkce Neexistují vnořené funkce Začátek programu – funkce main int fce1( int x, int y) { return x+y; } int fce2( int a) { return fce1( 1, 2*a+5); } int main( int argc, char** argv) {... } int fce2( int a) { int fce1( int x, int y) { return x+y; } return fce1( 2*a+17); } vnořené funkce nelze! začátek programu argumenty z příkazové řádky později

26 Funkce - návratový typ Typ funkce = typ návratové hodnoty Hodnota se vrací pomocí klíčového slova return Speciální typ void - 'prázdný' typ ekvivalent procedury Pascalu int fce1( int x, int y) { return x+y; } void fce2( char* text) { printf( text); } void fce3( char* text) { if( ! text) return; printf( text); } návrat celočíselné hodnoty návrat z funkce

27 Parametry funkce Pevný počet, pevný typ možnost proměnného počtu parametrů - printf Parametry jsou odděleny čárkou U každého parametru musí být uveden jeho typ Funkce bez parametrů - void int fce1( int x, int y, float z) {... } int f2( double a[5], char* str) {... } int f3( void) {... } int fce1( int x, y) {... } int fce3 {... } každý parametr musí mít typ

28 Volání funkce Shoda počtu formálních a skutečných parametrů Kompatibilita typů formálních a skutečných parametrů I funkce bez parametrů musí obsahovat operátor () Návratová hodnota - lze ignorovat int fce1( int x, int y, float z) {... } int fce3( void) {... } int fce2( int a) { fce1( a, 1, a); fce3(); return 0; } int fce1( int x, int y, float z) {... } int fce2( int a) { fce1; fce1( a, 1); fce1( a, 1, "ahoj"); }

29 Předávání parametrů funkci Všechny parametry se předávají hodnotou 'Výstupní' parametry pouze přes ukazatele nebo reference vymění se jen lokální proměnné funkce předají se pouze hodnoty (1, 2) nikoliv 'proměnné' void swap( int x, int y) { int pom; pom = x; x = y; y = pom; } void fce( void) { int a = 1; int b = 2; int c = 0; swap( a, b); c = 2 * a + b; }

30 Lokální proměnné Definice lokálních proměnných C: na začátku těla funkce (bloku) C++: kdekoliv v těle funkce Možná inicializace – při každém běhu funkce neinicializovaná proměnná – náhodná hodnota !!! int fce( void) { int x, y; int z = 0; return z + x; } deklarace celočíselných proměnných deklarace s inicializací náhodná hodnota !!! int fce2( void) { int i = 0; while( i < 100) { int j = i + 1; i = j * i; } // j jiz neexistuje return i; } hlavní blok funkce vnořený blok deklarace proměnné ve vnořeném bloku inicializace se provádí při každém průběhu po ukončení bloku již proměnná neexistuje

31 Globální proměnné Definice mimo tělo funkce Viditelné v jakékoliv funkci Možná inicializace – při startu programu Používat s rozvahou!! pouze pro sdílená data int g = 1; void fce( int x) { int z = 2; g = g + z * x; } int main( void) { fce( 2); fce( 3); return g; } globální proměnná formální parametr skutečný parametr lokální proměnná co vrátí main ??

32 Výraz Norma: "Výraz je posloupnost operací a operátorů specifikující výpočet hodnoty" Přiřazovací 'příkaz' je také výraz jeho hodnotou je přiřazovaná hodnota Výrazy mohou mít vedlejší efekty 1 a+b*sin(x) printf( "Ahoj") q = &b[17]+*++p "retezec" a = b = 0; if( (x = fnc()) != 0)... užitečné: test přiřazované hodnoty přiřazení je také výraz

33 Příkaz Příkaz je výraz ukončený ';' (středníkem) složený příkaz - posloupnost příkazů mezi '{' a '}' programová konstrukce 1; a+b*sin(x); printf( "Ahoj"); q = &b[17]+*++p; "retezec"; { 1; a+b*sin(x); printf( "Ahoj"); q = &b[17]+*++p; "retezec"; } složený příkaz

34 Podmíněný příkaz if (výraz) příkaz if (výraz) příkaz else příkaz if( a > 1) printf( "OK"); if( a > 1) b = 0; printf( "OK"); if( a > 1) b = 0; printf( "OK"); else printf( "nee"); if( a > 1) { b = 0; printf( "OK"); } else { printf( "nee"); } if( a > 1) printf( "OK"); else printf( "nee"); syntakticky správně, ale dělá něco jiného syntakticky špatně if( a > 1) { b = 0; printf( "OK"); } else { printf( "nee"); }

35 Vnořené podmíněné příkazy if( a > 1) { if( b > 0) printf( "OK"); } else { printf( "nee"); } Syntakticky správně, ale nepřehledné Na pohled nejasné párování if( a > 1) { if( b > 0) printf( "OK"); else printf( "nee"); } U vnořených podmínek vždy používat { } if( a > 1) if( b > 0) printf( "OK"); else printf( "nee");

36 Vícenásobné větvení – konstrukce switch switch (výraz) { case konstanta: posloupnost příkazů break; case konstanta: posloupnost příkazů break; default: posloupnost příkazů } switch( opcode) { case 0: // no op break; case 10: add(); break; case 11: case 12: cmp(opcode); break; default: error(opcode); break; } switch( ch) { case '0'..'9': x += ch - '0'; break; case 'A'..'F': x += ch - 'A'; break; } switch( opcode) { case 0: // no op case 10:... zapomenutý break syntakticky OK, ale dělá něco jiného interval nelze celočíselný výraz ukončení větve pokud výraz není roven žádné z konstant více návěští pro jednu větev

37 Cyklus – konstrukce while a do-while while (výraz) příkaz podmínka na začátku do příkaz while (výraz) ; podmínka na konci while( a > 1) { fce( a); a = a / 2; } while( a > 1) a = a / 2; do { fce( a); a = a / 2; } while( a > 1); tělo se vždy alespoň jednou provede Pozor! cyklus pokračuje pokud je podmínka platná

38 for (výraz1 ; výraz2 ; výraz3 ) příkaz je ekvivalentem výraz1; while (výraz2 ) { příkaz výraz3 ; } Cyklus – konstrukce for i=0; while( i<=9) { fce( i); i=i+1; } inicializace for( i=0; i<=9; i=i+1) { fce( i); } podmínkainkrementtělo cyklu FOR I := 0 TO 9 DO FCE(I) ekvivalent v jiném jazyce for( init(a); i 0; a[i++]=0) { fce( i); } for v C++: obecnější, širší možnosti použití jako inicializaci, podmínku i inkrement lze libovolný výraz init(a); while( i 0) { fce( i); a[i++]=0; }

39 breakokamžité ukončení celého cyklu cyklus s podmínkou uprostřed ošetření chybových stavů continueukončení (jednoho) běhu těla cyklu Ukončení cyklu - break, continue for(;;) { errc = fce(); if( errc < 0) break; jinafce(); } n = 1; while( n<1000) { val = get(); if( val == 0) continue; n = n * val; }

40 Nepoužívat!.... pokud k tomu není dobrý důvod Když už, tak pouze dopředu (skok dozadu = cyklus) Dobrý důvod: výskok z vícenásobně vnořených cyklů Goto for( i=0; i<10; ++i) { for( j=0; j<10; ++j) { if( fnc( i, j) == ERROR) goto konec_obou_cyklu; } konec_obou_cyklu: dalsi_funkce(); nelze break - ukončil by pouze vnitřní cyklus, vnější cyklus by pokračoval label návěští

41 Celočíselné typy typ 8 bit16 bit 32 bit64 bit char short 8 / / 32 int / 64 long / 64 long size_t3264 ptrdiff_t3264 wchar_t16/32 Základní celočíselné typy jsou znaménkové Pro každý typ existuje unsigned varianta možné využití unsigned: unsigned char, pole bitů, modulová aritmetika pokud není dobrý důvod, unsigned raději nepoužívat char  short  int  long  long long -2GB.. +2GB velikost objektů rozdíl ukazatelů rozšířená znaková sada 1 byte

42 Logické a znakové hodnoty a typy C: Až do r. 1999: neexistuje typ 'boolean' Porovnání a logické operátory jsou celočíselné výrazy FALSE (nepravda)  0 TRUE (pravda)  1 (libovolná hodnota různá od 0) důsledek: if( x != 0)  if( x) if( x == 0)  if( !x) C++, C99 celočíselný typ bool (C99: _Bool) hodnoty true (=1), false (=0) charnorma neurčuje signed / unsigned (!) korektní porovnání na nerovnost pouze 'a' < 'ž' ? signed char unsigned char 'byte' wchar_tstddef.h: znak roz ší řen é sady (Unicode) časté použití: test (ne)nulovosti záleží na implementaci většinou char = signed 40 > 200 !!! 200  -56

43 enum pohlavi { p_muz, p_zena }; pohlavi p = p_muz; int pp = p_zena + 1; pohlavi q = 0; enum flags { f1 = 1, f2 = 2, f3 = 4, f4 = 8 }; if( x & f1)... enum porty { pop3 = 111, ftp = 21, smtp = 80 }; Výčtový typ C++: samostatný typ - nelze ( C: celočíselné konstanty - OK ) test bitů hodnoty doplní překladač (od 0) explicitní hodnoty lze použít jako celočíselnou hodnotu (ale většinou to nemá rozumný smysl)

44 'Reálné' typy float  double  long double malá přesnost - nepoužívat! standard pro 'reálné' výpočty zvýšená přesnost double x = 1; double y = x / 3; if( x == 3 * y) printf( "Presne"); else printf( "Nepresne"); Pozor! Reálné výpočty jsou vždy nepřesné raději nepoužívat pouze pro fyzikální nebo numerické veličiny double zustatek = 15.60; long zustatek_v_halerich = 1560; pro přesné hodnoty používejte přesné typy

45 Celočíselné konverze Automatické konverze (integral promotions) Výpočty výrazů vždy v šíři alespoň int signed char, unsigned char, signed short  signed int unsigned short  signed int (pokud je int delší) / unsigned int Automatické konverze u binárních operací signed int  unsigned int  signed long  unsigned long   float  double  long double vždy když je použit menší typ než int při nestejných operandech moudro: prostě se to zkonvertuje na ten větší

46 Přehled operátorů, asociativita a priorita postfix ( ) [ ] ->. :: post-in/de-krementace volání funkce index přístup k položce struktury kvalifikace identifikátoru prefix ! ~ + - & * 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 L.* ->*dereference member-ptru L* / %multiplikativní operátory L+ -aditivní operátory L >bitové posuny L >= uspořádání L== !=rovnosti L&bitové AND L^bitové XOR L|bitové OR L&&booleovské AND L||booleovské OR L? :podmíněný výraz P= *= /= %= += -= &= ^= |= >= přiřazení kombinované přiřazení L,sekvence

47 Základní aritmetické operátory + - * / % podle typu operandů  automatická konverze na větší typ % - modulo x / y1x / b1.666 x % y2x % bError a / b1.666a / y1.666 a % bErrora % yError int x=5, y=3; double a=5, b=3; modulo je pouze celočíselná operace celočíselné dělení reálné dělení

48 Bitové a logické operátory & | ^ ~ - bitové operace AND, OR, XOR, NOT && || !- logické operace AND, OR, NOT 5 & 315 && 31 5 | 375 || 31 5 ^ 365 ^^ 3Error 5 ^ & 005 && 00 5 | 055 || 01 neexistuje 5 = = = = = = = oba op.  0 alespoň jeden operand  0 alespoň jeden operand = 0

49 Zkrácené vyhodnocování, relační operátory a && b- je-li a=0, b se nevyhodnocuje, výsledek = false (0) a || b- je-li a=1, b se nevyhodnocuje, výsledek = true (1) = > == != výraz typu int (bool) - výsledek vždy 0 nebo 1 (false, true) porovnávání na (ne)rovnost float/double ! porovnání vs. přiřazení ! int x[10];// pole 0..9 if( i < 10 && x[i] != 0) y = y / x[i]; test mezí pole před přístupem k prvku pole if( x==y && *x++)... Pozor! operátory s vedlejšími efekty se nemusí provést ! if( x = 1)... POZOR!!! Přiřazení! (zde hodnota vždy = 1)

50 Přiřazení, inkrementace, bitový posun = += -= *= /= %= &= |= ^= >= kombinované přiřazení a op= b  a = a op b a  a = a + 1, výsledkem je nová hodnota a a++  a = a + 1, výsledkem je stará hodnota a přesněji: a++  (tmp = a, a = a + 1, tmp) > bitový posun C++ - časté použití pro jiné účely (streams) - přetěžování i += 2; x[ i+=1] /= 3; int sum = 0; int i, x[10];... for( i=0; i<9; sum += x[i++]) ; pozor - vždy si uvědomit, zda jde o pre- nebo post- inkrementaci pokud si lze vybrat, preferujte preinkrement

51 Podmíněný výraz, sekvence a ? b : c po vyhodnocení podmínky a se vyhodnotí buď b (je-li a != 0) nebo c (je-li a == 0) a, b po úplném vyhodnocení a se vyhodnotí b x = (y>0 ? y : 0); x = (tmp = y, y = y + 1, tmp); ekvivalent x = y++; ternární operátor operátor sekvence ('zapomnění')

52 i = 0; p[ i++] = i++; Pravidla vyhodnocování a( b,c) vedlejší efekty parametrů jsou vyhodnoceny před zavoláním fce a && b je-li a nulový, b se nevyhodnocuje a || b je-li a nenulový, b se nevyhodnocuje a ? b : c po vyhodnocení a se vyhodnotí buď b nebo c a, b po úplném vyhodnocení a se vyhodnotí b Žádná další pravidla nejsou ! ostatní operátory jsou vyhodnocovány v libovolném pořadí vedlejší efekty se mohou projevit kdykoliv během výpočtu možné výsledky: p[0] = 0; p[1] = 0; p[0] = 1;

53 Fungující triky vs. chyby Fungující triky Test ukazatele nebo indexu while ( iv < v) p = p->next;

54 Typové kostrukce - přehled A x[ n] pole n prvků typu A, n je konstantní výraz A x[] pole neznámého počtu prvků typu A (pouze v některých kontextech) A * x ukazatel na typ A void * x ukazatel na neurčený typ *x ++x nelze A const * x const A * x ukazatel na konstantní hodnotu typu A ++x lze ++*x nelze A * const x konstantní ukazatel na typ A ++x nelze ++*x lze 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 parametrů, C: bez určení parametrů A x( par) funkce s určenými parametry A x( void) funkce bez parametrů void x( par) funkce bez návratové hodnoty (procedura)

55 Pole Deklarace: t x[n] - pole x o n prvcích (0..n-1) typu t Indexy polí vždy začínají od 0 ! Při přístupu se nikdy nekontrolují meze !!! Vícerozměrné pole je pole polí Deklarace: t x[m][n]; Přístup: x[m][n] = a; int x[5]; for( i=1; i <=5; ++i) x[i] = i; 01234??? ?12345 Přepis náhodného místa v paměti ! Nepředvídatelné následky !! int x[8][8]; for( i=0; i < 8; ++i) for( j=0; j < 8; ++j) x[ i ] [ j ] = i * j; Pozor! x[m,n] není prvek dvojrozměrného pole o souřadnicích m a n čárka = operátor sekvence význam: m-prvkové pole typu (n-prvkové pole typu t)

56 Inicializace pole Při deklaraci pole lze obsah pole inicializovat Nejde o přiřazení, lze pouze při deklaraci Deklarace inicializovaného pole nemusí obsahovat velikost překladač dopočítá z inicializace Při nesouhlasu velkosti pole a počtu inicializátorů velikost > inicializátory: inicializace začátku, obsah zbytku nedefinován velikost < inicializátory: kompilační chyba (Too many initializers) int cisla[] = { 1, 2, 3, 4 }; char* jmena[] = { "Josef", "Karel", "Jana" }; int matice[][3] = { { 11, 12, 13 }, { 21, 22, 23 } };

57 Struktury struct osoba { char jmeno[20]; char prijemni[30]; int rok_narozeni; int pohlavi; }; osoba os, *po, zam[20]; osoba beda = { "Béda", "Trávníček", 1980, p_muz }; strcpy( os.jmeno, "Venca"); zam[3].rok_narozeni = 1968; po = &zam[3]; po->pohlavi = p_muz; definice struktury položky struktury struktura, ukazatel na strukturu, pole struktur přístup k položkám x.y (*px).y  px->y definice proměnné typu struktura s inicializací Béda Trávníček

58 Pojmenování struktur a výčtových typů C++ Jméno třídy/struktury/unie/výčtového typu je samostatný typ 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; struct STR { int a, b; STR * next; }; STR * p; C++ - samostatný typ C - pouze tag

59 Ukazatele Co to je proměnná? místo v paměti, typ (  velikost), hodnota hodnota se dá číst a většinou i zapisovat Co to je ukazatel (pointer)? něco, čím si můžu ukazovat na proměnnou (nebo na jiné paměťové místo – pole, položka struktury, dynamická alokace) K čemu jsou ukazatele dobré: zpracování řetězců, dynamické datové struktury, předávání parametrů odkazem, práce s buffery,... Pro C i C++ je práce s ukazateli typická 1 někde bydlí je nějak velká (typ) má hodnotu

60 Ukazatele Deklarace: t x – proměnná x typu t t *p - ukazatel p na typ t Operátor reference:p = &x Operátor dereference:x = *p Neinicializovaný ukazatel vs. nulový ukazatel C++:0 C:#define NULL 0 17 x: p: proměnná  ukazatel na ni ukazatel  hodnota na kterou ukazuje

61 Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if( ! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; přepsání náhodného místa v paměti 1 x::px 3 y::py ? ? 5 jaký zde bude obsah x a y?

62 Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if( ! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; typicky 'segmentation fault' váš program bude ukončen pro pokus o porušení ochrany paměti 1 x::px 3 y::py 0: 7 umožní test

63 Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if( ! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; přístup k hodnotě proměnné přes ukazatel 1 x::px 4 y::py Pozor na prioritu! *py++  *(py++) 1 x::px 3 y::py

64 Ukazatele - příklad int x = 1, y = 3; int * px, * py; *px = 5; py = NULL; *py = 7; if( ! py) etwas(); px = &x; py = &y; (*py)++; px = py; y = x; py = &x; *py = 9; 1 x::px 1 y::py 9 x::px 1 y::py jaký zde bude obsah x a y?

65 Pole a ukazatele, aritmetika ukazatelů t a[n]; t *p = &a[i];ukazatel p ukazuje na nějaký prvek pole (stejného typu) p + jpřičtení celočíselné hodnoty - posun o j prvků v rámci pole p - jodečtení - posun zpět int a[5]; int *p; a[2] = 20; p = &a[2]; a[0] = *p - 15; p++; *p = 30; ??20?? 5? 3030? p p reference na prvek pole inkrementace ukazatele - posun na další prvek a reference na prvek pole

66 Pole a ukazatele, aritmetika ukazatelů p = &a[1]; *(p+3) = 40; Operátor [] p[i]  *(p+i) &p[i]  p+i a  &a[0] Automatické konverze pole-ukazatel identifikátor pole se chová jako ukazatel na nultý prvek Pole nelze přiřazovat ani předávat hodnotou ukazatel na nultý prvek p = a je ekvivalentní p = &a[0] 5? p přičtení čísla k ukazateli  posun o n prvků identifikátor pole je ukazatel na svůj nultý prvek indexování pole (ukazatele) je jen jiný zápis přístupu přes ukazatel

67 Pole a ukazatele, aritmetika ukazatelů int a[5]; int *p; identifikátor pole je konstantní  nelze do něj přiřazovat p = &a[0]; p = a; *p = a[1]; *(p+2) = a[2] – 1; p = a + 2; p[1] = *(a+1); a[3] = p[2]; *(a+2) = p[-1]; 3[a] = 2[p]; a[4] = p + 1; p = a[0]; p[1] = a; a = p + 2; nekompatibilní typy nestejná úroveň indirekce p[i]  *(p+i)  *(i+p)  i[p]

68 Řetězce Řetězec je pole znaků (char) zakončené nulou konvence, knihovny "Ahoj" Xproměnná 'X'znaková konstanta - celočíselná hodnota "X"řetězec - ukazatel 'A''h''o''j''\0' Každý řetězec musí být vždy ukončen nulou 'A''h''o''j''\0' vždy myslet na koncovou nulu ! pozor na uvozovky a apostrofy ! char buffer[4]; strcpy( buffer, "Ahoj"); '\0' = 0 kód znaku v použitém kódování (ASCII, CP1250, ISO8859-2, EBCDIC,...) když chcete říct mezera, napište mezeru (' ') ať vás ani nenapadne napsat 32 přestože to na VAŠEM počítači funguje

69 Řetězcové proměnné a konstanty static char s1[] = "Uno"; const char *s2 = "Due"; puts( "Uno"); 'U''n''o''\0' 'D''u''e''\0' s1: s2: Inicializovaná proměnná typu ukazatel s2++ se přesune na další znak Inicializované pole (konstantní ukazatel) s1++ nelze! anonymní globální proměnná const char[] ekvivalent globální proměnné typu const char[ ], inicializované obsahem konstanty... = { 'U', 'n', 'o', 0 };

70 Předávání řetězců Jazyk C/C++ nedovoluje kopírování pole není možné pole přiřazovat, předávat jako parametr ani vracet z funkce Předávání řetězců do funkcí Předává se vždy odkazem Parametr typicky 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 ukazatel na pole, které má funkce vyplnit standardní C knihovny to ale většinou nedělají

71 Řetězce – knihovní funkce, délka řetězce v C neexistují 'řetězcové operace' (C++: třída string) přiřazení, zřetězení, porovnání, podřetězec,... vše standardní knihovní funkce #include size_t strlen( const char* s); Ahoj\0??? pole: počet znaků (bez koncové nuly) char pole[8] = "Ahoj"; x = strlen( pole);// 4 skutečný počet znaků (4) nikoliv velikost pole deklarace řetězcových funkcí inicializované pole typu char

72 Délka řetězce – přes index int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } přístup přes index

73 Délka řetězce – přes ukazatel int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } přístup přes ukazatel

74 Délka řetězce – cyklus for int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } for( i=0; *s != '\0'; i++) s++; podmínka for cyklu může být libovolná

75 Délka řetězce – for bez těla int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } for( i=0; *s != '\0'; i++) s++; for( i=0; *s != '\0'; i++, s++) ; více inkrementací prázdné tělo nezapomenout na ';' !!

76 Délka řetězce – podmínka s vedlejším efektem int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } for( i=0; *s != '\0'; i++) s++; for( i=0; *s != '\0'; i++, s++) ; int i=0; while ( *s++ != '\0') i++; složitější podmínka: test nenulovosti inkrementace ukazatele

77 Délka řetězce – nenulovost výrazu v podmínce int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } for( i=0; *s != '\0'; i++) s++; for( i=0; *s != '\0'; i++, s++) ; int i=0; while ( *s++ != '\0') i++; int i=0; while ( *s++) i++; while(a!=0)  while(a) podmínka je splněna pokud je nenulová

78 Délka řetězce – rozdíl ukazatelů int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } char *p = s; while (*p++) ; return p-s-1; for( i=0; *s != '\0'; i++) s++; for( i=0; *s != '\0'; i++, s++) ; int i=0; while ( *s++ != '\0') i++; int i=0; while ( *s++) i++; rozdíl ukazatelů = počet prvků mezi nimi pozor na ± 1 !

79 Délka řetězce – různé způsoby implementace int i = 0; while ( *s != '\0') { i++; s++; } return i; int strlen ( const char* s) { int i = 0; while ( s[i] != '\0') { i++; } return i; } char *p = s; while (*p++) ; return p-s-1; for( i=0; *s != '\0'; i++) s++; for( i=0; *s != '\0'; i++, s++) ; int i=0; while ( *s++ != '\0') i++; int i=0; while ( *s++) i++; přístup přes index přístup přes ukazatel podmínka for cyklu může být libovolná více inkrementací prázdné tělo nezapomenout na ';' !! složitější podmínka: test nenulovosti inkrementace ukazatele while(a!=0)  while(a) podmínka je splněna pokud je nenulová rozdíl ukazatelů = počet prvků mezi nimi pozor na ± 1 !

80 Řetězce - kopírování char* strcpy( char* d, const char* s); zkopíruje obsah s do místa začínajího od d char buf[8]; char pozdrav[] = "Ahoj"; strcpy( buf, pozdrav); Ahoj\0 pozdrav Ahoj\0??? buf kopíruje pouze do koncové '\0'

81 Řetězce – chyby při kopírování Dobryden\0 pozdrav buf char buf[6]; char pozdrav[] = "Dobry den"; strcpy( buf, pozdrav); Dobryden\0 vždy pozor na dostatek místa funkce nic nekontroluje !!! váš program provedl... char *ptr; char pozdrav[] = "Ahoj"; strcpy( ptr, pozdrav); kopírování na neinicializovaný ukazatel !!! váš program provedl... Ahoj\0 pozdrav ptr ? Ahoj\0 ptr neinicializovaný !!!

82 Řetězce – zřetězení, vyhledávání char* strcat( char* d, const char* s);připojí s za d char* strchr( const char* s1, int c);vyhledá první pozici c v s1 char* strstr( const char* s1, const char* s2);vyhledá podřetězec s2 v s1 char buf[10]; char* bp; strcpy( buf, "Ahoj"); strcat( buf, "Babi"); bp = strstr( buf, "oj"); Ahoj\0????? po strcpy AhojBabi\0? po strcat buf bp AhojBabi\0? pozor na dostatek místa !

83 Řetězce – porovnávání a další funkce int strcmp( const char* s1, const char* s2); s1 < s2  -1 s1 = s2  0 s1 > s2  +1 lexikografické uspořádání (jako ve slovníku) další řetězcové funkce: strncat, strncmp, strncpy strrchr Find last occurrence of given character in string strspn, strcspnGet length of substring composed / not composed of given characters strpbrk Find first occurrence of character from one string in another string strtok Find next token in string, sequentially truncate string if delimiter is found sprintf Write formatted data to a string memcpy, memset, memcmp, memchr co znamená 's1 < s2'? ABCE ===  ABCDE výsledek podle prvního rozdílného znaku

84 Problém vracení řetězců - lokální proměnná Naprosto chybné řešení Nekontroluje přetečení pole buf Vrací odkaz na lokální proměnnou, která v okamžiku návratu zaniká char * cele_jmeno( const char * jm, const char * prijm) { char buf[ 100]; strcpy( buf, jm); strcat( buf, " "); strcat( buf, prijm); return buf; }

85 Problém vracení řetězců - statická proměnná Chybné řešení Nekontroluje přetečení pole buf Používá statickou proměnnou zbytečně zabírá místo i v době, kdy funkce není vyvolána opakovaná volání ji sdílejí: podmínka nikdy nebude splněna, protože strcmp vždy dostane stejné ukazatele na totéž pole buf char * cele_jmeno( const char * jm, const char * prijm) { static char buf[ 100]; strcpy( buf, jm); strcat( buf, " "); strcat( buf, prijm); return buf; } if ( strcmp( cele_jmeno( j1, p1), cele_jmeno( j2, p2)) )

86 Problém vracení řetězců - parametr Funkční řešení, 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 cele_jmeno( char * buf, const char * jm, const char * prijm) { strcpy( buf, jm); strcat( buf, " "); strcat( buf, prijm); } void tisk( const char * jm, const char * prijm) { char buf[ 100]; cele_jmeno( buf, jm, prijm); puts( buf); }

87 Problém vracení řetězců - správné a bezpečné řešení int cele_jmeno( char * buf, size_t bufsize, const char * jm, const char * prijm) { size_t lj = strlen( jm); size_t lp = strlen( prijm); if ( lj + lp + 2 > bufsize ) { /* error */ return -1; } memcpy( buf, jm, lj); buf[ lj] = ' '; memcpy( buf + lj + 1, prijm, lp); buf[ lj + lp + 1] = 0; return lj + lp + 1; } void tisk( const char * jm, const char * prijm) { enum { N = 100 }; char buf[ N]; if( cele_jmeno( buf, N, jm, prijm) > 0) puts( buf); } max velikost pole kontrola korektnosti výsledku kontrola velikosti pole pozor na mezeru a konec! kopírování jednotlivých částí návrat výsledné délky

88 Kombinace typových kostrukcí A * x[10] pole ukazatelů A (* x)[10] ukazatel na pole 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 čtení deklarací: od identifikátoru doprava, až to nepůjde, tak doleva typicky se nepoužívá pole = ukazatel na 1. prvek

89 int*(*pf[10])(void); int*(*maso(int*(*p1)(void),int*(*p2)(void)))(void); Kombinace typových kostrukcí - příklad co to je za maso ???

90 int*(*pf[10])(void); int* ( *maso(int*(*p1)(void),int*(*p2)(void)) ) (void); typedef int* fce( void); fce * pf [10]; fce * maso( fce* p1, fce* p2); Kombinace typových kostrukcí - typedef použitím typedef se může výrazně zpřehlednit kód

91 Parametry příkazové řádky C:\> myprog.exe -n -w a.txt b.txt 0 myprog.exe\0 -n -w a.txt b.txt argv 5 argc int main( int argc, char** argv) pole řetězců (ukazatelů na char) Počet parametrů včetně názvu programu ! = počet ukazatelů v argv

92 Zpracování příkazové řádky – výpis parametrů int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); ++argv; } C:\> myprog.exe -n -w a.txt b.txt myprog.exe -n -w a.txt b.txt výstup řetězce a odřádkování posun na další parametr

93 Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); ++argv; } myprog.exe\0 -n -w a.txt b.txt argv 0 typ: char** **argv  argv[0][0] typ: char argv[4][1] *argv  argv[0] typ: char* argv[4] C:\> myprog.exe -n -w a.txt b.txt

94 Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); ++argv; } myprog.exe\0 -n -w a.txt b.txt argv 0 argv++ **argv

95 Zpracování příkazové řádky int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); ++argv; } myprog.exe\0 -n -w a.txt b.txt argv 0 *argv == 0

96 Zpracování parametrů příkazové řádky int main( int argc, char** argv) { int n=0, w=0; while( *++argv && **argv=='-') { switch( argv[0][1]) { case 'n': n = 1; break; case 'w': w = 1; break; default: error(); } if( !argv[0] || !argv[1]) error(); doit( argv[0], argv[1], n, w); return 0; } options usage: myprog [-n] [-w] fileA fileB nastavení přepínače zbývající parametry výkonná funkce prg.exe\0 -n -w a.txt b.txt 0 argv

97 Zpracování parametrů příkazové řádky int main( int argc, char** argv) { int n=0, w=0; int x = 0; char* f = 0; while( *++argv && **argv=='-') { switch( argv[0][1]) { case 'n': n = 1; break; case 'w': w = 1; break; case 'x': x = atoi( argv[0]+2); break; case 'f': f = argv[0]+2; break; default: error(); } if( !argv[0] || !argv[1]) error(); doit( argv[0], argv[1], n, w, x, f); return 0; } číselný parametr usage: myprog [-n] [-w] [- x123] [-ffilename] fileA fileB řetězcový parametr -x123\0 -ffil... argv

98 Zpracování parametrů příkazové řádky int main( int argc, char** argv) { int n=0, w=0; int x = 0; char* f = 0; while( *++argv && **argv=='-') { switch( argv[0][1]) { case 'n': n = 1; break; case 'w': w = 1; break; case 'x': x = atoi( argv[0]+2); break; case 'f': if( argv[0][2]) f = argv[0]+2; else f = *++argv; break; default: error(); } if( !argv[0] || !argv[1]) error(); doit( argv[0], argv[1], n, w, x, f); return 0; } -ffile usage: myprog [-n] [-w] [- x123] [-f filename] fileA fileB -f file -f\0 argv -ffil... filen

99 Dynamická alokace paměti C: standardní knihovny, void* malloc( size_t size); void free( void* p); #include ELEM * p; p = malloc( sizeof( ELEM)); if ( !p ) { /* chyba */ } free( p); int n = 100; p = malloc( n * sizeof( ELEM)); if ( !p ) { /* chyba */ } free( p); C++ nutnost přetypování p = (ELEM*)malloc( sizeof( ELEM)); vždy ověřit !!! váš program provedl... C++: součást jazyka new Tnew T[ n] delete pdelete[] p ELEM * p; p = new ELEM; delete p; int n = 100; p = new ELEM[ n]; delete[] p; mechanismus výjimek Jeden objekt Pole objektů Jeden objekt

100 int main( int argc, char** argv) { char* buf;... buf = new char[ strlen(argv[1]) + strlen(argv[2]) + 1))]; strcpy( buf, argv[1]); strcat( buf, argv[2]); Velikost pole určena za běhu programu int main( int argc, char** argv) { char* buf;... buf = malloc( strlen(argv[1]) + strlen(argv[2]) + 1))); if( ! buf) error(); strcpy( buf, argv[1]); strcat( buf, argv[2]); spočítá potřebnou velikost ze vstupních parametrů pozor na koncovou '\0' nutný test na úspěšnost

101 Ladicí implementace alokačních funkcí Obložit každý alokovaný blok prostorem vyplněným značkami Při dealokaci zkontrolovat neporušenost značek a změnit je void * my_malloc( size_t s) { char * p = (char *)malloc( s+sizeof(size_t)+2*AB_SIZE); if ( ! p ) return 0; *(size_t *)p = s; memcpy( p+sizeof(size_t), AB_FILL, AB_SIZE) memcpy( p+sizeof(size_t)+AB_SIZE+s, AB_FILL, AB_SIZE) return p+sizeof(size_t)+AB_SIZE; } void my_free( void * vp) { char * p; if ( ! vp ) ERROR(); p = (char *)vp - (sizeof(size_t)+AB_SIZE); if ( memcmp( p+sizeof(size_t), AB_FILL, AB_SIZE) ) ERROR(); if ( memcmp( p+sizeof(size_t)+AB_SIZE+*(size_t *)p, AB_FILL, AB_SIZE) ) ERROR(); /*... zmenit znacky... */ free( p); } size #### velikost bloku kontrolní výplň

102 Organizace paměti procesu Kódový segment Kód programu Datový segment Globální proměnné Heap Dynamicky alokovaná data Zásobník Lokální proměnné a parametry funkcí IP R0 R1... SP

103 Organizace paměti procesu – kódový segment 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 IP R0 R1... SP int fce( int x) {... } { int (*fp)( int); fp = fce;... (*fp)(17); fp(17) alternativní syntaxe

104 Organizace paměti procesu – datový segment 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 Heap Zásobník IP R0 R1... SP int bigpole[ 1000]; char retezec[] = "CHAIN"; { int* p = bigpole; const char* s = "ahoj";... inicializované pole

105 Organizace paměti procesu - heap Kódový segment Datový segment Heap vytvářen startovacím modulem knihoven dynamicky alokovaná data new/delete malloc / free obsazené bloky různé velikosti + seznam volných bloků Zásobník IP R0 R1... SP { char* s; s = new char[128];... nemixovat

106 Organizace paměti procesu - zásobník Kódový segment Datový segment Heap Zásobník připraven operačním systémem lokální proměnné pomocné proměnné generované kompilátorem návratové adresy aktivační záznamy funkcí IP R0 R1... SP { char pole[100]; char s[] = "Ahoj"; int x = * 3;... inicializace při každém průchodu

107 Statické proměnné static int x; int fce( int a) { static int y = 0; return y += a; } globální proměnná neviditelná z jiných modulů de facto globální (!) proměnná neviditelná z jiných funkcí inicializace: C: před vstupem do main C++: před prvním průchodem C++: lepší řešení - namespace C++: raději skrýt do třídy

108 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = new char[ strlen(s)+2]; *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } co je v kterém segmentu?

109 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = new char[strlen(s)+2]; *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } program pokus pokus\0 buf p D Z H

110 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = new char[strlen(s)+2]; *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } program pokus pokus\0 buf p D Z H s p

111 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = new char[strlen(s)+2]; *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } program pokus pokus\0 buf p D Z H s p #pokus \0

112 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = new char[strlen(s)+2]; *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } program pokus pokus\0 buf p D Z H #pokus \0

113 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = new char[strlen(s)+2]; *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } program pokus pokus\0 buf p D Z H s p #pokus \0

114 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = malloc(strlen(s)+2); *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } program pokus pokus\0 buf p D Z H s p #pokus \0 ##pokus \0

115 Organizace paměti procesu – příklad const int max = 100; char buf[max]; char* prefix( char* s) { char* p = malloc(strlen(s)+2); *p = '#'; strcpy( p + 1, s); return p; } int main( int argc, char** argv) { char* p; strcpy( buf, argv[ argc – 1]); p = prefix( buf); p = prefix( p);.... } program pokus pokus\0 buf p D Z H #pokus \0 ##pokus \0

116 Organizace paměti - ukazatele na funkce typedef double DoubleFce(double); const int DF_POCET = 4; DoubleFce* fce[DF_POCET]= { cos, sin, log, tan}; double* spocti( double vysl[], DoubleFce* f, int pocet, double hod, double krok) { for( int i = 0; i < pocet; ++i) { vysl[i] = f(hod); hod += krok; } return vysledek; } int main() { const int pocet=8; const double zac=0.1; const double krok=0.4; double vysledek[ pocet]; for( int i = 0; i < DF_POCET; ++i) { spocti( fce[i], vysledek, pocet, zac, krok); zobraz( vysledek,....); } return 0; } C cos {... sin {... log {... fce D vysled. Z f vysl

117 Souborový vstup a výstup struktura definovaná v Neznámý obsah Pro knihovní funkce FILE * FILE deskriptor souboru - deklaruje programátor Otevření souboru: kontrola existence a práv vytvoření vnitřních knihovních struktur asociace s otevřeným souborem předání deskriptoru souboru (FILE*) soubor (na disku) OS soubor 'na síti'

118 #include FILE* fp; int c; if( !(fp = fopen("c:\\f.txt", "r"))) error(); while( (c = getc( fp)) != EOF) putchar( c); fclose( fp); Práce se soubory typ 'soubor' (ukazatel na strukturu) otevření souboru pojmenovávací konvence dle OS čtení ze souboru zavření souboru pozor na '\\' !!!

119 Otevření souboru FILE* fopen( const char* fname, const char* mode); ropen file for reading wtruncate to zero length or create file for writing aappend; open or create file for writing at end-of-file r+open file for update (reading and writing) w+truncate to zero length or create file for update a+append; open or create file for update, writing at end-of-file rbbinary file... modesoubor ex.soubor neex.seek rRError0 wDel, WW0 aWWEnd r+R/WR/WError0 w+Del, R/WR/WR/W0 a+R/WR/WR/WR/WEnd +: vždy čtení i zápis a: otevřít na konci r: soubor musí existovat w: soubor se smaže

120 Textové vs. binární soubory Textový soubor konverze konců řádek ('\n') na platformově závislou vnější reprezentaci typicky 0x0D 0x0A (Win) nebo 0x0A (Unix) konverze je automatická, programátor se o to nemusí starat vhodné pro ukládání lidsky čitelných dat getc / putc, fgets / fputs, fprintf,... chování fseek / ftell na '\n' nedefinován - nepoužívat Binární soubor žádné konverze se neprovádí v souboru je přesný binární obraz zapisovaných dat vhodné pro ukládání vnitřních datových struktur lidsky přímo nečitelné typicky fread / fwrite, lze i getc / putc (přístup po bajtech) fseek / ftell OK

121 Funkce pro práci se soubory FILE* fopen( const char* fname, const char* mode); int fclose( FILE* fp); int fprintf( FILE* fp, const char* format,...); int getc( FILE* fp); int putc( int c, FILE* fp); char* fgets( char* buffer, size_t limit, FILE* fp); int fputs( const char* buffer, FILE* fp); size_t fread( void* ptr, size_t size, size_t n, FILE* fp); size_t fwrite( const void* ptr, size_t size, size_t n, FILE* fp); long ftell( FILE* fp); int fseek( FILE* fp, long offset, int whence); whence: SEEK_SET, SEEK_CUR, SEEK_END int fflush( FILE *fp);

122 Práce se soubory - příklad #include const int buflen = 1024; int main(int argc, char ** argv) { FILE *f; char s[buflen]; int n; if (argc <= 1) return 1; f = fopen(argv[1], "r"); if (!f) return 2; while ((n = fread(s, 1, buflen, f)) > 0) { fwrite(s, 1, n, stdout); } fclose(f); return 0; } cat kopírování obsahu souboru na standardní výstup

123 Práce se soubory - příklad #include int main( int argc, char** argv) { FILE* f; long l; if( argc < 2) return 8; f = fopen( argv[1], "r"); if( !f) return 4; fseek( f, 0, SEEK_END); l = ftell( f); fclose( f); printf( "Delka %s je %ld.\n", argv[1], l); return 0; } zjištění skutečné velikosti souboru seek na konec zjištění pozice Pozor! long

124 Souborový vs. standardní v/v funkce pro práci se standardním vstupem/výstupem int getchar( void); int putchar( int c); int printf( const char* format,...); char* gets( char* buffer); standardní vstup/výstup FILE* stand. otevřený na čtení/zápis před vstupem do main FILE *stdin; FILE *stdout; getchar()  getc(stdin) putchar(c)  putc(c, stdout) FILE* fp = stdout; if(...) fp = fopen( "...", "r"); c = getc( fp); jednotný zápis na std výstup nebo do souboru Nepoužívat! Nelze ohlídat přetečení bufferu všechny souborové funkce lze použít i pro std. v/v

125 Knihovní funkce C strlen, strcmp, stricmp, strcpy, strncpy, strcat, strchr, strrchr, strstr, memset, memcmp, memcpy, memchr getchar, putchar, fopen, tmpfile, fclose, getc, putc, fgets, fputs, fread, fwrite, ftell, fseek, printf, fprintf, vfprintf, fflush, ungetc FILE, stdin, stdout, EOF, SEEK_SET,... malloc, free, atoi, atof, strtol, qsort, rand, exit isalpha, isdigit, isxdigit, isalnum, isspace, ispunct, iscntrl, islower, isupper, tolower, toupper abs, floor, sin, sqrt, exp, exp, log,... time, gmtime, strftime, asctime, clock, a mnoho mnoho dalších C++ - vše v prostoru jmen std

126 printf int printf( const char *,...); % [flags] [width] [.precision] [opt_pref] type %c - char - znak %d - int - decimálně %x - int - šestnáctkově %ld - long %f - double - s desetinnou tečkou %g - double - s exponentem %s - char * - řetězec fprintf( FILE*, sprintf( char*, swprintf( wchar_t*, _snprintf( char*, int n, _scprintf(... printf(":%c:%6d:%08X:%7.3f:%7.3f:\n", 'a', 17, 1234, , ); printf(":%s:%6s:%6s:%-6s:%-6.3s:%.3s:\n", "ahoj", "ahoj", "ahojbabi", "ahoj", "ahoj", "ahoj"); printf(":%*d:%-*.*s:\n", 6, 17, 6, 3, "ahoj"); printf(":%c:%c:%d:%s:\n", 0xffeedd41, "ahoj", "ahoj", 'a'); :a:▫▫▫▫17:000004D2:▫▫ :▫▫ : :ahoj:▫▫ahoj:ahojbabi:ahoj▫▫:aho▫▫▫:aho: :▫▫▫▫17:aho▫▫▫: :A:  : : místo width a/nebo precision znak * hodnota následujícího parametru Unhandled exception at 0x c : Access violation reading location 0x width: min. počet míst na výstupu precision: max. počet zpracovávaných znaků

127 Direktivy preprocesoru #include #include "mymodul.h" #define KZR #define KZR 17 #define KZR( pzr) ((pzr) * 2) #undef #ifdef #ifndef #if #else #endif # ## knihovní headery – C, C/C++, C++ uživatelské headery definice symbolu – viz Spojování modulů - #ifndef definice makra, lépe const int kzr = 17; definice parametrického makra raději vůbec nepoužívat, lépe inline funkce test na (ne)definovanost symbolu obrana před vícenásobným #include viz Spojování modulů - #ifndef test konstantního výrazu - #if sizeof( int) == 4 'ouvozovkování' - #abc  "abc" spojení identifikátorů - a##b  ab

128 x.c double A() { return B( 7); } Spojování modulů – problém hlaviček funkcí error: Undefined 'B' y.c double B() { return 3.14; }

129 x.c double B(); double A() { return B(); } Spojování modulů – externí deklarace externí deklarace y.c double B() { return 3.14; }

130 Spojování modulů – nekonzistence C: nedefinované chování C++: linker error x.c double B(); double A() { return B(); } y.c int B( int q) { return q+1; } x.obj import B export A y.obj export B app.exe nekonzistence funkce B (počet a typy parametrů, návratová hodnota)

131 Spojování modulů – header x.c #include "y.h" double A() { return B( 7); } y.c int B( int q) { return q+1; } y.h int B( int q); int B( int q); double A() { return B( 7); } preprocesor možná nekonzistence

132 Spojování modulů – řešení x.c #include "y.h" double A() { return B( 7); } y.c #include "y.h" double B() { return 3.14; } y.h int B( int q); int B( int q); double A() { return B( 7); } int B( int q); double B() { return 3.14; } error: Redefinition of 'B'

133 y.obj export c Spojování modulů – definice dat x.c #include "y.h" double A() {} y.c #include "y.h" int c; y.h int c; int c; double A() {} int c; x.obj export c export A linker error: Duplicate symbol 'c'

134 y.obj export c Deklarace vs. definice x.c #include "y.h" double A() {} y.c #include "y.h" int c; y.h extern int c; extern int c; double A() {} extern int c; int c; x.obj import c export A Deklarace Definice Deklarace Definice

135 Spojování modulů - typy x.c #include "y.h" double A() { return 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; enum T C; Definice nového typuDeklarace proměnné tohoto typu

136 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; } y.h #include "t.h" extern enum T C; z.h #include "t.h" extern enum T D; error: Type redefinition: 'T' Přes y.h a z.h je t.h vložen dvakrát

137 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 extern enum T C; #ifndef T_H_ #define T_H_ enum T { P, Q}; #endif extern enum T D; y.h #include "t.h" extern enum T C; z.h #include "t.h" extern enum T D; symbol již definován nepřekládá se není-li symbol definován... definice nového symbolu (makra) vlastní headery obkládejte ifndefy

138 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

139 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

140 Programování není zápis algoritmů Nízkoúrovňové (technické) Běhové prostředí programu Vazba programu na operační systém Přenositelnost mezi platformami Typické chyby a ochrana proti nim Ladění programů debuggerem a bez debuggeru Udržovatelné programy, kultura programování Vysokoúrovňové ('manažerské') Komunikace se 'zákazníkem' - uživatelské požadavky Analýza a specifikace, programátorské zadání Testy - aplikační, integrační, zátěžové Nasazení (deployment) Pilotní provoz, roll-out, konverze dat Dokumentace - vývojová, uživatelská Školení 'běžných uživatelů' Softwarové inženýrství Informační systémy Programování ≈ výroba software

141 Odlišnosti mezi platformami Vlastnosti hardware Velikost adresového prostoru a velikostí ukazatelů Pořadí ukládání vícebajtových hodnot (little/big endian) Dostupné formáty celých a reálných čísel Vlastnosti operačního systému Znaková sadou (ASCII/EBCDIC, Win/ISO), oddělovače řádků Konvence jmen souborů (oddělovače, povolené znaky, délka) Další vlastnosti souborového systému (sémantika delete, links, přístupová práva) Základní služby a konvence OS (environment, registry) Konvence (/usr/bin,.exe, $HOME) Vlastnosti překladače Volba velikosti základních aritmetických typů Způsob zarovnání položek struktur Rozpoznávaná pod-/nad-množinou jazyka Chyby v diagnostice a generovaném kódu Vlastnosti knihoven Dostupnost, pojmenování a sémantika funkcí

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

143 Vazba programu na operační systém Proces je izolován od ostatních procesů a jádra OS Virtuální adresový prostor, 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 Použití efektivní ale obtížné a nebezpečné Veškerá komunikace přes systémová volání 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++ Relativně pomalé (změna kontextu, přeplánování) 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, nebo pouze částečně (Microsoft)

144 Zveřejněné rozhraní operačního systému "C-API" - zpřístupněno knihovnami 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í Knihovny mohou změnit logiku systémových volání Soubory: Buffering, překlad znakových sad, statefull/stateless Standardizované rozhraní služeb OS stdio.h - souborový vstup a výstup 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 - '\n' Neřeší adresářové služby signal.h, stdlib.h - řízení procesu Vyvolání/příjem signálu (Unix-like), ukončení procesu, system("winword my.doc")

145 Vlákna (threads) 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ých druhů 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 liší Lze je implementovat i 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 Pokusy o unifikaci - nestandardních knihovny 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 - Pozor na externí knihovny! Nutnost synchronizace práce se sdílenými datovými strukturami

146 Ladění programů debuggerem Spustit program v ladicím režimu Ladicí režim typicky není pro laděný program identický s normálním Chybný program se může chovat (a často chová) při ladění jinak než finální verze Krokovat a spouštět program Zobrazovat data lokální i globální proměnné, složitější výrazy zásobník volání paměť laděného procesu Odchytit chybující program a zobrazit stav těsně před chybou Nastavovat breakpointy do kódu Mohou být podmíněné Breakpointy na data (změna dat, přístup do paměti, splnění podmínky) Některé typy mohou o několik řádů zpomalit běh programu Nevýhody Ne všude dostupné (kernely, pračky, banky, jaderné elektrárny,...) Nevhodné pro paralelní nebo dlouhodobý běh

147 Ladění programů bez debuggeru Ladicí 'tisky' (logy) Záznam důležitých okamžiků běhu programu Co, kdy a kde tisknout Jak tisknout ('monitor', soubor, databáze, pípání...) Automatické testování Testovací skripty Sady testovacích dat Lokalizace chyby Minimalizace zdrojového textu, kde se chyba vyskytuje Prevence proti zavlečeným chybám Ladicí implementace alokačních funkcí Obložit každý alokovaný blok prostorem vyplněným značkami Při dealokaci zkontrolovat neporušenost značek a změnit je

148 Bezpečné programování 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 zdánlivě 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,... Důsledná chybová diagnostika Test úspěšnosti každého malloc, fopen,... Testy proti interním chybám a špatným parametrům Ochrana proti užívání odalokovaných bloků 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 void f( int* p) { assert( p); /*...*/ } free(p); p=0; 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 při odevzdání zápočťáku

149 Udržovatelné zdrojové texty Logické rozdělení do modulů a hlavičkových souborů na nižší úrovni datové struktury a funkce, třídy Jasné oddělení rozhraní od implementace Oddělení obsluhy uživatelského rozhraní od vlastní logiky aplikace "Vstup/výstup"  "výpočet" Minimum globálních proměnných ideálně žádné, příp. jedna třída App Komentáře, zejména k rozhraním Indentace, omezená délka řádek Pojmenovávací konvence, logicky zvolené a dlouhé identifikátory Buď my_big_array nebo myBigArray, žádné pom1, pom2 Obvykle typy a konstanty začínají velkými písmeny, proměnné malými Pojmenování všech smysluplných konstant Žádné int x[ 100] ani case 27: Jaké jiné konstanty než smysluplné by měly být ve zdrojových textech? Nepoužívat deprecated features const a = 123; char *s = "abcd"; bool b; b++; správně const int a = 123; const char *s = "abcd"; (bool nelze inkrementovat)

150 Oblíbené chyby – struktura programu  Chybějící nebo přebývající středník: for (i=0; i

151 Oblíbené chyby – výrazy  Celočíselné dělení: x=1/3;  Zaokrouhlovací chyby: for (x=0; x!=1.0; x+=0.1)...  Odporující si parametry u printf: printf("%d",1.25);  Záměna & | a && || a=1; b=2; if(a&b)...  Zkrácené vyhodnocování logických výrazů  Pozor na záměnu znaků: a=1; if (a<1,5)...  Pozor na prioritu operátorů. Závorka navíc nikdy neuškodí.  Nespoléhat na pořadí vyhodnocování (následující výstupy nejsou definovány): printf("%d %d",i++,i--);  a[i++]=b[i++] může být přeloženo 3 způsoby  if (0

152 Oblíbené chyby – ukazatele  Neinicializované proměnné, obzvlášť ukazatele: char *text; strcpy( text, "Chyba!");  Ukazatel na proměnnou, která už neexistuje: char text[20]; strcpy( text,....); return text;  Chybějící nebo naopak přebývající & u parametru předávaného ukazatelem: scanf( "%d %s", i, &text);

153 101 mouder Organizační záležitosti 1. Kompilujte bez varování 2. Používejte automatizovaný systém kompilace 3. Používejte systém pro správu verzí 4. Investujte do čtení kódu Design Style 5. Jedna entita jedna zodpovědnost 6. Korektnost, jednoduchost a čitelnost především 7. Používejte škálovatelné algoritmy a konstrukce 8. Neoptimizujte předčasně 9. Neoptimizute pozdě 10. Minimalizujte lokální a sdílená data 11. Skrývejte informace 12. Naučte se správně využívat vlákna 13. Každý zdroj by měl patřit nějakému objektu Coding Style 14. Kompilační chyby jsou lepší než běhové 15. Používejte const 16. Vyhýbejte se makrům 17. Vyhýbejte se magickým číslům 18. Deklarujte proměnné s nejmenším možným rozsahem platnosti 19. Inicializujte proměnné 20. Vyhýbejte se dlouhým funkcím a přílišnému větvení 21. Vyhýbejte se inicializačním závislostem mezi moduly 22. Minimalizujte závislosti, vyhýbejte se cyklickým závislostem 23. Hlavičkové soubory by měly být samostatné 24. Používejte ochranu vícenásobného vkládání #include Sutter, Alexandrescu: C programovacích technik (C++ Coding Standards)

154 Co (a proč ) dělá tento program? #define _ F-->00||F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ } Pozor! Je v čistém C, nikoliv v C++

155 Part II - C++ C++

156 Významné vlastnosti jazyka C++ Run-time: vlastnosti C++ s reprezentací za běhu programu dědičnost (inheritance) virtuální metody, polymorfismus, pozdní vazba (virtual functions) násobná dědičnost, virtuální dědičnost (multiple/virtual inheritance) zpracování výjimek (exception handling) typová informace za běhu (RTTI) Syntaxe: 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 zobecnění (šablony) Knihovny proudy (streams) STL (Standard Template Library)

157 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 u struktury) Funkce (metody), virtuální / statické funkce metody můžou přistupovat k datovým položkám 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 virtuálních metody, výjimky, RTTI, virtuální dědičnost class A { public: A() : x(0), y(0) {} void Set( int x, int y) { x_ = x; y_ = y; } int Prod() { return x_ * y_); } private: int x_, y_; }; A a; A * pa = &a; a.Set( 1, 2); cout Prod();

158 Princip a účel dědičnosti Princip dědičnosti Třída (struktura) může mít jednoho (nebo více) předků relace předek-potomek je orientovaný acyklický graf není povinný společný prapředek pro všechny třídy Účel dědičnosti Lze napsat kód, který pracuje s daty společného předka několika tříd 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 Inherit to be reused, not to reuse Polymorfismus lze vytvořit strukturu sdružující objekty různých tříd, pokud mají společného předka

159 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 se shodným tvarem jako reprezentace samostatného objektu typu A Z každého ukazatele na typ B je možno odvodit ukazatel na část typu A konverze je implicitní, tj. není třeba ji explicitně uvádět ve zdrojovém kódu konverze může vyžadovat jednoduchý výpočet obvykle pouze při násobné dědičnosti přičtení posunutí Z ukazatele na typ A je možno odvodit ukazatel na typ B za těchto okolností: konkrétní objekt, na který ukazuje ukazatel na typ A, je typu B (nebo jeho potomka) zodpovědnost za ověření skutečného typu objektu má programátor konverzi je třeba explicitně vynutit přetypováním konverze může vyžadovat odečtení posunutí B A xyu

160 Dědičnost - reusabilita A xy pa class A { public: int x, y; }; int f( A * pa) { return pa->x * pa->y; } kód nic netušící o případných potomcích

161 Dědičnost - reusabilita B A xyu pbpa A xy 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); } volání kódu pracujícího s předkem případné konverze zajístí kompilátor automaticky

162 Dědičnost - polymorfismus C A tn u seznam B A tnxy B A tnxy 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; }; seznam objektů se společným předkem

163 Dědičnost - polymorfismus C A tn u pbpa seznam B A tnxy B A tnxy 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; } odkaz na objekt odvozené třídy zodpovědnost programátora za korektnost

164 Násobná dědičnost B A u xy C A xz B A xy C A xz D A x 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 = (D *)ap; // explicitní přetypování

165 Virtuální dědičnost B A u xy C A xz B A xy C z D A x 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 = (D *)ap; chyba: nelze najít

166 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

167 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 Objekt typu B má být použitelný kdekoliv, kde se používá typ A B je specializací 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 Is-A relationship jezevčík JE pes pes JE zvíře

168 Nesprávné užití dědičnosti - kompozice 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 class Letadlo {... Motor motor[N]; }; class Letadlo : public Motor {}; class Letadlo : public Motor, public Motor {}; class Tlacitko {... Icon ico; }; class ZamackavaciTlacitko {... Icon icoOn; Icon icoOff; };

169 Nesprávné užití dědičnosti - delegace 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 Windows  Delegace Převedení funkčnosti na jiný objekt C++: Ukazatel class JM1 {... Myslivec* mysl; Jezevcik* jez; }; class JM1 : public Myslivec, public Jezevcik {};

170 Nesprávné užití dědičnosti - abstraktní předek Mlok není potomkem ryby a savce Důkaz: Nemá dvě hlavy... Virtuální dědičnost? Ne: Nekojí Abstraktní předek obratlovec Tlačítko není potomkem obdélníku a textu Abstraktní předek vizuální objekt  Společný abstraktní předek class Obratlovec {}; class Ryba : public Obratlovec {}; class Savec : public Obratlovec {}; class Obojzivelnik : public Obratlovec {}; class Mlok : public Ryba, public Savec {}; class Mlok : virtual public Ryba, virtual public Motor {};

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

172 Potomka lze přiřadit do předka (platí i pro ukazatele) Předka NELZE přiřadit do potomka (platí i pro ukazatele) Kompatibilita předka a potomka pes umí jíst, brouk neumí štěkat azor class zvire {}; class pes : public zvire {}; zvire pytlik, *pz; pes azor, *pp; pytlik = azor; *pz = &azor; azor = pytlik; *pp = &pytlik; stav žaludek pytlik žaludek stav žaludek pytlik azor ??? nelze !

173 class A { public: int x, y; }; int f( A * pa) { return pa->x * pa->y; } A oa; A * pa = & oa; int u = f( &oa); int v = f( pa); class A { private: int x, y; public: int f(); }; int A::f() { return x * y; } A oa; A * pa = & oa; int u = oa.f(); int v = pa->f(); Metoda (member function) this->x Každá metoda dostane 'tajný' parametr this - ukazatel na objekt A:: => skrytý parametr A* this Metoda (member function) = funkce uvnitř třídy implementace (tělo) metody deklarace (hlavička) metody volání metody chráněné položky přístupné pouze metodám třídy veřejné položky přístupné komukoliv

174 Metoda - umístění funkce uvnitř třídy Vyvolání na konkrétní instanci třídy Při volání funkce je 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é 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čí (private) Funkce může přistupovat k chráněným prvkům třídy (private)

175 Tělo metody uvnitř a vně třídy Tělo uvnitř třídyInline tělo vně třídy Ekvivalent těla uvnitř Kompilátor se pokusí rozvinout tělo Tělo v hlavičkovém souboru class A { public: int x, y; int f() { return x * y; } }; class A { public: int x, y; int f(); }; inline int A::f() { return x * y; } int A::f() { return x * y; } class A { public: int x, y; int f(); }; Tělo vně třídy Ekvivalent externí deklarace a definice globální funkce Tělo v.cpp souboru.h.cpp inline: kompilátor se pokusít tělo funkce rozvinout v místě volání

176 Konstantní metody konstantní metoda nemodifikuje objekt, na kterém je vyvolána lze aplikovat na konstantní objekt class A { public: int x, y; int f() const { return x * y; } }; const A * pa;... pa->f(); class A { public: int x, y; }; int f( const A * pa) { return x * y; } const A* this

177 class A { private: int x, y; public: int f() { return x * y; } }; class B : public A { private: int z; public: int g() { return f() * z; } }; B ob; A * pa = & ob; B * pb = & ob; int u = pa->f(); int v = pb->f(); int w = pb->g(); Metody základních a odvozených tříd automatická konverze na ukazatel na předka volání metody základní třídy A::f() volání metody odvozené třídy

178 class A { private: int x, y; public: int f() { return x * y; } }; class B : public A { private: int z; public: int f() { return z; } }; B ob; A * pa = & ob; B * pb = & ob; pa->f();// A::f pb->f();// B::f pb->A::f();// A::f pb->B::f();// B::f pa->A::f();// A::f // pa->B::f(); nelze Zakrývání metod – compile time binding volání odpovídající metody podle typu objektu stejná metoda jako předka - zakrývání kvalifikované volání – explicitně určím která metoda se má volat nelze (takhle) kvalifikovaně volat funkce odvozených tříd

179 Implicitní parametry int fce( int a, int b = 2, int c = 4) { return 2*a + b - c; } fce( 1);// int fce( 1, 2, 4) fce( 1, 5);// int fce( 1, 5, 4) fce( 1, 5, 6);// int fce( 1, 5, 6) Některé parametry funkce mohou mít implicitní hodnoty pokud nejsou všechny parametry implicitní – implicitní parametry odzadu Při volání funkce lze implicitní parametry vynechat použije se implicitní hodnota 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ě Volá se stále stejná funkce int fce( int, int, int)

180 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 vhodné varianty funkce podle počtu a typu skutečných parametrů Určí se ceny typových konverzí parametrů, zjednodušeně: Konverze non-const -> const / typ reference jsou nejlevnější Konverze potomek -> předek / aritmetická konverze na větší typ Uživatelská konverze / ztrátová aritmetická konverze jsou nejdražší Vybere se nejlacinější aplikovatelná varianta Pokud je jich více, kompilátor ohlásí chybu int pocitej( int x); int pocitej( int a, int b); int pocitej( int a, const char* s);

181 Přetěžování funkcí int pocitej( int x) { return x + 1; } int pocitej( int a, int b) { return 2 * a + b; } int pocitej( int a, const char* s) { return a + strlen( s); } pocitej( 1); // int pocitej( int) pocitej( 1, 2); // int pocitej( int, int) pocitej( 1, "ahoj"); // int pocitej( int, char*) Funkce je definována svým identifikátorem a počtem a typem parametrů Funkce se stejným identifikátorem ale různým počtem parametrů Správná funkce podle počtu a typů skutečných parametrů Funkce se stejným počtem ale různým typem parametrů

182 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) min( 1, 2.0);// min( double, double) min( 1.0, 2);// min( double, double) min( 1.0, 2.0);// min( double, double) void f( int, double); void f( double, int); f( 1, 2);// chyba f( 1.0, 2.0);// chyba přesná shoda levnější varianta přesná shoda obě varianty stejně drahé

183 Přetěžování vs. implicitní parametry Kdy použít přetěžování a kdy implicitní parametry? Implicitní parametry Stejný kód pro různý počet parametrů Místo chybějících parametrů se dosadí implicitní hodnoty Přetěžování Pro různé počty nebo typy parametrů různý kód

184 Reference C++ definuje vedle deklarátoru ukazatele * deklaráror reference & z hlediska přeloženého kódu jsou oba deklarátory ekvivalentní ukazatel i reference na objekt jsou reprezentovány adresou objektu odlišné je jejich používání ve zdrojovém textu Výraz typu reference má hodnotu objektu, na nějž reference odkazuje tento objekt se určuje při inicializaci reference odkaz na něj trvá po dobu platnosti referenční proměnné (nebo objektu, jehož je reference součástí) identita reference a referencovaného objektu implicitní typová konverze T T & int & ref = data; void f( int & u); int & g();

185 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

186 Reference Novinky související s existencí reference Inicializaci reference 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í Nulovou referenci je obtížnější vytvořit i testovat int & f() { int x; return x; } int * p = 0; int & r = * p; if ( ! & r ) //...

187 class bod { private: int x, y; public: … bod operator+( const bod&); bod operator=( const bod&); }; bod a, b, c; c = a + b; Přetěžování operátorů přetížení operátoru + a + b  a.operator+(b) a = b  a.operator=(b) c.operator=( a.operator+( b)); c.assign( a.add( b)); bod bod::operator=( const bod& b) { x = b.x; y = b.y; return *this; } bod bod::operator+( const bod& b) { return bod( x+b.x, y+b.y); } Těla metod

188 Přetěžování operátorů - pravidla 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 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í Typy skutečných operandů nemusejí přesně odpovídat typům formálních parametrů Pro výběr správné varianty 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 Operátory, které jsou metodami, jsou s výjimkou operátoru přiřazení dědičné a smějí být virtuální class bod { bod operator+( const bod&); }; Bod& operator+( const bod&, const bod&);

189 Přetěžování operátorů - binární operátory běžné binární operátory: + - * / % > = >= ^ & | && || == != += -= *= /= %= ^= &= |= ->* lze předefinovat jako globální funkce nebo metody binární operátor [ ] lze předefinovat pouze metodou A B::operator xxx( C) A B::operator xxx( const C &) A B::operator xxx( const C &) const A operator xxx( B, C) A operator xxx( B &, C &) A operator xxx( const B &, const C &) A B::operator []( C) A B::operator []( C &) A B::operator []( const C &) const není zde operator= speciální metoda třídy speciální operátory - > a () později

190 Přetěžování operátorů - unární operátory unární operátory + - * & ~ ! a prefixové operátory lze předefinovat jako globální funkce nebo metody postfixové operátory ++ a -- lze předefinovat jako globální funkce nebo metody A operator xxx( B) A operator xxx( B &) A operator xxx( const B &) A B::operator xxx() A B::operator xxx() const A operator xxx( B, int) A operator xxx( B &, int) A operator xxx( const B &, int) A B::operator xxx( int) A B::operator xxx( int) const fiktivní operand typu int ++X X++

191 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 default constructor, copy constructor 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í class A { private: int x; public: A() {} A( int x_) {} ~A() {} };

192 Konstruktor, seznam inicializátorů class A { private: int x; int y; public: A( int x_, int y_) { x = x_; y = y_; } A( int x_, int y_) : x(x_), y(y_) {} }; seznam inicializátorů položek member initializer list zavolají se konstruktory složek s parametry dle seznamu inicializátorů volání konstruktorů je podle pořadí v deklaraci třídy, ne podle pořadí inicializátorů na složky, které nejsou v seznamu inicializátorů, se použije implicitní konstruktor nakonec se zavolá tělo konstruktoru povinné použití seznam inicializátorů: inicializace const položek inicializace referencí předávání argumentů konstruktorům předka nebo vloženého objektu vždy preferujte seznam inicializátorů parametrický konstruktor vs. implicitní konstruktor + přiřazení

193 Implicitní a kopírovací konstruktor Implicitní konstruktor (bez parametrů, default constructor) Proměnné bez inicializace, dynamická alokace polí Pokud třída nemá žádný konstruktor, kompilátor se jej pokusí vygenerovat 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 Kopírovací konstruktor (copy constructor) Používán pro předávání parametrů a návratových hodnot Pokud třída kopírovací konstruktor nemá, kompilátor se jej pokusí vygenerovat Položky, které nejsou třídami (POD), jsou kopírovány Na předky a položky se volá kopírovací konstruktor To nemusí jít kvůli ochraně přístupu (konstruktor v sekci private) class A { A(); } class A { A(const A&); }

194 Konstruktory class A { private: int x; int y; public: A() : x(-1), y(-1) {} A( int x_, int y_) : x(x_), y(y_) {} A( const A& a) : x(a.x), y(a.y) {} }; A a1;// A::A() A a2( 1, 2);// A::A(int,int) A a3( a2);// A::A(const A&) A a4 = a2;// A::A(const A&) A* pa; pa = new A; pa = new A( 2,3); pa = new A( a4); pa = new A[ 100]; implicitní konstruktor default constructor copy (kopírovací) konstruktor vytvoří objekt jako kopii jiného různé zápisy použití copy konstruktoru alokace pole - pouze implicitní konstruktor Pro U≠T není ekvivalentní: U u; T t(u); // T::T( U&) T t = u; // T::T( T(u)) nebo // T::T( u.operator T())

195 Konstruktory a dědičnost class B : public A { private: int z; public: B() : z(-1) {}; B( int x_, int y_, int z_) : A(x_,y_), z(z_) {} B( const B& b) : A(b), z(b.z) {} }; B b( 1, 2, 3);// B::B(int,int,int) A a(b1);// A::A(const A&) // B ba(a);// error A* pa = new B;// B::B() // B* pb = new A;// error class A { private: int x; int y; public: A() : x(-1), y(-1) {} A( int x_, int y_) : x(x_), y(y_) {} A( const A& a) : x(a.x), y(a.y) {} }; neimplicitní konstruktor předka pouze přes seznam inicializátorů dědičnost – na místě předka mohu použít potomka před vstupem do těla konstruktoru potomka se provedou kostruktory předků a položek Konstrukce součástí se děje před konstrukcí celku, destrukce součástí po destrukci celku implicitní konstruktor předka

196 Vznik a zánik objektů – lokální proměnné Lokální proměnné pro lokální objekt je kostruktor vyvolán při průchodu deklarací při zániku lokálního objektu (opuštění bloku s deklarací jakýmkoli způsobem) je vyvolán jeho destruktor deklarace objektu může být kdekoliv uvnitř těla funkce nebo složeného příkazu rozsah platnosti objektu je od místa deklarace po konec složeného příkazu skoky, které by vstupovaly do bloku a obcházely deklaraci, jsou zakázány void f( void) { int x; x = 1; A a; if( x) { B b; x = 2; } if( !x) return; x = 3; } A::~A A::A B::B B::~B

197 Vznik a zánik objektů – parametry předávané hodnotou Parametry předávané hodnotou copy konstruktor, konstruktor s jedním parametrem 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 automaticky destruktor objektu je vyvolán před návratem z funkce void f( A a) { } // A::~A void g() { A a; B b; // class B : public A f( a); // A::A( const A &) f( b); // A::A( const A &) f( 1); // lze pokud ex. A( int) } Na místě předka můžu mít potomka

198 Vznik a zánik objektů – globální proměnné Globální a statické proměnné pro každý globální objekt je vyvolán konstruktor před vstupem do funkce main pro každý statický objekt je vyvolán konstruktor nejpozději při průchodu deklarací po opuštění main (nebo po zavolání exit) je pro každý globální objekt vyvolán destruktor pořadí vyvolávání je implementačně závislé ! A a( 1), b; A d( a); f() { static A e( 1); } int main() { f(); } při vyvolání konstruktoru d nemusí být a inicializováno!

199 Vznik a zánik objektů – dynamicky alokované objekty Dynamicky alokované objekty pro dynamickou alokaci slouží operátory new a delete funkce malloc a free s nimi nelze kombinovat v C++ raději nepoužívat operátor new alokuje pro objekt paměť, vyvolává konstruktor dle parametrů a vrací ukazatel na vytvořený objekt pokud se (nedostatek paměti) alokace nezdaří: C++ 98 a novější: vyvolá se výjimka std::bad_alloc starší C++: konstruktor se nevyvolává a operátor vrací nulový ukazatel vrácení nulového ukazatele při nezdaru lze vynutit: new( nothrow ) A operátor delete vyvolává destruktor objektu a poté dealokuje paměť je odolný proti nulovým ukazatelům parametr delete POUZE alokovaný a doposud neodalokovaný objekt A *p, *q; p = new A; q = new A( *p); /*... */ delete p; delete q; A::A() A::A( const A&) A *p, **q; p = new A[ 20]; q = new A*[ 20]; /*... */ delete[] p; delete[] q; pole ukazatelů

200 Vznik a zánik objektů – dočasné objekty Dočasné objekty užití jména třídy jako jména funkce v operátoru volání způsobí: vyhrazení místa pro objekt na zásobníku 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 konstruktor s jedním parametrem je konverzní konstruktor lze použít jako typovou konverzi (function-style cast) A a, b; a = A(); b = A( 1); return A( 2); konstruktor, kopie, destruktor konverzní konstruktor z int vyrobí A použití jako návratové hodnoty

201 Vznik a zánik objektů – příklad A f( A y) { return y; } A p, q; p = f( q); kolik konstruktorů a jakých vyvolá zpracování této řádky?

202 Vznik a zánik objektů – příklad A f( A y) { return y; } A p, q; p = f( q); kolik konstruktorů a jakých vyvolá zpracování této řádky? odpověď: to nikdo neví proč? norma C++: kompilátor může ale nemusí vytváření objektů optimalizovat vždy ale platí: pokud se zavolá konstruktor, vždy se zavolá i destruktor jiná otázka: jak to tedy může být v nejhorším a v optimalizovaném případě?

203 Vznik a zánik objektů – hloupý kompilátor A f( A y) { return y; } A p, q; p = f( q); 0. pro návrat z funkce se alokuje místo pro dočasnou proměnnou temp2 1. q  y předání hodnoty skutečného parametru do funkce copy konstruktor 2. y  temp1 'spočítá' se hodnota výrazu pro return a vytvoří se dočasný objekt copy konstruktor 3. temp1  temp2 předání návratové hodnoty ven z funkce copy konstruktor 4. temp2  p přiřazení návratové hodnoty funkce proměnné operátor přiřazení

204 Vznik a zánik objektů – chytrý kompilátor A f( A y) { return y; } A p, q; p = f( q); 1. q  y předání hodnoty skutečného parametru do funkce copy konstruktor 2. y  temp 'spočítá' se hodnota výrazu pro return a vytvoří se dočasný objekt copy konstruktor 4. temp  p přiřazení návratové hodnoty funkce proměnné operátor přiřazení

205 odlišné chování potomků – pozdní vazba (late binding) Polymorfismus najde něco v přírodě zvířepespitbul člověk jez sní maso sní hodně masa jde do restaurace

206 Polymorfismus - motivace class zvire { jez() { priroda(); } }; class pes : public zvire { jez() { maso(1); } }; class pitbul : public pes { jez() { maso(10); } }; class clovek : public zvire { jez() { hospoda(); } }; zvire pytlik; pes punta; pitbul zorro; clovek pepa; pytlik.jez();// priroda(); punta.jez();// maso(1); zorro.jez();// maso(10); pepa.jez();// hospoda(); Tohle není polymorfismus ! Funkce je známa v době překladu 'normální' vlastnost tříd zakrývání metod Při překladu je známo ze které třídy se volá metoda Každá třída má vlastní implementaci (tělo) metody jez

207 Polymorfismus – pozdní vazba zvire* rodina[4]; rodina[0] = new clovek; rodina[1] = new pes; rodina[2] = new pitbul; rodina[3] = new zvire; for( int i = 0; i < 4; ++i) rodina[i]->jez(); Chci pokaždé se zavolat jinou metodu Rozlišení metody se musí dít za běhu Pozdní vazba (late binding; virtual call) 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 Má smysl pouze u vyvolání na objektu určeném odkazem chtěl bych, aby se volaly 'správné' metody

208 Virtuální metody - deklarace class zvire { virtual jez() { priroda(); } }; class pes : public zvire { virtual jez() { maso(1); } }; class pitbul : public pes { virtual jez() { maso(10); } }; class clovek : public zvire { virtual jez() { hospoda(); } }; magické klíčové slovo virtual každý objekt si s sebou nese informaci kterou virtuální funkci používá

209 Virtuální metody - implementace Princip implementace každý objekt obsahuje ukazatel na tabulku virtuálních funkcí tabulka ukazatelů na funkce při volání metody se volá tělo funkce z této tabulky B y A x f g A::f A::g A x f g B::f class A { public: int x; virtual int f() { return 1; } virtual int g() { return 2; } }; class B : public A { public: int y; virtual int f() { return 9; } };

210 Virtuální metody - implementace class A { public: int x; virtual int f() { return 1; } virtual int g() { return 2; } }; class B : public A { public: int y; virtual int f() { return 9; } }; A a; B b; A* pa = &b; pa->f(); int A_f( struct A* th) { return 1; } int A_g( struct A* th) { return 2; } struct A { int (*pf)( A*); int (*pg)( A*); int x; int f() { return pf( this); } int g() { return pg( this); } A() { pf = A_f; pg = A_g; } }; int B_f( struct B* th) { return 9; } struct B : public A { int y; B() { pf = (int(*)(A*))B_f; } }; A a; B b; A* pa = &b; pa->f();

211 Volání virtuálních metod class A { public: virtual f(); }; class B : public A { public: virtual f(); }; A a; B b; A * paa = &a; A * pab = &b; B * pbb = &b; // B * pba = &a; nelze! a.f(); // A::f b.f(); // B::f paa->f(); // A::f pab->f(); // B::f pbb->f(); // B::f b.A::f(); // A::f b.B::f(); // B::f a.B::f(); // NE! paa->A::f(); // A::f pab->A::f(); // A::f pab->B::f(); // NE! pbb->A::f(); // A::f pbb->B::f(); // B::f pozdní vazba b B::f paa A::f a pab pbb kvalifikované volání zakrývání metod

212 Virtuální metody a konstruktory a destruktory class A { public: virtual f(); A() { f(); } // A::f ~A() { f(); } // A::f g() { f(); } // A/B::f }; class B : public A { public: virtual f(); B() { f(); } // A::A B::f ~B() { f(); } // B::f A::~A g() { f(); } // B::f }; nejdřív se zavolá konstruktor předka nejdřív se provede kód destruktoru, pak se zavolá destruktor předka v konstruktoru a destruktoru se vždy volá metoda vytvářeného/rušeného objektu určí se za běhu podle skutečného typu objektu Konstruktor provádí i vyplnění skrytých položek třídy, 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 celkem. Analogický mechanismus funguje i při destrukci objektů.

213 Dědičnost a destruktory int x=0; int ya=0; int yb=0; class A { public: virtual f() { x=1; ya=1; } ~A() { f(); } // A::f }; class B : public A { public: virtual f() { x=2; yb=2; } ~B() { f(); } // B::f A::~A }; A * pa = new B; delete pa; co udělá tohle? x=1 ya=1 ya=2 ? x=1 ya=1 ya=0 ? něco jiného?

214 Dědičnost a nevirtuální destruktory – POZOR! int x=0; int ya=0; int yb=0; class A { public: virtual f() { x=1; ya=1; } ~A() { f(); } // A::f }; class B : public A { public: virtual f() { x=2; yb=2; } ~B() { f(); } // B::f A::~A }; A * pa = new B; delete pa; co udělá tohle? - zformátuje disk - pošle zdrojáky mailem - zaviruje počítač -... cokoliv! delete objektu odvozené třídy přes ukazatel na základní třídu s nevirtuálním destruktorem je NEDEFINOVÁN

215 int x=0; int ya=0; int yb=0; class A { public: virtual f() { x=1; ya=1; } virtual ~A() { f(); } // A::f }; class B : public A { public: virtual f() { x=2; yb=2; } virtual ~B() { f(); } // B::f A::~A }; A * pa = new B; delete pa;// x=1 ya=1 yb=2 Virtuální destruktory nejdříve se provede tělo ~B, potom se zavolá ~A Má-li být třída předkem určeným pro ukazatele na dynamicky vytvářené potomky, musí mít virtuální destruktor.

216 Čistě virtuální metody, abstraktní třída Č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 Často neobsahuje žádné datové položky B A f A fB::f class A { public: virtual int f() = 0; }; class B : public A { public: virtual int f() { return 9; } };

217 Čistě virtuální metody, abstraktní třída int armada; class vojak { public: enum THod { vojin, desatnik, porucik, general }; vojak( THod hod = vojin) { hodnost=hod; armada++; } virtual void pal() = 0; virtual ~vojak() { armada--; } private: THod hodnost; }; class samopal {}; class kalasnikov : public samopal {}; class pesak : public vojak { private: samopal* sam; public: pesak( THod hod=vojin) : vojak( hod) { sam = new kalasnikov; } virtual void pal() { sam->pal(); } virtual ~pesak() { delete sam; } }; pure virtual function abstraktní třída společné rozhraní Nutný virtuální destruktor abstraktní třída nelze vytvořit objekt společný předek

218 Statická data class A { private: int x; static int c; static char* dny[]; static const int MAX = 100; public: int f() { return x + c + MAX; } }; int A::c; const char* A::dny[] = { "Po", "Ut",... }; A a; a.f(); Statická data class variables (proměnné třídy) pouze jedna kopie bez ohledu na počet instancí objekty statická data sdílejí není součástí instancí (objektů) nutnost definice! časté použití: konstanty třídy jednoduché konstanty nemusí mít definici strukturované konstanty mít definici musí např. řetězce potřebuje paměť  potřebuje definici nekonstantní statická proměnná strukturovaná statická konstanta statická konstanta

219 Statické metody class A { private: int x; static int y; public: int f() { return x + y; } static int g() { return y; } static int h( A* a) { return a->x; } }; A a; a.f(); a.g(); A::g(); A::h(&a); Statické metody nedostávají automatický parametr this protože nepracují s žádnou konkrétní instancí mohou přímo přistupovat pouze k statickým datům k nestatickým datům mohou přistupovat přes specifikovaný objekt mohou přistupovat k private položkám nemohou být virtuální lze je volat jako metody nebo jako globální funkce a.g() A::g()

220 class A { int f( int a, int b); virtual int f( int a, int b); virtual int f( int a, int b) = 0; static int f( int a, int b); } Přehled druhů metod metoda member function virtuální metoda virtual function čistě virtuální metoda pure-virtual function statická metoda static member function

221 Ukládací třídy (storage classes) dataglobálníuvnitř funkceuvnitř třídy bez specifikace statická alokace, export mimo modul = 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 statická alokace (jediná instance) statická alokace (jediná instance), export extern odkaz na globální definici, bez alokace zakázáno funkceglobálníuvnitř funkceuvnitř třídy bez spec.export mimo modulzakázáno metoda - implicitní this virtual zakázáno virtuální metoda static bez exportu mimo modulzakázáno bez this, export

222 Ochrana přístupu Položky tříd jsou z hlediska ochrany přístupu rozděleny na public: veřejně přístupné položky protected: položky přístupné metodám třídy a metodám tříd 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 inheritance: práva zůstávají beze změn private inheritance: všechny položky předka jsou v nové třídě privátní jediný rozdíl mezi struct a class

223 private inheritance public inheritance - Is-A potomek JE vše, co předek private inheritace potomek NENÍ vše, co předek metody předka jsou private..... co to teda je??? class Osoba { void jist(); }; class Student : public Osoba { void studovat(); }; Osoba o; Student s; s.jist();// Student JE Osoba // o.studovat();// Osoba NENÍ Student class Osoba { void jist(); }; class Student : Osoba { void studovat(); }; Osoba o; Student s; // s.jist();// Student NENÍ Osoba // o.studovat();// Osoba NENÍ Student

224 private inheritance private inheritace ' is-implemented-in-terms-of ' odvozená třída může využít kód základní třídy dědičnost implementace, nikoliv rozhraní položka třídy (containment, layering) vs. private inheritance vždy, pokud to lze, používejte containment private inheritance používejte pokud to jinak nelze class A { f(); }; class B : private A { public: g() { A::f(); } }; class A { f(); }; class B { private: A a; public: g() { a.f(); } };

225 private inheritance & protected úkol: obecný zásobník, zásobníky konkrétních typů, ochrana nevýhody: neexistuje ochrana, kdokoliv může přistupovat k Stack a void* class Stack { // implem. class public: Stack(); ~Stack(); void push( void* object); void* pop(); private: struct Node { void* data; Node* next; }; Node* top; Stack( const Stack&); Stack operator=( const Stack&); }; class IntStack {// interface class public: void push( int* p) { s.push(p); } int* pop() { return (int*)s.pop(); } private: Stack s; }; class Slon {}; class SlonStack { public: void push( Slon* p) { s.push(p); } Slon* pop() { return (Slon*)s.pop(); } private: Stack s; };

226 private inheritance & protected private inheritace, protected constructors & methods výhody: Stack nelze zkonstruovat ani volat jeho metody každý musí použít iterface class class Stack { protected: Stack(); ~Stack(); void push( void* object); void* pop(); private:.... }; class IntStack : private Stack { public: void push( int* p) { Stack::push(p); } int* pop() { return (int*)Stack::pop(); } }; class Slon {}; class SlonStack : private Stack { public: void push( Slon* p) {push(p); } Slon* pop() { return (Slon*)Stack::pop(); } };

227 Spřátelené funkce a třídy - friend class X { private: int x; public: friend class Y; friend int patchX( X& x); }; class Y { int patchX( X& x) { return x.x = 1; } }; int patchX( X& x) { return x.x = -1; } friend funkce a třídy možnost přístupu k private položkám třídy možno použít na třídy nebo funkce nepříliš bezpečné - pokud možno nepoužívat friend funkce nebo třída závisí na implementaci třídy... a když už používat, tak jen v dobře odůvodněných případech např. operator overloading spřátelená třída spřátelená funkce Pozor! Nejde o metodu X!

228 Symetrický operátor - metoda class complx { private: int re, im; public: complx( int _re = 0, int _im = 0) { re = _re; im = _im; } complx operator+( const complx& b) const { return complx( re + b.re, im + b.im); } }; complx x(1,2); complx y; y = x + 1; // y = 1 + x;error: binary '+' : no global operator found konverze: konstruktor cmplx(1,0)

229 Symetrický operátor - globální funkce class complx { private: int re, im; public: complx( int _re = 0, int _im = 0) { re = _re; im = _im; } friend complx operator+( const complx& a, const complx& b) { return complx( a.re + b.re, a.im + b.im); } }; complx x(1,2); complx y; y = x + 1; y = 1 + x; Symetrické operátory je vhodné implementovat jeko friend globální funkce

230 Konverzní operátor class complx { private: int re, im; public: complx( int _re = 0, int _im = 0) { re = _re; im = _im; } operator int() { return (re + im) / 2; } }; complx x(3);// complx( 3, 0); int i(x);// complx::operator int() uživatelsky definovaná konverze jednoho typu na jiný syntaxe: operator (); operator int(); operator char*(); nemá žádné parametry konvertovanou hodnotou je objekt, na který je operátor vyvolán nemá návratový typ typ je jednoznačně určen samotným operátorem užitečné např. pro kombinaci C a C++ kódu string vs. char*

231 Konverzní operátor class complx { private: int re, im; public: complx( int _re = 0, int _im = 0) { re = _re; im = _im; } friend complx operator+( const complx& a, const complx& b) { return complx( a.re + b.re, a.im + b.im); } operator int() { return (re + im) / 2; } }; complx x(1,2); x = x + 1;// error: more similar conversions Pozor! Konverzní operátory mohou kolidovat s jinými způsoby konverze konverzní konstruktory, integrální promoce, built-in operátory... používat s rozmyslem! complx::complx( int _re, int = 0) complx operator +(const complx &,const complx &) complx::operator int() operator+(int, int)

232 Prostory jmen (namespaces) namespace aa { int p; int f1( int x) { return x + p; } } namespace aa { void f2( int x, int y); } using namespace std; int aa::f2( int x, int y) { cout << p * (x + y); } using aa::f2; aa::f1( f2( 5, 6)); zapouzdření identifikátorů prevence kolizí (velké projekty, knihovny) stejné identifikátory v různých prostorech jmen prostor jmen se může opakovaně otevírat a zavírat standardní knihovny – namespace std definice prostoru jmen přístup ze stejného prostoru definice funkce mimo prostor jmen direktiva using - rozbalení std znovuotevření prostoru aa deklarace using - pouze ident. Později: argument dependent lookup Koenig lookup

233 Příklad - polymorfní datové struktury Zadání: kontejner obsahující čísla libovolného typu (int, double, řetězec, complex,...) Technické upřesnění: třída Seznam operace append, print společný předek prvků AbstractNum konkrétní prvky IntNum, DoubleNum,... stačí jednoduchá implementace polem pole objektů vs. pole odkazů IN x AN DN d AN IN x AN S

234 Polymorfní datové struktury - kostra tříd class Seznam { public: void append( AbstractNum *p); void print(); Seznam(); ~Seznam(); private: enum { MAX = 100 }; AbstractNum* pole[MAX]; int n; }; int main(int argc, char** argv){ Seznam s; s.append( new.... ); s.print(); return 0; } class AbstractNum { public: virtual void print()=0; virtual ~AbstractNum() {} }; abstraktní předek umí existovat a vytisknout se virtuální destruktor! přidávání dynamicky vytvořených konkrétních typů

235 Polymorfní datové struktury - implementace metod class Seznam { public: void append( AbstractNum *p); void print(); Seznam(); ~Seznam(); private: enum { MAX = 100 }; AbstractNum* pole[MAX]; int n; }; // konstruktor Seznam::Seznam() { for(int i=0; iprint(); } // pridani prvku do seznamu void Seznam::append(AbstractNum* p) { if (n

236 Polymorfní datové struktury - konkrétní datové typy class IntNum : public AbstractNum { public: IntNum(int x_) { x=x_; } virtual ~IntNum() {} virtual void print() { printf("%d ",x); } private: int x; }; class IntDouble : public AbstractNum { public: IntDouble(double d_) { d=d_; } virtual ~IntDouble() {} virtual void print() { printf("%f ",d); } private: double d; }; Seznam s; s.append(new IntNum(234)); s.append(new DoubleNum(1.45)); s.append(new IntNum(67)); s.print(); konkrétní datové typy implementují vlastní metody jednotného rozhraní kontejner obsahuje různé typy... a všechny vytiskne konkrétní datové typy implementují vlastní metody jednotného rozhraní

237 Polymorfní datové struktury - konstruktor const položek class IntNum : public AbstractNum { public: IntNum(int x_) { x=x_; } private: const int x; }; Požadavek: co když chci zakázat měnit hodnotu prvků compiler error: x must be initialized in constructor base / member initializer list class IntNum : public AbstractNum { public: IntNum(int x_) : x(x_) {} private: const int x; }; seznam inicializátorů používejte všude, kde to lze

238 Polymorfní datové struktury - přiřazení int main(int argc, char** argv){ Seznam s, s2; s.append(new IntNum(234)); s.append(new DoubleNum(1.45)); s.append(new IntNum(67)); s.print(); s2 = s; return 0; } Problém: přiřazení seznamů Je to korektní kód?

239 Polymorfní datové struktury - přiřazení Problém: přiřazení seznamů Spadne to! Kde? v destruktoru ~Seznam Proč? Navíc: memory leaks int main(int argc, char** argv){ Seznam s, s2; s.append(new IntNum(234)); s.append(new DoubleNum(1.45)); s.append(new IntNum(67)); s.print(); s2 = s; return 0; } Tady! problém je v s2 = s ; v Seznam není operator= kompilátor si ho vyrobí automaticky okopíruje datové položky a ukazatele !!! destruktor s2 dealokuje prvky destruktor s znovu odalokuje prvky bloky už ale neexistují !

240 Polymorfní datové struktury - přiřazení Možné řešení: zakázání přiřazení class Seznam { public: void append( AbstractNum *p); void print(); Seznam(); ~Seznam(); private: Seznam& operator=(const Seznam&); enum { MAX = 100 }; AbstractNum* pole[MAX]; int n; }; operator= v sekci private znemožní přiřazení seznamů stačí pouze deklarace (bez těla) nikdo ho nemůže zavolat je to už teď konečně korektní ??

241 Polymorfní datové struktury - copy konstruktor Není! copy konstruktor! class Seznam { public: void append( AbstractNum *p); void print(); Seznam(); ~Seznam(); private: Seznam& operator=(const Seznam&); Seznam(const Seznam&); enum { MAX = 100 }; AbstractNum* pole[MAX]; int n; }; je to už teď konečně korektní ?? int main(int argc, char** argv){ Seznam s; s.append(new IntNum(234)); s.append(new DoubleNum(1.45)); s.append(new IntNum(67)); s.print(); Seznam s2 = s; return 0; } oprator= a copy konstruktor by se měly chovat stejně!

242 Polymorfní datové struktury - přiřazení Pokud chceme dovolit přiřazení (kopírování), je nutné si ujasnit logiku má nebo nemá se změna projevit i v druhém seznamu? kopie hodnot (alespoň logická) nebo jen kopie datové struktury? typicky: chování takové, jako kdyby se okopírovaly všechny prvky U každé třídy obsahující odkazy na dynamicky alokovaná data buď zakázat přiřazení operator= a copy konstruktor do sekce private nebo nadefinovat kopírování napsat vlastní duplikaci  VŽDY napsat hlavičku operatoru = a copy konstruktoru! Seznam a, b;.... b = a; a[1]->x = 999;// b[1]->x ???

243 Polymorfní datové struktury - kopie prvků class Seznam { public: void append( AbstractNum *p); void print(); Seznam(); ~Seznam(); Seznam& operator=(const Seznam&); private: enum { MAX = 100 }; AbstractNum* pole[MAX]; int n; }; je to správně ?? Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i

244 Polymorfní datové struktury - úklid starého stavu Je to správně? Není !! nezruší se předchozí odkazy! memory leaks je to už teď správně ?? Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i

245 Polymorfní datové struktury - generování nových prvků Je to už teď správně? Není !! okopírují se pouze ukazatele data zůstanou stejná prakticky totéž, jako kdybychom nechali automaticky vygenerovaný operator = musíme vygenerovat nové prvky je to už teď správně ?? Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i

246 Polymorfní datové struktury - zrušení abstraktnosti Je to už teď správně? Není !! AbstractNum je abstraktní třída nelze instanciovat (vytvořit objekt) neprojde kompilátorem je to už teď správně ?? Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i

247 Je to už teď správně? Není !! vytvoří se pouze část objektu - společný předek mnohem horší chyba než předchozí případ projde kompilátorem, nespadne, ale dělá nesmysly! slicing Co s tím? když je skutečná hodnota IntNum - vytvořit IntNum když je skutečná hodnota DoubleNum - vytvořit doubleNum Polymorfní datové struktury - vytvoření správných typů IN x AN class AbstractNum { public: enum T { T_INT, T_DOUBLE,...}; virtual T get_t() const; virtual void print()=0; virtual ~AbstractNum() {} }; switch( s.pole[i]->get_t()) { case AbstraktNum::T_INT: pole[i] = new IntNum(*s.pole[i]); break;... je to už teď správně ??

248 Polymorfní datové struktury - vytvoření správných typů Je to už teď správně? Nooo..... dělá to to, co má, ale... je to ošklivé - těžko rozšiřitelné přidání nového typu vyžaduje zásah do implementace společného předka! navíc syntaktická chyba předka nelze automaticky konvertovat na potomka new IntNum(*s.pole[i]) - skutečný parametr typu AbstractNum& konverze new IntNum(* (IntNum*) s.pole[i]) new IntNum( (IntNum&) *s.pole[i]) Jak to udělat lépe? využít mechanismus pozdní vazby každý prvek bude umět naklonovat sám sebe rozhraní v AbstractNum, implementace v IntNum, DoubleNum,... virtuální klonovací metoda

249 Polymorfní datové struktury - klonování class AbstractNum { public: virtual AbstractNum* clone() const =0; virtual void print()=0; virtual ~AbstractNum() {} }; je to už teď správně ?? class IntNum : public AbstractNum { public: virtual AbstractNum* clone() const { return new IntNum(*this); } IntNum(int x_) : x(x_) {} private: const int x; }; Seznam& Seznam::operator=( const Seznam& s) {... for(int i=0; iclone();... jednotné rozhraní na klonovací funkce musí být typu AbstractNum* jinak by to nebyla stejná virt. metoda IntNum AbstractNum případné posunutí ukazatelů řeší automaticky mechanismus virt. funkcí IN x AN

250 Polymorfní datové struktury - přiřazení sebe sama Je to už teď správně? Pořád není !!!! co když někdo provede s = s ? takhle blbě to asi nikdo nenapíše, ale... Seznam p[100]; p[i] = p[j]; nejprve se zruší všechny prvky... a pak se kopírují dealokované bloky!!! ani vynulování ukazatelů moc nepomůže neokopírovalo by se nic nutná ochrana! je to už teď správně ?? Seznam& Seznam::operator=( const Seznam& s) { if( this == &b) return *this;... rovnost ukazatelů  stejný objekt this&s

251 ... to be continued Jazyk member pointers.* a ->*, true / contractual const, mutable static_cast, dynamic_cast, reinterpret_cast, const_cast funktory výjimky, try, catch, throw, exception safety šablony, explicitní instanciace, parciální specializace, traits & policy classes Koenigovo vyhledávání RTTI, typeid, type_info Knihovny streams (proudy) STL – kontejnery, iterátory, algoritmy auto_ptr OOP kánonické formy tříd, abstraktní datové typy, polymorfní typy counted pointers, mělké vs. hluboké kopie návrhové vzory (design patterns) – zprávy, obálkové třídy, forwarding,... hlouběji o objektovém návrhu, reusabilitě, efektivitě implementace

252 101 mouder Organizational and Policy Issues 0. Don’t sweat the small stuff. (Or: Know what not to standardize.) 1. Compile cleanly at high warning levels. 2. Use an automated build system. 3. Use a version control system. 4. Invest in code reviews. Design Style 5. Give one entity one cohesive responsibility. 6. Correctness, simplicity, and clarity come first. 7. Know when and how to code for scalability. 8. Don’t optimize prematurely. 9. Don’t pessimize prematurely. 10. Minimize global and shared data. 11. Hide information. 12. Know when and how to code for concurrency. 13. Ensure resources are owned by objects. Use explicit RAII and smart pointers. Coding Style 14. Prefer compile- and link-time errors to run-time errors. 15. Use const proactively. 16. Avoid macros. 17. Avoid magic numbers. 18. Declare variables as locally as possible. 19. Always initialize variables. 20. Avoid long functions. Avoid deep nesting. 21. Avoid initialization dependencies across compilation units. 22. Minimize definitional dependencies. Avoid cyclic dependencies. 23. Make header files self-sufficient. 24. Always write internal #include guards. Never write external #include guards. Sutter, Alexandrescu: C programovacích technik (C++ Coding Standards)

253 101 mouder Functions and Operators 25. Take parameters appropriately by value, (smart) pointer, or reference. 26. Preserve natural semantics for overloaded operators. 27. Prefer the canonical forms of arithmetic and assignment operators. 28. Prefer the canonical form of ++ and --. Prefer calling the prefix forms. 29. Consider overloading to avoid implicit type conversions. 30. Avoid overloading &&, ||, or, (comma). 31. Don’t write code that depends on the order of evaluation of function arguments. Class Design and Inheritance 32. Be clear what kind of class you’re writing. 33. Prefer minimal classes to monolithic classes. 34. Prefer composition to inheritance. 35. Avoid inheriting from classes that were not designed to be base classes. 36. Prefer providing abstract interfaces. 37. Public inheritance is substitutability. Inherit, not to reuse, but to be reused. 38. Practice safe overriding. 39. Consider making virtual functions nonpublic, and public functions nonvirtual. 40. Avoid providing implicit conversions. 41. Make data members private, except in behaviorless aggregates (C-style structs). 42. Don’t give away your internals. 43. Pimpl judiciously. 44. Prefer writing nonmember nonfriend functions. 45. Always provide new and delete together. 46. If you provide any class-specific new, provide all of the standard forms (plain, in-place, and nothrow).

254 101 mouder Construction, Destruction, and Copying 47. Define and initialize member variables in the same order. 48. Prefer initialization to assignment in constructors. 49. Avoid calling virtual functions in constructors and destructors. 50. Make base class destructors public and virtual, or protected and nonvirtual. 51. Destructors, deallocation, and swap never fail. 52. Copy and destroy consistently. 53. Explicitly enable or disable copying. 54. Avoid slicing. Consider Clone instead of copying in base classes. 55. Prefer the canonical form of assignment. 56. Whenever it makes sense, provide a no-fail swap (and provide it correctly). Namespaces and Modules 57. Keep a type and its nonmember function interface in the same namespace. 58. Keep types and functions in separate namespaces unless they’re specifically intended to work together. 59. Don’t write namespace usings in a header file or before an #include. 60. Avoid allocating and deallocating memory in different modules. 61. Don’t define entities with linkage in a header file. 62. Don’t allow exceptions to propagate across module boundaries. 63. Use sufficiently portable types in a module’s interface. Templates and Genericity 64. Blend static and dynamic polymorphism judiciously. 65. Customize intentionally and explicitly. 66. Don’t specialize function templates. 67. Don’t write unintentionally nongeneric code. Error Handling and Exceptions 68. Assert liberally to document internal assumptions and invariants. 69. Establish a rational error handling policy, and follow it strictly. 70. Distinguish between errors and non-errors. 71. Design and write error-safe code. 72. Prefer to use exceptions to report errors. 73. Throw by value, catch by reference. 74. Report, handle, and translate errors appropriately. 75. Avoid exception specifications.

255 101 mouder STL: Containers 76. Use vector by default. Otherwise, choose an appropriate container. 77. Use vector and string instead of arrays. 78. Use vector (and string::c_str) to exchange data with non-C++ APIs. 79. Store only values and smart pointers in containers. 80. Prefer push_back to other ways of expanding a sequence. 81. Prefer range operations to single-element operations. 82. Use the accepted idioms to really shrink capacity and really erase elements. STL: Algorithms 83. Use a checked STL implementation. 84. Prefer algorithm calls to handwritten loops. 85. Use the right STL search algorithm. 86. Use the right STL sort algorithm. 87. Make predicates pure functions. 88. Prefer function objects over functions as algorithm and comparer arguments. 89. Write function objects correctly. Type Safety 90. Avoid type switching; prefer polymorphism. 91. Rely on types, not on representations. 92. Avoid using reinterpret_cast. 93. Avoid using static_cast on pointers. 94. Avoid casting away const. 95. Don’t use C-style casts. 96. Don’t memcpy or memcmp non-PODs. 97. Don’t use unions to reinterpret representation. 98. Don’t use varargs (ellipsis). 99. Don’t use invalid objects. Don’t use unsafe functions Don’t treat arrays polymorphically.


Stáhnout ppt "PRG029 Programování v C a C++ (LS 2005/06) RNDr. Filip Zavoral, Ph.D. Katedra softwarového inženýrství"

Podobné prezentace


Reklamy Google