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

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

PRG029 Programování v C a C++ (LS 2006/07)

Podobné prezentace


Prezentace na téma: "PRG029 Programování v C a C++ (LS 2006/07)"— Transkript prezentace:

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

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 a 'externisti'– 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é
POZOR!! Letos poslední běh! 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 V modulu SIS Nastěnka bude upřesněno jak

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

5 Obsah přednášky Kurz jazyka C Úvod do 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í V laboratoři SW2 Laboratoř SW2
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 správný program a ono to řeklo 'Váš program provedl neplatnou instrukci a bude ukončen '. Co mám dělat?

7 Jak správně C++ používat pro středně pokročilé
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++ (2nd 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 v češtině: Moderní programování v C++
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++ Programování I, II  výuka (v) Pascalu
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 'Vše' již bylo naprogramováno
Pascal vs. C a C++ Programování v 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 1970-3 první verze C, společný vývoj s UNIXem
1973 přepsání jádra UNIXu do C 1978 Kerninghan, Ritchie: The C Programming Language 1980 standardy – ANSI X3J11, od r ISO 9899 1980 AT&T - "C with Classes" 1983 poprvé název C++ (Rick Mascitti) 1985 Stroustrup: The C++ Programming Language 1989 ANSI X3J16 norma C++ 2003 nejnovější ISO/ANSI norma C++ 2005 C TR1 rozšíření knihoven 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 přesněji společná část C a C++ se zdůrazněním odlišností
Part I - C C přesněji společná část C a C++ se zdůrazněním odlišností

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

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

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í
knihovny standardní i jiné knihovní headery .h .obj .obj .obj .obj .obj .lib spustitelný program .cpp CC .obj Link .exe spojování (linkování) objektový modul (přeložený kód) kompilace

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

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

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í
.h .h .obj .obj .obj .obj .obj .lib .c .obj .c CC .obj .obj Link .exe .cpp editor debugger projekt

22 make .h .h .obj .obj .obj .obj .obj .lib .c .obj .c CC .obj .obj Link
.exe .cpp makefile make

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
deklarace knihovních funkcí, ... hlavička funkce, formální parametr #include <stdio.h> int vynasob( int c) { int i = 1, v; if( c < 1 || 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); neinicializovaná proměnná inicializovaná celočíselná proměnná okanžitý návrat z funkce kontrola parametrů 2 * 7 = 14 začátek cyklu ++i konec cyklu konec, OK hlavní program konec, OK ignorování návratové hodnoty volání funkce se skutečným parametrem The END

25 argumenty z příkazové řádky
Funkce vnořené funkce nelze! Základní programová jednotka je funkce Neexistují vnořené funkce Začátek programu – funkce main int fce2( int a) { int fce1( int x, int y) return x+y; } return fce1( 2*a+17); 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) ... začátek programu argumenty z příkazové řádky později

26 návrat celočíselné hodnoty
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 void fce2( char* text) { printf( text); } void fce3( char* text) if( ! text) return; int fce1( int x, int y) { return x+y; } návrat z funkce návrat celočíselné hodnoty

27 každý parametr musí mít typ
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 každý parametr musí mít typ 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

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 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; vymění se jen lokální proměnné funkce předají se pouze hodnoty (1, 2) nikoliv 'proměnné'

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 !!! hlavní blok funkce deklarace celočíselných proměnných int fce2( void) { int i = 0; while( i < 100) int j = i + 1; i = j * i; } // j jiz neexistuje return i; vnořený blok int fce( void) { int x, y; int z = 0; return z + x; } deklarace proměnné ve vnořeném bloku deklarace s inicializací inicializace se provádí při každém průběhu náhodná hodnota !!! 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 globální proměnná formální parametr int g = 1; void fce( int x) { int z = 2; g = g + z * x; } int main( void) fce( 2); fce( 3); return g; lokální proměnná co vrátí main ?? skutečný parametr

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

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

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

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 Pozor! cyklus pokračuje pokud je podmínka platná while( a > 1) a = a / 2; tělo se vždy alespoň jednou provede while( a > 1) { fce( a); a = a / 2; } do { fce( a); a = a / 2; } while( a > 1);

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

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

40 ukončil by pouze vnitřní cyklus, vnější cyklus by pokračoval
Goto 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ů nelze break - ukončil by pouze vnitřní cyklus, vnější cyklus by pokračoval 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(); label návěští

41 rozšířená znaková sada
Celočíselné typy 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 typ 8 bit 16 bit 32 bit 64 bit char 8 short 8 / 16 16 16 / 32 int 32 32 / 64 long long long - 64 size_t ptrdiff_t wchar_t 16/32 1 byte -2GB .. +2GB velikost objektů rozdíl ukazatelů rozšířená znaková sada

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) char norma neurčuje signed / unsigned (!) korektní porovnání na nerovnost pouze 'a' < 'ž' ? signed char unsigned char 'byte' wchar_t stddef.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 Výčtový typ 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 }; hodnoty doplní překladač (od 0) lze použít jako celočíselnou hodnotu (ale většinou to nemá rozumný smysl) C++: samostatný typ - nelze ( C: celočíselné konstanty - OK ) test bitů explicitní hodnoty

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

45 Celočíselné konverze Automatické konverze (integral promotions)
vždy když je použit menší typ než int 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 při nestejných operandech moudro: prostě se to zkonvertuje na ten větší

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

47 Základní aritmetické operátory
+ - * / % podle typu operandů  automatická konverze na větší typ % - modulo int x=5, y=3; double a=5, b=3; modulo je pouze celočíselná operace celočíselné dělení x / y 1 x / b 1.666 x % y 2 x % b Error a / b a / y a % b a % y reálné dělení

48 Bitové a logické operátory
& | ^ ~ - bitové operace AND, OR, XOR, NOT && || ! - logické operace AND, OR, NOT oba op.  0 5 & 3 1 5 && 3 5 | 3 7 5 || 3 5 ^ 3 6 5 ^^ 3 Error 5 ^ 15 10 5 & 0 5 && 0 5 | 0 5 5 || 0 5 = 01012 3 = 00112 1 = 00012 7 = 01112 9 = 10012 15 = 11112 10 = 10102 alespoň jeden operand  0 neexistuje 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í ! test mezí pole před přístupem k prvku pole int x[10]; // pole 0..9 if( i < 10 && x[i] != 0) y = y / x[i]; if( x==y && *x++) ... Pozor! operátory s vedlejšími efekty se nemusí provést ! POZOR!!! Přiřazení! (zde hodnota vždy = 1) if( x = 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; pokud si lze vybrat, preferujte preinkrement 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

51 Podmíněný výraz, sekvence
ternární operátor 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); operátor sekvence ('zapomnění') x = (tmp = y, y = y + 1, tmp); ekvivalent x = y++;

52 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; i = 0; p[ i++] = i++;

53 Fungující triky vs. chyby
Test ukazatele nebo indexu while ( i<MAX && pole[i]<v ) ++pole[i]; Volání funkce s vedlejším efektem while ( (c = getchar()) != EOF && c != '\n' ); Kopie řetězce while ( *a++ = *b++ ); Chyby Vícenásobný výskyt modifikované proměnné p[ i++] = i++; Nadbytečné volání funkce s vedlejším efektem if ( getchar() == 'A' && getchar() == 'B' ) while( p && p->v < v) p = p->next; není definováno pořadí nevím, jestli se provede

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; Přepis náhodného místa v paměti ! Nepředvídatelné následky !! int x[5]; for( i=1; i <=5; ++i) x[i] = i; 1 2 3 4 ??? ? 5 význam: m-prvkové pole typu (n-prvkové pole typu t) 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

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];
definice struktury Béda 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; Trávníček 1980 položky struktury struktura, ukazatel na strukturu, pole struktur definice proměnné typu struktura s inicializací přístup k položkám x.y (*px).y  px->y

58 Pojmenování struktur a výčtových typů
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; STR * next; }; STR * p; struct STR { int a, b; struct STR * next; }; struct STR * p; C++ - samostatný typ C - pouze tag

59 Ukazatele 1 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á je nějak velká (typ) má hodnotu 1 někde bydlí

60 Ukazatele p: x: 17 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 x: 17 proměnná  ukazatel na ni ukazatel  hodnota na kterou ukazuje

61 přepsání náhodného místa v paměti
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 ? 5 x: 1 :px y: 3 :py ? jaký zde bude obsah x a y?

62 Ukazatele - příklad x: 1 :px y: 3 :py 0: 7 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; x: 1 :px y: 3 :py umožní test 0: 7 typicky 'segmentation fault' váš program bude ukončen pro pokus o porušení ochrany paměti

63 přístup k hodnotě proměnné přes ukazatel
Ukazatele - příklad x: 1 :px 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; y: 3 :py x: 1 :px y: 4 :py Pozor na prioritu! *py++  *(py++) přístup k hodnotě proměnné přes ukazatel

64 Ukazatele - příklad x: 1 :px y: 1 :py x: 9 :px y: 1 :py
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; x: 1 :px y: 1 :py x: 9 :px jaký zde bude obsah x a y? y: 1 :py

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 + j přičtení celočíselné hodnoty - posun o j prvků v rámci pole p - j odečtení - posun zpět int a[5]; int *p; a[2] = 20; p = &a[2]; a[0] = *p - 15; ++p; *p = 30; 1 2 3 4 ? 20 a p reference na prvek pole reference na prvek pole 5 ? 20 30 p inkrementace ukazatele - posun na další prvek

66 Pole a ukazatele, aritmetika ukazatelů
5 ? 20 30 40 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] p přičtení čísla k ukazateli  posun o n prvků indexování pole (ukazatele) je jen jiný zápis přístupu přes ukazatel identifikátor pole je ukazatel na svůj nultý prvek

67 Pole a ukazatele, aritmetika ukazatelů
int a[5]; int *p; 10 20 30 40 nekompatibilní typy nestejná úroveň indirekce 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; identifikátor pole je konstantní  nelze do něj přiřazovat p[i]  *(p+i)  *(i+p)  i[p]

68 Řetězce Řetězec je pole znaků (char) zakončené nulou "Ahoj" X proměnná
konvence, knihovny "Ahoj" X proměnná 'X' znaková konstanta - celočíselná hodnota "X" řetězec - ukazatel Každý řetězec musí být vždy ukončen nulou 'A' 'h' 'o' 'j' '\0' '\0' = 0 char buffer[4]; strcpy( buffer, "Ahoj"); 'A' 'h' 'o' 'j' '\0' pozor na uvozovky a apostrofy ! vždy myslet na koncovou nulu ! 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"); Inicializované pole (konstantní ukazatel) ++s1 nelze! anonymní globální proměnná const char[] ... = { 'U', 'n', 'o', 0 }; s1: 'U' 'n' 'o' '\0' s2: 'D' 'u' 'e' '\0' Inicializovaná proměnná typu ukazatel s2++ se přesune na další znak ekvivalent globální proměnné typu const char[ ], inicializované obsahem konstanty

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 <string.h> size_t strlen( const char* s); deklarace řetězcových funkcí počet znaků (bez koncové nuly) char pole[8] = "Ahoj"; x = strlen( pole); // 4 inicializované pole typu char skutečný počet znaků (4) nikoliv velikost pole pole: A h o j \0 ?

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

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

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

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

76 Délka řetězce – podmínka s vedlejším efektem
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) ; složitější podmínka: test nenulovosti inkrementace ukazatele, pozor na postfix int i=0; while ( *s++ != '\0') ++i; int i = 0; while ( *s != '\0') { ++i; ++s; } return i;

77 Délka řetězce – nenulovost výrazu v podmínce
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 != '\0') { ++i; ++s; } return i; while(a!=0)  while(a) podmínka je splněna pokud je nenulová int i=0; while ( *s++) ++i;

78 Délka řetězce – rozdíl ukazatelů
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 != '\0') { ++i; ++s; } return i; int i=0; while ( *s++) ++i; rozdíl ukazatelů = počet prvků mezi nimi pozor na ± 1 ! char *p = s; while (*p++) ; return p-s-1;

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

80 Řetězce - kopírování buf pozdrav
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); kopíruje pouze do koncové '\0' buf A h o j \0 ? pozdrav A h o j \0

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

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"); po strcpy A h o j \0 ? po strcat A h o j B a b i \0 ? pozor na dostatek místa ! buf A h o j B a b i \0 ? bp

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, strcspn Get 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 výsledek podle prvního rozdílného znaku co znamená 's1 < s2'? A B C E = D

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; } kontrola velikosti pole pozor na mezeru a konec! kopírování jednotlivých částí návrat výsledné délky 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

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 typicky se nepoužívá pole = ukazatel na 1. prvek čtení deklarací: od identifikátoru doprava, až to nepůjde, tak doleva

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

90 Kombinace typových kostrukcí - typedef
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); 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 pole řetězců (ukazatelů na char) int main( int argc, char** argv) argv 5 argc b . t x \0 a . t x \0 Počet parametrů včetně názvu programu ! = počet ukazatelů v argv - w \0 - n \0 m y p r o g . e x \0

92 Zpracování příkazové řádky – výpis parametrů
C:\> myprog.exe -n -w a.txt b.txt int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); ++argv; } 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; } C:\> myprog.exe -n -w a.txt b.txt argv[4] argv[4][1] b . t x \0 *argv  argv[0] typ: char* a . t x \0 - w \0 argv typ: char** - n \0 m y p r o g . e x \0 argv **argv  argv[0][0] typ: char

94 Zpracování příkazové řádky
int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); ++argv; } b . t x \0 a . t x \0 argv++ **argv - w \0 - n \0 m y p r o g . e x \0 argv

95 Zpracování příkazové řádky
int main( int argc, char** argv) { while( *argv) { printf( "%s\n", *argv); ++argv; } b . t x \0 a . t x \0 *argv == 0 - w \0 - n \0 m y p r o g . e x \0 argv

96 Zpracování parametrů příkazové řádky
usage: myprog [-n] [-w] fileA fileB 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 nastavení přepínače zbývající parametry b . t x \0 a . t x \0 - w \0 výkonná funkce - n \0 argv p r g . e x \0

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; usage: myprog [-n] [-w] [-x123] [-ffilename] fileA fileB číselný parametr řetězcový parametr - f i l ... - x 1 2 3 \0 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; usage: myprog [-n] [-w] [-x123] [-f filename] fileA fileB -ffile -f file f i l e n ... - f \0 - f i l ... argv

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

100 Velikost pole určena za běhu programu
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]); spočítá potřebnou velikost ze vstupních parametrů pozor na koncovou '\0' 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]); 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 velikost bloku 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; } 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); } #### 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
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 inicializované pole int bigpole[ 1000]; char retezec[] = "CHAIN"; { int* p = bigpole; const char* s = "ahoj"; ...

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 nemixovat { char* s; s = new char[128]; ...

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

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
pokus\0 buf 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); .... D p Z program pokus H

110 Organizace paměti procesu – příklad
pokus\0 buf 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); .... D p s p Z program pokus H

111 Organizace paměti procesu – příklad
pokus\0 buf 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); .... D p s p Z #pokus\0 program pokus H

112 Organizace paměti procesu – příklad
pokus\0 buf 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); .... D p Z #pokus\0 program pokus H

113 Organizace paměti procesu – příklad
pokus\0 buf 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); .... D p s p Z #pokus\0 program pokus H

114 Organizace paměti procesu – příklad
pokus\0 buf 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); .... D p s p Z #pokus\0 ##pokus\0 program pokus H

115 Organizace paměti procesu – příklad
pokus\0 buf 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); .... D p Z #pokus\0 ##pokus\0 program pokus H

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(vysledek, fce[i], pocet, zac, krok); zobraz( vysledek, ....); return 0; cos { ... sin { ... log { ... C fce D vysled. f vysl Z

117 Souborový vstup a výstup
deskriptor souboru - deklaruje programátor struktura definovaná v <stdio.h> soubor 'na síti' FILE * FILE Neznámý obsah Pro knihovní funkce OS 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)

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

119 r: soubor musí existovat
Otevření souboru FILE* fopen( const char* fname, const char* mode); r open file for reading w truncate to zero length or create file for writing a append; 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 rb binary file ... r: soubor musí existovat mode soubor ex. soubor neex. seek r R Error w Del, W W a End r+ R/W w+ Del, R/W a+ w: soubor se smaže a: otevřít na konci +: vždy čtení i zápis

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 <stdio.h> const int buflen = 1024; char buf[buflen]; int main(int argc, char ** argv) { FILE *f; int n; if (argc <= 1) return 1; f = fopen(argv[1], "r"); if (!f) return 2; while ((n = fread(buf, 1, buflen, f)) > 0) { fwrite(buf, 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 <stdio.h> int main( int argc, char** argv) { FILE* f; long l; if( argc < 2) return 8; f = fopen( argv[1], "rb"); 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); Nepoužívat! Nelze ohlídat přetečení bufferu všechny souborové funkce lze použít i pro std. v/v jednotný zápis na std výstup nebo do souboru

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

126 printf int printf( const char *, ...); fprintf( FILE*, sprintf( char*,
swprintf( wchar_t*, _snprintf( char*, int n, _scprintf( ... 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 místo width a/nebo precision znak * hodnota následujícího parametru width: min. počet míst na výstupu precision: max. počet zpracovávaných znaků 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:: : Unhandled exception at 0x c : Access violation reading location 0x

127 Direktivy preprocesoru
#include <stdio.h> #include <cstdio> #include <iostream> #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 Spojování modulů – problém hlaviček funkcí
x.c double A() { return B( 7); } y.c double B() { return 3.14; } error: Undefined 'B'

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

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

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

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

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

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

135 Spojování modulů - typy
Definice nového typu Deklarace proměnné tohoto typu x.c #include "y.h" double A() { return C; } y.h enum T { P, Q}; extern enum T C; y.c #include "y.h" enum T C; enum T { P, Q}; extern enum T C; double A() { return C; } enum T { P, Q}; extern enum T C; enum T C;

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

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

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

139 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 int x; char * p = (char *)&x; struct { char a; int b; } S; fwrite( &S, 1, sizeof( S), fp); #ifdef _MSC_VER // Microsoft typedef __int64 INT64; const char delimiter = '\\'; #else typedef long long INT64; #ifdef UNIX const char delimiter = '/'; #endif Ideál: Program přenositelný bez úpravy zdrojového textu

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

141 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í 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")

142 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

143 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

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

145 Oblíbené chyby – struktura programu
Chybějící nebo přebývající středník: for (i=0; i<n; i++); { ... } funkce(); { ... } Přehozené parametry funkcí nebo části konstrukce: char text[20]; strcpy( "Chyba!", text); for( i=0; i++; i<20)... Nezapomínat na break v konstrukci switch Pozor na define (raději takhle vůbec nepoužívat): #define uint int* uint p1,p2; #define N 100; int delka=N+1; /* rozvine se: int delka=100;+1; */ #define square (x) x*x K čemu patří else? if(podmínka1)   if(podmínka2) /* akce */ else /* jiná akce */

146 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<x<1)...

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

148 101 mouder Sutter, Alexandrescu: C++ 101 programovacích technik
(C++ Coding Standards) 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

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

150 C++ Part II - C++ Inheritance, Encapsulation, Polymorphism
Inheritance, Enkapsulace, Polymorfismus Dědičnost, Zapouzdření, ... Polymorfismus

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

152 Třída a objekt Třída (class) Objekt (instance třídy)
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 << pa->Prod();

153 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

154 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 x y u

155 Dědičnost - reusabilita
x y 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

156 Dědičnost - reusabilita
x y pa class A { public: int x, y; }; int f( A * pa) { return pa->x * pa->y; } class B : public A { int u; int g( B * pb) return pb->u * f( pb); B A x y u pb pa volání kódu pracujícího s předkem případné konverze zajístí kompilátor automaticky

157 Dědičnost - polymorfismus
enum Typ { T_B, T_C }; class A { public: Typ t; A * n; }; A * seznam; class B : public A { int x, y; class C : public A { int u; seznam B A t n x y C A t n u B A t n x y seznam objektů se společným předkem

158 Dědičnost - polymorfismus
enum Typ { T_B, T_C }; class A { public: Typ t; A * n; }; A * seznam; class B : public A { int x, y; class C : public A { int u; A * pa = seznam->n->n; if ( pa->t == T_B ) { B * pb = (B *)pa; } seznam B A t n x y C A t n u B A t n x y pb pa odkaz na objekt odvozené třídy zodpovědnost programátora za korektnost

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

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

161 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

162 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

163 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ě... class Letadlo : public Motor {}; class Letadlo : public Motor, public Motor {}; class Letadlo { ... Motor motor[N]; }; class ObrazkoveTlacitko { ... Icon ico; };  Kompozice Skládání velkých objektů z malých C++: Třída s datovými položkami class ZamackavaciTlacitko { ... Icon icoOn; Icon icoOff; };

164 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... Jiný důkaz - veterinář... Kompozice? Ne: Nerodí se zároveň Tlačítko Start není potomkem Windows class JM1 : public Myslivec, public Jezevcik {}; class JM1 { ... Myslivec* mysl; Jezevcik* jez; }; Různé doby životnosti objektů pozor na platnost referencí !  Delegace Převedení funkčnosti na jiný objekt C++: Ukazatel

165 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 class Mlok : public Ryba, public Savec {}; class Mlok : virtual public Ryba, virtual public Savec {}; class Obratlovec {}; class Ryba : public Obratlovec {}; class Savec class Obojzivelnik Společný abstraktní předek definuje společné vlastnosti, rozhraní

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

167 Kompatibilita předka a potomka
Potomka lze přiřadit do předka (platí i pro ukazatele) Předka NELZE přiřadit do potomka (platí i pro ukazatele) pes umí jíst ... jako všechna zvířata brouk neumí štěkat ... nelze ho použít jako psa class Zvire {}; class Pes : public Zvire {}; Zvire pytlik, *pz; Pes azor, *pp; pytlik = azor; pz = &azor; Pozor na polymorfní datové struktury Slicing ! azor pytlik stav žaludek žaludek azor azor = pytlik; pp = &pytlik; pytlik stav žaludek žaludek nelze ! ???

168 Metoda (member function)
Metoda (member function) = funkce uvnitř třídy chráněné položky přístupné pouze metodám třídy veřejné položky přístupné komukoliv Každá metoda dostane 'tajný' parametr this - ukazatel na objekt 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(); deklarace (hlavička) metody A:: => skrytý parametr A* this implementace (tělo) metody this->x volání metody

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

170 Tělo metody uvnitř a vně třídy
Tělo uvnitř třídy Inline 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; } inline: kompilátor se pokusít tělo funkce rozvinout v místě volání inline: kompilátor se pokusít tělo funkce rozvinout v místě volání Tělo vně třídy Ekvivalent externí deklarace a definice globální funkce Tělo v .cpp souboru .h class A { public: int x, y; int f(); }; .cpp int A::f() { return x * y; }

171 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 const A* this

172 Metody základních a odvozených tříd
class A { private: int x, y; public: int f() { return x * y; } }; class B : public A { int z; 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(); A::f() automatická konverze na ukazatel na předka volání metody základní třídy volání metody odvozené třídy

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

174 Volá se stále stejná funkce int fce( int, int, int)
Implicitní parametry 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ě 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) Volá se stále stejná funkce int fce( int, int, int)

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

176 Přetěžování funkcí Funkce je definována svým identifikátorem a počtem a typem parametrů 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 se stejným identifikátorem ale různým počtem parametrů Funkce se stejným počtem ale různým typem parametrů Správná funkce podle počtu a typů skutečných parametrů

177 obě varianty stejně drahé
Přetěžování funkcí int min( int a, int b) { return a < b ? a : b; } double min( double a, double b) min( 1, 2); // min( int, int) 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é

178 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

179 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; ref = 17; void f( int & u) { u = 1; } int a = 0; f(a); a: 1 u: ref: 17 :data

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

181 Reference souvislosti 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 a, int& b) {...} int x = 1, y = 2; f(x,y); int & f() { int x; return x; } int& f(int& b) { ++b; return b; } int x = 1; f(x) = 2; int * p = 0; int & r = * p; if ( ! & r ) // ...

182 Přetěžování operátorů
class bod { private: int x, y; public: bod operator+( const bod&); bod& operator=( const bod&); }; bod a, b, c; c = a + b; přetížení operátoru + a + b  a.operator+(b) a = b  a.operator=(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); c.operator=( a.operator+( b)); c.assign( a.add( b)); Těla metod

183 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 ++a nemusí být ekvivalentní a=a+1 a[b] nemusí být ekvivalentní *(a+b) ani b[a] je však velmi doporučeno dodržovat běžnou sémantiku 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ů stejná pravidla jako pro přetížené funkce

184 Přetěžování operátorů - pravidla
Dva způsoby definice přetíženého operátoru - symbol příslušného operátoru) metoda se jménem ve třídě prvního operandu globální funkce se jménem globální funkce: např. při vyžadované spolupráci se základními typy Operátory, které jsou metodami, jsou s výjimkou operátoru přiřazení dědičné a mohou být virtuální class bod { bod operator+( const bod&); }; Bod operator+( const bod&, const bod&); class DlouhyInt { ... DlouhyInt operator+( DlouhyInt&); }; DlouhyInt a; a = 1 + a;

185 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, C) A B &, C &) A const B &, const C &) A C) A const C &) A const C &) const není zde operator= speciální metoda třídy A B::operator []( C) A B::operator []( C &) A B::operator []( const C &) const speciální operátory -> a () později

186 Přetěžování operátorů - unární operátory
a prefixové operátory ++ -- lze předefinovat jako globální funkce nebo metody postfixové operátory ++ a -- ++X A B) A B &) A const B &) A A const pro některé operátory (např. ++) netypické X++ A B, int) A B &, int) A const B &, int) A int) A int) const fiktivní operand typu int

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

188 Konstruktor, seznam inicializátorů
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 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 vždy preferujte seznam inicializátorů parametrický konstruktor vs. implicitní konstruktor + přiřazení

189 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) Kopírovací konstruktor a operátor přiřazení vždy spolu ... podrobněji později, teď jen varování class A { A(); } class A { A(const A&); } A f(A a) { return a; } A b; b = f(b);

190 Konstruktory implicitní konstruktor default constructor
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 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()) alokace pole - pouze implicitní konstruktor

191 Konstruktory a dědičnost
implicitní konstruktor předka 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) {} }; 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 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

192 Vznik a zánik objektů – lokální proměnné
kostruktor je 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 destruktor deklarace může být kdekoliv uvnitř funkce rozsah platnosti 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 = 1; A a; if( x) { B b; x = 2; } if( !x) return; x = 3; A::A B::B B::~B A::~A A::~A

193 Vznik a zánik objektů – 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

194 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ý lokální 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! možné řešení lazy inicializace

195 Vznik a zánik objektů – dynamicky alokované objekty
pro dynamickou alokaci slouží operátory new a delete funkce malloc a free s nimi nelze kombinovat v C++ 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ří: vyvolá se výjimka std::bad_alloc (C++ 98 a novější) starší C++: výjimka se nevyvolá, 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 *p, **q; p = new A[ 20]; q = new A*[ 20]; /* ... */ delete[] p; delete[] q; A::A() A::A( const A&) pole ukazatelů

196 Vznik a zánik objektů – dočasné objekty
užití jména třídy jako jména funkce v operátoru volání způsobí: vyhrazení místa pro objekt na zásobníku mezi okolními lokálními proměnnými vyvolání konstruktoru s patřičnými parametry použití 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) konstruktor, kopie, destruktor A a, b; a = A(); b = A( 1); return A( 2); použití jako návratové hodnoty konverzní konstruktor z int vyrobí A

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

198 Vznik a zánik objektů – příklad
A f( A y) { return y; } A p, q; p = f( q); kolik konstruktorů a jakých a kolik destruktorů 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ě?

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

200 Vznik a zánik objektů – chytrý kompilátor
2. y  temp 'spočítá' se hodnota výrazu pro return a vytvoří se dočasný objekt copy konstruktor 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 4. temp  p přiřazení návratové hodnoty funkce proměnné operátor přiřazení

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

202 Polymorfismus - motivace
Tohle není polymorfismus ! Funkce je známa v době překladu 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(); '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

203 Polymorfismus – pozdní vazba
chtěl bych, aby se volaly 'správné' metody 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(); 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 skutečného objektu, na jehož součást ukazuje ukazatel přesněji: 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 Chci pokaždé se zavolat jinou metodu Rozlišení metody se musí dít za běhu

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

205 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 class A { public: int x; virtual int f() { return 1; } virtual int g() { return 2; } }; class B : public A { int y; virtual int f() { return 9; } A* paa = new A; A* pab = new B; B* pbb = new B; paa->f(); paa->g(); // 1 2 pab->f(); pab->g(); // 9 2 pbb->f(); pbb->g(); // 9 2 A x f A::f g A::g B A x y f B::f g

206 Virtuální metody - implementace
class A { public: int x; virtual int f() { return 1; } virtual int g() { return 2; } }; class B : public A { 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();

207 Volání virtuálních metod
class A { public: virtual f(); }; class B : public A { A a; B b; A * paa = &a; A * pab = &b; B * pbb = &b; // B * pba = &a; nelze! zakrývání metod 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 paa kvalifikované volání a A::f pab b B::f pbb

208 Virtuální metody a konstruktory a destruktory
v konstruktoru a destruktoru se vždy volá metoda vytvářeného/rušeného objektu nejdřív se zavolá konstruktor předka 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 }; určí se za běhu podle skutečného typu objektu nejdřív se provede kód destruktoru, pak se zavolá destruktor předka 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ů.

209 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 { 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?

210 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 { 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

211 Virtuální destruktory
Má-li být třída předkem určeným pro ukazatele na dynamicky vytvářené potomky, musí mít virtuální destruktor. 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 { 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 nejdříve se provede tělo ~B, potom se zavolá ~A

212 Č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 Nelze instanciovat Lze používat ukazatele na tuto třídu a vyvolávat metody této třídy Často neobsahuje žádné datové položky - definice rozhraní class A { public: virtual int f() = 0; }; class B : public A { virtual int f() { return 9; } A f B A f B::f

213 Čistě virtuální metody, abstraktní třída
nelze vytvořit objekt společný předek 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; }; pure virtual function abstraktní třída společné rozhraní 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; } }; Nutný virtuální destruktor

214 Statická data 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 statická konstanta 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(); nekonstantní statická proměnná strukturovaná statická konstanta

215 Statické metody 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() 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);

216 Přehled druhů metod metoda member function virtuální metoda
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); } virtuální metoda virtual function čistě virtuální metoda pure-virtual function statická metoda static member function

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

218 jediný rozdíl mezi struct a class
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

219 private inheritance public inheritance - Is-A private inheritace
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

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

221 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 { void push( Slon* p) { s.push(p); } Slon* pop() { return (Slon*)s.pop(); }

222 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 { void push( Slon* p) {push(p); } Slon* pop() { return (Slon*)Stack::pop(); }

223 Spřátelené funkce a třídy - friend
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é - používat s rozmyslem 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, různé úrovně ochrany přístupu 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; } spřátelená třída spřátelená funkce Pozor! Nejde o metodu X!

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

225 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

226 Konverzní operátor uživatelsky definovaná konverze jednoho typu na jiný syntaxe: operator <typ>(); 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* 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()

227 Konverzní operátor 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! 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 complx::complx( int _re, int = 0) complx operator +(const complx &,const complx &) complx::operator int() operator+(int, int)

228 Prostory jmen (namespaces)
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 Později: argument dependent lookup Koenig lookup definice prostoru jmen namespace aa { int p; int f1( int x) { return x + p; } } 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)); přístup ze stejného prostoru znovuotevření prostoru aa direktiva using - rozbalení std definice funkce mimo prostor jmen deklarace using - pouze ident.

229 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ů S IN DN IN AN AN AN x d x

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

231 Polymorfní datové struktury - implementace metod
// konstruktor Seznam::Seznam() { for(int i=0; i<MAX; ++i) pole[i]=0; n=0; } // destruktor Seznam::~Seznam() for(int i=0; i<n; ++i) delete pole[i]; // tisk seznamu void Seznam::print() pole[i]->print(); // pridani prvku do seznamu void Seznam::append(AbstractNum* p) if (n<MAX) pole[n++]=p; class Seznam { public: void append( AbstractNum *p); void print(); Seznam(); ~Seznam(); private: enum { MAX = 100 }; AbstractNum* pole[MAX]; int n; }; každý prvek ví jak se vytisknout

232 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; }; konkrétní datové typy implementují vlastní metody jednotného rozhraní konkrétní datové typy implementují vlastní metody jednotného rozhraní 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(); kontejner obsahuje různé typy ... a všechny vytiskne

233 Polymorfní datové struktury - konstruktor const položek
Požadavek: co když chci zakázat měnit hodnotu prvků class IntNum : public AbstractNum { public: IntNum(int x_) { x=x_; } private: const int x; }; 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

234 Polymorfní datové struktury - přiřazení
Problém: přiřazení seznamů 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; } Je to korektní kód?

235 Polymorfní datové struktury - přiřazení
Problém: přiřazení seznamů Spadne to! Kde? v destruktoru ~Seznam Proč? Navíc: memory leaks 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í ! 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!

236 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í ??

237 Polymorfní datové struktury - copy konstruktor
Není! copy konstruktor! 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; } 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; }; oprator= a copy konstruktor by se měly chovat stejně! je to už teď konečně korektní ??

238 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 ???

239 Polymorfní datové struktury - kopie prvků
Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i<s.n; ++i) pole[i] = s.pole[i]; n = s.n; return *this } class Seznam { public: void append( AbstractNum *p); void print(); Seznam(); ~Seznam(); Seznam& operator=(const Seznam&); private: enum { MAX = 100 }; AbstractNum* pole[MAX]; int n; }; okopíruji všechny prvky je to správně ??

240 Polymorfní datové struktury - úklid starého stavu
Je to správně? Není !! nezruší se předchozí odkazy! memory leaks nejdříve zruším všechny prvky cílového kontejneru Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i<n; ++i) // jako v destruktoru delete pole[i]; for(int i=0; i<s.n; ++i) // jako v copy konstr. pole[i] = s.pole[i]; n = s.n; return *this } je to už teď správně ??

241 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 dynamická alokace nového prvku Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i<n; ++i) delete pole[i]; for(int i=0; i<s.n; ++i) pole[i] = new AbstractNum( *s.pole[i]); n = s.n; return *this } přímá konverze odvozené třídy na AbstractNum& je to už teď správně ??

242 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 class AbstractNum { public: virtual void print() {}; virtual ~AbstractNum() {} }; psychicky zdeptaný programátor: tak tu abstraktnost odstraníme! Seznam& Seznam::operator=( const Seznam& s) { for(int i=0; i<n; ++i) delete pole[i]; for(int i=0; i<s.n; ++i) pole[i] = new AbstractNum( *s.pole[i]); n = s.n; return *this } je to už teď správně ??

243 Polymorfní datové struktury - vytvoření správných typů
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 IN AN 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ě ??

244 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

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

246 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! this &s Seznam& Seznam::operator=( const Seznam& s) { if( this == &b) return *this; ... rovnost ukazatelů  stejný objekt je to už teď správně ??

247 Polymorfní datové struktury - přiřazení sebe sama
Seznam& Seznam::operator=( const Seznam& s) { if( this == &b) return *this; ... rovnost ukazatelů  stejný objekt Je to už teď správně? .... no teď už snad ano ale co když jsou referencované objekty velké? časté kopírování neefektivní optimalizace zachování sémantiky vs. odlišná implementace reference counting, ...

248 101 mouder Sutter, Alexandrescu: C++ 101 programovacích technik
(C++ Coding Standards) 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.

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

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

251 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. 100. Don’t treat arrays polymorphically.

252 ... to be continued Jazyk Knihovny OOP
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, shared_ptr OOP kánonické formy tříd, abstraktní datové typy, polymorfní typy counted pointers, mělké vs. hluboké kopie hlouběji o objektovém návrhu, reusabilitě, efektivitě implementace návrhové vzory (design patterns) – factory, visitor, observer ...

253 ... not used

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

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

256 Programování není (jen) 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ů' Programování ≈ výroba software Softwarové inženýrství Informační systémy

257 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

258 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


Stáhnout ppt "PRG029 Programování v C a C++ (LS 2006/07)"

Podobné prezentace


Reklamy Google