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

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

David Bednárek www.ksi.mff.cuni.cz/~bednarek Programování v C++ David Bednárek www.ksi.mff.cuni.cz/~bednarek.

Podobné prezentace


Prezentace na téma: "David Bednárek www.ksi.mff.cuni.cz/~bednarek Programování v C++ David Bednárek www.ksi.mff.cuni.cz/~bednarek."— Transkript prezentace:

1 David Bednárek www.ksi.mff.cuni.cz/~bednarek
Programování v C++ David Bednárek

2 Pravidla studia NPRG041 2/2 Z,Zk

3 Elektronický zápis do jednotlivých skupin
Zápis na cvičení Elektronický zápis do jednotlivých skupin is.cuni.cz/studium Zápis předmětů a rozvrhu - zápis Grupíček - výsledky Zapsáni musejí být všichni Do Kapacita laboratoře je omezena, skupiny nelze přeplňovat Zvláštní skupina pro repetenty Repetenti kontaktují cvičícího do Udělit zápočet může jen cvičící, ke kterému je student zapsán Kdo nebude do zapsán, zápočet v tomto šk. roce nedostane

4 Základní podmínky společné všem skupinám
Udělení zápočtu Základní podmínky společné všem skupinám Úspěšné složení zápočtového testu 1. a 2. pokusy ve zkouškovém období pokusy v dubnu 2-3 hodiny v laboratoři, společně pro všechny skupiny Vypracování zápočtového programu Dohoda o tématu - do listopadu Předvedení cvičícímu do Doladění a odevzdání do Další podmínky udělení zápočtu určuje cvičící Cvičící může podmínky individuálně upravit, pokud se s ním student na začátku semestru dohodne Přiměřená účast na cvičeních Úspěšné odevzdání domácího úkolu

5 Zkouška bude provedena formou abc-testu
Vlastnosti a pravidla jazyka C++ Používání knihoven C++ (kontejnery, algoritmy, iostream) Typické konstrukce objektového programování Run-time/static polymorphism Termíny Ve zkouškovém období ZS Během výuky v LS

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

7 Historie C++

8 The C++ programming language
Historie C++ téměř nadmnožina inspirace nadmnožina významná změna B (Bell Labs. 1969) BCPL (Cambridge 1966) C (Bell Labs. 1971) K&R C (Kernigan & Ritchie 1978) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) C++98 (ISO/IEC ) C++03 (ISO/IEC ) C++TR1 (ISO/IEC ) C++11 (ISO/IEC ) šablony paralelismus C++14 (2014+)

9 The C++ programming language
Historie C++ a C téměř nadmnožina inspirace nadmnožina významná změna B (Bell Labs. 1969) BCPL (Cambridge 1966) C (Bell Labs. 1971) K&R C (Kernigan & Ritchie 1978) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) C++98 (ISO/IEC ) C++03 (ISO/IEC ) C++TR1 (ISO/IEC ) C++11 (ISO/IEC ) ANSI C (ANSI X3J ) C11 (ISO/IEC ) C99 (ISO/IEC ) šablony paralelismus C++14 (2014+)

10 Historie C++ - Objective-C
téměř nadmnožina inspirace nadmnožina významná změna B (Bell Labs. 1969) BCPL (Cambridge 1966) C (Bell Labs. 1971) K&R C (Kernigan & Ritchie 1978) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) C++98 (ISO/IEC ) C++03 (ISO/IEC ) C++TR1 (ISO/IEC ) C++11 (ISO/IEC ) Objective-C (Cox & Love 1981) Object-Oriented Programing (Cox 1986) Objective-C 2.0 (Apple 2006) Objective-C++ (Apple 2010) ANSI C (ANSI X3J ) C11 (ISO/IEC ) C99 (ISO/IEC ) šablony paralelismus C++14 (2014+)

11 Historie C++ - významné příbuzné jazyky
téměř nadmnožina inspirace nadmnožina významná změna B (Bell Labs. 1969) BCPL (Cambridge 1966) C (Bell Labs. 1971) K&R C (Kernigan & Ritchie 1978) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) C++98 (ISO/IEC ) C++03 (ISO/IEC ) C++TR1 (ISO/IEC ) C++11 (ISO/IEC ) Objective-C (Cox & Love 1981) Object-Oriented Programing (Cox 1986) Objective-C 2.0 (Apple 2006) Objective-C++ (Apple 2010) ANSI C (ANSI X3J ) C11 (ISO/IEC ) C99 (ISO/IEC ) C# (Microsoft 2002) Java (Sun 1995) C++/CLI (Microsoft 2005) šablony paralelismus C++14 (2014+)

12 Historie C++ - použití C v jádrech OS
téměř nadmnožina inspirace nadmnožina významná změna Linux 1991 Unix 1973 Windows NT 1993 OS-X 2000 MacOS 1984 B (Bell Labs. 1969) BCPL (Cambridge 1966) C (Bell Labs. 1971) K&R C (Kernigan & Ritchie 1978) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) C++98 (ISO/IEC ) C++03 (ISO/IEC ) C++TR1 (ISO/IEC ) C++11 (ISO/IEC ) Objective-C (Cox & Love 1981) Object-Oriented Programing (Cox 1986) Objective-C 2.0 (Apple 2006) Objective-C++ (Apple 2010) ANSI C (ANSI X3J ) C11 (ISO/IEC ) C99 (ISO/IEC ) C# (Microsoft 2002) Java (Sun 1995) C++/CLI (Microsoft 2005) šablony paralelismus C++14 (2014+)

13 Literatura

14 Literatura Pro začátečníky - před C++11 Bruce Eckel: Thinking in C++ (2000) Myslíme v jazyku C++ (Grada 2000) Miroslav Virius: Pasti a propasti jazyka C++ (Computer Press 2005) Programování v C++ (ČVUT 2001) Andrew Koenig, Barbara E. Moo: Accelerated C++ (2000) Stanley B. Lippman: Essential C++ (2000)

15 Literatura Pro středně pokročilé - před C++11 Andrei Alexandrescu, Herb Sutter: C++ Coding Standards (2005) Scott Meyers: Effective C++ (1998) More Effective C++ (1996) Effective STL (2001) Herb Sutter: Exceptional C++ (2000) More Exceptional C++ (2002) Exceptional C++ Style (2004) Nicolai M. Josuttis: Object-Oriented Programming in C++ (2002) The C++ Standard Library (1999)

16 Literatura Až si budete myslet, že všechno umíte - před C++11 Andrei Alexandrescu: Modern C++ Design (2001) Moderní programování v C++ (Computer Press 2004) David Vandevoorde, Nicolai M. Josuttis: C++ Templates (2003)

17 Scott Meyers: Overview of the New C++ (C++11)
Literatura C++11 Scott Meyers: Overview of the New C++ (C++11) 360 slajdů z přednášek Vysvětluje motivaci k novým vlastnostem Bjarne Stroustrup: The C++ Programming Language - Fourth Edition Addison-Wesley. ISBN May 2013 Učebnice celého C++ Zatím jediná učebnice obsahující C++11

18 Je lepší C++ nebo Java/C#?

19 Je lepší C++ nebo Java/C#?
Špatná otázka

20 Pro které oblasti je C++ lepší než Java/C#? Důraz na výkon
Co programovat v C++ Pro které oblasti je C++ lepší než Java/C#? Důraz na výkon C++ umožňuje programovat způsobem, který neubírá na výkonu Když budete programovat v C++ stejným stylem jako v Java/C#, dostanete přibližně stejný výkon Spolupráce s hardware C++ nechystá na programátora nepříjemná překvapení (GC etc.) Embedded assembler, spojování s jinými jazyky Spolupráce s OS Všechny významné OS mají v C jádro a tudíž i rozhraní OS Většina systémových aplikací je v C nebo C++ Nativní knihovny jazyků Java/C# jsou implementovány v C/C++ Generické programování Mechanismus šablon v C++ je silnější než v C/C++ Způsob implementace šablon v C++ neubírá na výkonu

21 Programování orientované na výkon Numerické výpočty
Co programovat v C++ Programování orientované na výkon Numerické výpočty Převládající jazyky: FORTRAN, C Pomalý posun směrem k C++ Databázové systémy Převládající jazyky: C/C++ Existují i databázové systémy v Javě Spolehlivé srovnání výkonu neexistuje Proč je Java/C# pomalejší? Garbage collection GC způsobuje mj. problémy s využitím cache Programování bez GC je pracnější, ale dává lepší výsledky Chybí pokročilé metody optimalizace v překladačích Vektorizace, transformace cyklů, ... Existují i situace, kdy je Java/C# rychlejší Překladače Javy/C# mají jednodušší úlohu

22 Programování v C++ je pohodlnější než v C
Co programovat v C++ Proč C++ a ne C Stávající aplikace/knihovny/OS jsou často v C Programování v C++ je pohodlnější než v C Menší pravděpodobnost chyb Šablony, operátory, zapouzdření, ... Při troše šikovnosti stejný výkon jako v C Moduly psané v C++ a C lze spojovat extern "C" void do_something_in_C( int x); void my_Cplusplus_function( int x) { do_something_in_C( x); } extern "C" void call_me_from_C( int y) { /* C++ code here */ }

23 Co raději neprogramovat v C++ Interaktivní aplikace s GUI
Co neprogramovat v C++ Co raději neprogramovat v C++ Interaktivní aplikace s GUI C++ nemá standardizované rozhraní na GUI Nativní rozhraní GUI v OS je většinou archaické C Knihovny pro GUI jsou archaické, nepřenositelné nebo obojí Qt, GTK+, wxWidgets... Garbage Collection při programování GUI citelně chybí Pokud je zároveň zapotřebí výkon, nic jiného než C++ nezbývá Aplikace skládané z mnoha cizích součástí Standard C++ poskytuje nedostatečné služby OS apod. Cizí knihovny obvykle doplňují chybějící části vlastní tvorbou Různé implementace chybějících částí mohou být v konfliktu

24 Proč (stále ještě) učíme C++?
Proč C++ Proč (stále ještě) učíme C++? Většina řadových programátorů v C++ programovat nebude MFF chce vychovávat elitu Programování OS, databází, překladačů Vědecké výpočty vyžadující výkon Hry, robotika,... Údržba rozsáhlých a historických softwarových systémů Porozumíte-li tomu, jak funguje C++, budete lépe rozumět jiným programovacím jazykům architektuře počítačů a operačních systémů překladačům Zvládnutí C++ je odznakem zdatnosti matfyzáka

25 Hello, World! Vstupní bod programu Parametry programu
#include <iostream> int main( int argc, char * * argv) { std::cout << "Hello, world!" << std::endl; return 0; } Vstupní bod programu Dědictví jazyka C Žádné třídy ani metody Globální funkce main Parametry programu Z příkazové řádky Děleno na kousky Archaické datové typy Ukazatel na ukazatel Logicky pole polí std - namespace knihoven cout - standardní výstup globální proměnná << - výstup do streamu přetížený operátor endl - oddělovač řádek

26 Hello, World! Dělení do modulů
Rozhraní modulů je nutno opsat do zvláštního souboru .hpp - hlavičkový soubor Definující i používající modul tento soubor inkluduje textová direktiva #include // world.hpp #ifndef WORLD_HPP_ #define WORLD_HPP_ void world(); #endif // main.cpp #include "world.hpp" int main( int argc, char * * argv) { world(); return 0; } // world.cpp #include "world.hpp" #include <iostream> void world() { std::cout << "Hello, world!" << std::endl; }

27 Hello, World! // world.hpp #ifndef WORLD_HPP_ #define WORLD_HPP_
#include <vector> #include <string> typedef std::vector< std::string> t_arg; void world( const t_arg & arg); #endif // main.cpp #include "world.hpp" int main( int argc, char * * argv) { world( t_arg( argv + 1, argv + argc)); return 0; } // world.cpp #include "world.hpp" #include <iostream> void world( const t_arg & arg) { if ( arg.empty() ) std::cout << "Hello, world!" << std::endl; }

28 Překladače / interpretry
Architektura Překladače / interpretry

29 CPU C P U

30 CPU rozumí pouze binárním kódu

31 1940... – programování ve strojovém kódu
C P U

32 1940... – programování ve strojovém kódu
15 0 C P U

33 1940... – programování ve strojovém kódu
15 0 X X XX XX X X XX XX XX X X X X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X XX X X X XX X XXX C P U

34 – assembler 15 0 X X XX XX X X XX XX XX X X X X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X XX X X X XX X XXX C P U PRINT NOGEN BEGIN BEGIN REGS SR R2,R2 SR R3,R3 LOOP AR R2,R3 LA R3,1(R0,R3) C R3,=F'10' BNE LOOP CVD R2,DBL ED RESULT,DBL+6 WTO RESULT RETURN LTORG RESULT DC X' ' DBL DC D'0' END BEGIN C P U assembler

35 1950... – operační systém C P U C P U
15 0 X X XX XX X X XX XX XX X X X X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X XX X X X XX X XXX C P U operační systém loader PRINT NOGEN BEGIN BEGIN REGS SR R2,R2 SR R3,R3 LOOP AR R2,R3 LA R3,1(R0,R3) C R3,=F'10' BNE LOOP CVD R2,DBL ED RESULT,DBL+6 WTO RESULT RETURN LTORG RESULT DC X' ' DBL DC D'0' END BEGIN C P U assembler

36 1950... – operační systém myprog.exe C P U C P U
15 0 X X XX XX X X XX XX XX X X X X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X XX X X X XX X XXX C P U operační systém loader PRINT NOGEN BEGIN BEGIN REGS SR R2,R2 SR R3,R3 LOOP AR R2,R3 LA R3,1(R0,R3) C R3,=F'10' BNE LOOP CVD R2,DBL ED RESULT,DBL+6 WTO RESULT RETURN LTORG RESULT DC X' ' DBL DC D'0' END BEGIN C P U myprog.exe assembler

37 1950... – překladač myprog.exe C P U C P U
15 0 X X X X X X X X X XXXX X X X X X X X X X X X X XX X X X XX X X XX X X X XXX X X X X X XXXX X XX XX XXXX X X XXX X X XX X X X X X X XX X X X X X X X X X X X X X X XX X X X X X X X X X X X X C P U operační systém loader READ INPUT TAPE 5, 501, IA, IB, IC 501 FORMAT (3I5) IF (IA) 777, 777, 701 701 IF (IB) 777, 777, 702 702 IF (IC) 777, 777, 703 703 IF (IA+IB-IC) 777,777,704 704 IF (IA+IC-IB) 777,777,705 705 IF (IB+IC-IA) 777,777,799 777 STOP 1 799 S = FLOATF (IA + IB + IC) / 2.0 AREA = SQRT( S * (S - FLOATF(IA)) * (S - FLOATF(IB)) * + (S - FLOATF(IC))) WRITE OUTPUT TAPE 6, 601, IA, IB, IC , AREA STOP END C P U myprog.exe překladač Fortran

38 1970... – překladač C Hello, world! myprog.exe C P U
15 0 Hello, world! C P U operační systém loader #include <stdio.h> int main(int,char**) { printf( "Hello, world!\n"); } C P U myprog.exe překladač C

39 1980... – překladač C++ Hello, world! myprog.exe C P U
15 0 Hello, world! C P U operační systém loader #include <iostream> int main(int,char**) { std::cout << "Hello, world!\n"; } C P U myprog.exe překladač C++

40 1960... – interpret(er) C P U interpret operační systém
10 INPUT "What is your name: ", U$ 20 PRINT "Hello "; U$ 30 INPUT "How do you want: ", N 40 S$ = "" 50 FOR I = 1 TO N 60 S$ = S$ + "*" 70 NEXT I 80 PRINT S$ 90 INPUT "Do you want? ", A$ 100 IF LEN(A$) = 0 THEN 90 110 A$ = LEFT$(A$, 1) 120 IF A$ = "Y" THEN 30 130 PRINT "Goodbye ";U$ 140 END 15 0 X X XX XX X X XX XX XX X X X X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X XX X X X XX X XXX interpret C P U operační systém

41 Interpretace s mezikódem
10 INPUT "What is your name: ", U$ 20 PRINT "Hello "; U$ 30 INPUT "How do you want: ", N 40 S$ = "" 50 FOR I = 1 TO N 60 S$ = S$ + "*" 70 NEXT I 80 PRINT S$ 90 INPUT "Do you want? ", A$ 100 IF LEN(A$) = 0 THEN 90 110 A$ = LEFT$(A$, 1) 120 IF A$ = "Y" THEN 30 130 PRINT "Goodbye ";U$ 140 END 04FBC41E 77AB2000 1AE04E33 15 0 X X XX XX X X XX XX XX X X X X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X XX X X X XX X XXX překladač interpret C P U operační systém

42 interpretovaný mezikód (bytecode)
public class HelloWorld { public static void main(String[] args) { System.out.println( "Hello, world!"); } myprog.class C P U překladač 04FBC41E 77AB2000 1AE04E33 15 0 Hello, world! interpret C P U operační systém

43 JIT překladač public class HelloWorld { public static void
main(String[] args) { System.out.println( "Hello, world!"); } myprog.class C P U překladač 15 0 Hello, world! JIT překladač C P U operační systém

44 Srovnání JIT/non-JIT public class HelloWorld { public static void
main(String[] args) { System.out.println( "Hello, world!"); } myprog.class C P U překladač JIT překladač C P U operační systém #include <iostream> int main(int,char**) { std::cout << "Hello, world!\n"; } C P U operační systém loader myprog.exe C P U překladač

45 Srovnání JIT/non-JIT JIT (Java, C#, C++/CLI) non-JIT (C++)
Distribuuje se bytecode (.class, .exe) Distribuuje se (někdy) jako binární instrukce (.exe) Distribuce závislá na jazyku a překladači Distribuce závislá na procesoru a OS Překladač zná přesně cílovou architekturu, může pozorovat chování programu Překladač má dost času na překlad

46 Dynamické spojování…? public class HelloWorld { public static void
main(String[] args) { mylib.doit(); } myprog.class } překladač mylib.class překladač JIT překladač C P U operační systém int main() { doit(); } #include <iostream> int main() { std::cout << "Hello, world!\n"; } C P U operační systém loader myprog.exe překladač mylib.dll překladač

47 Dynamické spojování import acme.mylib; public class HelloWorld {
public static void main(String[] args) { mylib.doit(); } myprog.class public class HelloWorld { public static void main(String[] args) { System.out.println( "Hello, world!"); } překladač mylib.class překladač JIT překladač C P U operační systém #include "mylib.hpp" int main() { doit(); } // mylib.hpp void doit(); #include <iostream> int main() { std::cout << "Hello, world!\n"; } C P U operační systém loader myprog.exe “překladač” mylib.dll “překladač”

48 Překlad jednoduchého programu - statické spojování
// iostream #include <fstream> namespace std { extern ofstream cout, cerr; }; // iostream #include <fstream> namespace std { extern ofstream cout, cerr; }; iostream.obj msvcrt.lib // myprog.cpp #include <iostream> int main() { std::cout << "Hello, world!\n"; } Kompilátor myprog.obj Linker myprog.exe

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

50 Spojování modulů Kompilátor Linker Kompilátor myprog.cpp
#include "bee.hpp" int main(int,char**) { return B( 7); } myprog.obj 0000: ???????? export main(int,argv**) import B(int) Kompilátor bee.hpp #ifndef bee_hpp #define bee_hpp int B( int q); #endif myprog.exe 0000: : Linker bee.cpp #include "bee.hpp" int B( int q) { return q+1; } bee.obj 0000: export B(int) Kompilátor

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

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

53 Statické knihovny Standardní Standardní .obj Standardní .lib
Uživatelské .hpp Uživatelské .cpp Kompilátor Přeložené .obj Linker Spustitelný soubor .exe Knihovní .hpp Knihovna .lib Knihovní .cpp Kompilátor Přeložené .obj Librarian

54 Dynamické knihovny (Microsoft)
Standardní Standardní .obj Standardní .lib Uživatelské .hpp Uživatelské .cpp Kompilátor Přeložené .obj Linker Spustitelný soubor .exe Knihovní .hpp Knihovna .lib Knihovní .cpp Kompilátor Přeložené .obj Linker Knihovna .dll

55 Dynamické knihovny (GNU)
Standardní Standardní .o Standardní .a Uživatelské .hpp Uživatelské .cpp Kompilátor Přeložené .o Linker Spustitelný soubor Knihovní .hpp Knihovní .cpp Kompilátor Přeložené .o Librarian Knihovna .so

56 Deklarace a definice

57 Zápis sdělující, že věc (typ/proměnná/funkce/...) existuje
Deklarace a definice Deklarace Zápis sdělující, že věc (typ/proměnná/funkce/...) existuje Identifikátor Základní vlastnosti věci Umožňuje překladači přeložit kód, který na věc odkazuje V některých případech je k tomu zapotřebí i definice Definice Zápis, který určuje všechny vlastnosti věci Obsah třídy, inicializace proměnné, kód funkce Umožňuje překladači vygenerovat kód a data, která věc reprezentují za běhu Každá definice je i deklarace Deklarace umožňují (některá) použití věci bez definice Oddělený překlad modulů Vyřešení cyklických závislostí Zmenšení objemu překládaného zdrojového kódu

58 One-definition rule #1: Jedna překladová jednotka...
Deklarace a definice One-definition rule #1: Jedna překladová jednotka... (modul, tj. jedno .cpp včetně inkludovaných hpp) ... smí obsahovat nejvýše jednu definici věci One-definition rule #2: Program... (tj. .exe včetně připojených .dll) ... smí obsahovat nejvýše jednu definici proměnné nebo non-inline funkce Definice třídy, typu či inline funkce se v různých modulech opakovat smějí (typicky vložením téhož .hpp souboru) Nejsou-li opakované definice totožné, nebo nesouhlasí-li definice s deklarací, program je nekorektní Diagnostika na úrovni programu není normou požadována a překladače/linkery ji dělají jen v jednoduchých případech

59 Deklarace a definice tříd a typů
class A; class A { ... }; Struktura struct A; struct A { Unie (v C++ prakticky nepoužitelné) union A; union A { Pojmenovaný typ typedef A A2; typedef A * AP; typedef std::shared_ptr< A> AS; typedef A AA[ 10]; typedef A AF(); typedef AF * AFP1; typedef A (* AFP2)(); typedef std::vector< A> AV; typedef AV::iterator AVI;

60 Deklarace a definice proměnných
Globální proměnná extern int x, y, z; int x; int y = 729; int z( 729); Statická položka třídy class A { static int x, y, z; }; int A::x; int A::y = 729; int A::z( 729); Statická konstantní položka třídy static const int x = 729; Statická lokální proměnná void f() { static int x; static int y = 7, z( 7); } Nestatická položka třídy int x, y; Nestatická lokální proměnná int y = 7, z( 7);

61 Deklarace a definice funkcí
non-inline Deklarace (.hpp nebo .cpp) Definice (.cpp) Globální funkce int f( int, int); int f( int p, int q) { return p + q;} Statická metoda class A { static int f( int p); }; int A::f( int p) { return p + 1; } Nestatická metoda int f( int p); Virtuální metoda virtual int f( int p); int A::f( int) { return 0; inline Definice (.hpp nebo .cpp) Globální inline funkce inline int f( int p, int q) { return p + q; Nestatická inline metoda (a) inline int A::f( int p) Nestatická inline metoda (b) int f( int p) { return p+1;}

62 Umístění dat

63 Statická alokace = 1 instance na proces
Umístění dat Statická alokace = 1 instance na proces Globální proměnná Statická položka třídy Statická lokální proměnná [C++11] thread_local objekty = 1 instance na vlákno Zásobníková alokace = 1 instance na každé vyvolání Lokální proměnná Parametr předávaný hodnotou Návratová hodnota funkce Pomocná proměnná při výpočtu výrazu Dynamická alokace = řízeno programem Dynamicky alokovaná data (new/delete)

64 Číselné typy, ukazatele
Inicializace Číselné typy, ukazatele Statická alokace Inicializováno nulou Zásobníková nebo dynamická alokace Neinicializováno! Třídy / struktury Inicializováno konstruktorem Lze určit parametry pro konstruktor Není-li konstruktor, platí tato pravidla pro jednotlivé části Parametr předávaný hodnotou / návratová hodnota funkce Inicializován copy-constructorem Není-li definován, je generován překladačem

65 Okamžik inicializace a destrukce
Globální proměnná Před vstupem do main Po výstupu z main Statická položka třídy Statická lokální proměnná Při prvním průchodu řízení deklarací Lokální proměnná V okamžiku průchodu řízení deklarací Při výstupu z bloku Parametr předávaný hodnotou Před voláním funkce Před návratem z funkce Návratová hodnota funkce V příkazu return Po návratu z funkce Pomocná proměnná při výpočtu výrazu Když je vypočtena její hodnota Na konci příkazu (v deklaraci: na konci bloku) Dynamicky alokovaná data Při volání new Při volání delete

66 Nejdůležitější datové typy

67 Vybrané číselné typy bool false, true char
znak základní sady (např. ASCII, 8 bit) std::wchar_t znak rozšířené sady (např. Unicode, 16/32 bit) int celé číslo se znaménkem (obvykle 32 bit) unsigned celé číslo bez znaménka (obvykle 32 bit) long long extra velké celé číslo se znaménkem (64 bit) unsigned long long extra velké celé číslo bez znaménka (64 bit) std::size_t dostatečně velké číslo pro velikost čehokoliv (32/64 bit) double “reálné” číslo (Intel: 64 bit) long double přesnější “reálné” číslo (Intel: 80 bit) std::complex<double> komplexní číslo dané přesnosti

68 Další důležité typy std::string řetězec (nad char) std::wstring
řetězec (nad std::wchar_t) std::istream vstupní proud (nad char) std::wistream vstupní proud (nad std::wchar_t) std::ostream výstupní proud (nad char) std::wostream výstupní proud (nad std::wchar_t) struct T { … } struktura std::pair<T1,T2> uspořádaná dvojice s prvky typu T1 a T2 std::vector<T> pole prvků typu T std::list<T> seznam prvků typu T std::map<K,T> asociativní pole prvků typu T indexované typem K std::multimap<K,T> asociativní pole s opakováním

69 Složené typy jsou iluze poskytovaná překladačem
Složené typy v C++ Složené typy jsou iluze poskytovaná překladačem Souvislý úsek paměti dělený na elementární typy sizeof(T) = velikost tohoto úseku Zarovnání může vynutit nevyužití některých míst Veškerá manipulace je překladačem rozložena na manipulaci s elementárními typy V jednoduchých případech (POD) lze kopírovat jako blok bajtů Pole: T a[N] N-tice stejných typů N musí být překladači známá konstanta Třída (class nebo struct) Pojmenované položky různých typů Ve složitějších případech režijní informace Dědičnost implementována vložením předka Virtuální dědičnost vyžaduje nepřímé odkazy na předky

70 Ukazatel vs. hodnota

71 Reference, ukazatelé, iterátory
Konstrukce jazyka C++ Použití syntakticky shodné s hodnotou (r.a) T & const T & Ukazatel Konstrukce jazyka C/C++ Zvláštní operátory pro přístup k hodnotě (*p, p->a) Ukazatelová aritmetika pro přístup k sousedům v polích Manuální alokace/dealokace objektů T * const T * Chytré ukazatele Třída ve standardních knihovnách C++ Automatická dealokace při zániku odkazů std::shared_ptr< T> std::unique_ptr< T> Iterátory Třídy reprezentující odkazy na prvky kontejneru typu K Napodobenina ukazatelové aritmetiky K::iterator K::const_iterator

72 Referenční semantika C#/Java vs. C++
Referenční typy (C#,Java) Ukazatele na objekty (C++) class T { public int a; } class test { static void f( T z) { z.a = 3; static void g() T x = new T(); //vznik x.a = 1; T y = x; //druhý odkaz y.a = 2; // x.a == 2 f( x); // x.a == 3 //zrušení zařídí garbage collector public: int a; }; void f( T * z) z->a = 3; void g() T * x = new T; x->a = 1; T * y = x; y->a = 2; // x->a == 2 // x->a == 3 delete x; //zánik je nutno vyvolat ručně

73 Referenční semantika C#/Java vs. C++
Referenční typy (C#,Java) Chytré ukazatele na objekty (C++) class T { public int a; } class test { static void f( T z) { z.a = 3; static void g() T x = new T(); //vznik x.a = 1; T y = x; //druhý odkaz y.a = 2; // x.a == 2 f( x); // x.a == 3 //zrušení zařídí garbage collector public: int a; }; void f( T * z) z->a = 3; void g() std::shared_ptr< T> x = new T; x->a = 1; std::shared_ptr< T> y = x; y->a = 2; // x->a == 2 // x->a == 3 //zrušení při zániku posledního odkazu

74 Hodnotová semantika C#/Java vs. C++
Hodnotové typy (C#) Objekty (C++) struct T { int a; } class test { static void f( T z) { z.a = 3; static void g() T x; //vznik x.a = 1; T y = x; //kopie y.a = 2; // x.a == 1 f( x); //zrušení v okamžiku zániku proměnné class T { public: }; void f( T z) void g()

75 Hodnotová semantika C#/Java vs. C++
Předání odkazem (C#) (hodnotové typy) Předání odkazem (C++) struct T { int a; } class test { static void f( ref T z) { z.a = 3; static void g() T x; //vznik x.a = 1; f( ref x); // x.a == 3 class T { public: }; void f( T & z) void g() f( x);

76 Referenční semantika C#/Java vs. C++
Předání odkazem (C#) Referenční typy Předání odkazem (C++) Reference na ukazatel Užíváno řídce – nebezpečí chyb class T { public int a; } class test { static void f( ref T z) { z = new T(); //vznik static void g() T x = new T(); f( ref x); // x je nyní jiný objekt //zrušení zařídí garbage collector public: int a; }; void f( T * & z) delete z; //zánik je nutno vyvolat ručně z = new T; void g() T * x = new T; f( x); // *x je nyní jiný objekt delete x;

77 Dynamická alokace pole Dynamickou alokaci je nutné použít, pokud
my_class * p = 0; void f1() { p = new my_class( 20, 30); } void f2() delete p; Dynamická alokace pole std::vector je lepší void f2( std::size_t n) int * q = new int[ n]; q[ n-1] = p->m(); // ... delete[] q; Dynamickou alokaci je nutné použít, pokud Rozsah života objektu se nekryje s vyvoláním funkce, nebo Alokovaný objekt je třída s dědičností zařazená do datové struktury Ostatní případy lze většinou nahradit použitím kontejnerů Kontejnery skrývají dynamickou alokaci uvnitř

78 Chytré ukazatele řeší dealokaci samy
void f1() { std::unique_ptr< my_class> p = new my_class( 20, 30); std::unique_ptr< my_class> q = std::move( p); // nuluje p q.reset( new my_class( 10, 20)); // dealokuje původní objekt } // dealokuje druhý objekt void f2() std::shared_ptr< my_class> p std::shared_ptr< my_class> q = p; } // dealokuje oba objekty Chytré ukazatele řeší dealokaci samy C++11 unique_ptr vždy jen jediný odkaz zajistí překladač shared_ptr počítání odkazů režie za běhu Slabší a pomalejší než Garbage Collection problém: cyklické struktury

79 Reference

80 Reference Hodnotové typy (C++,C#,...) Referenční typy (C#,Java)
Lokálně existující objekty (C++) Reference na objekty (C++) Ukazatele na objekty (C++) f(int y); void g() { int x; //vznik int y = z; //kopie } //zrušení při //výstupu z bloku class T {...} T x = new T; T y = x; //druhý odkaz y.f(); //zrušení zařídí //garbage //collector T x; //kopie //zánik při T & y = x; //odkaz T * x = new T; T * y = x; y->f(); delete y; //zánik x->f(); //chyba

81 Ukazatel (*) a reference (&)
Reference a ukazatelé Ukazatel (*) a reference (&) Z hlediska implementace ekvivalentní - ukazatel i reference na objekt jsou reprezentovány adresou tohoto objektu Ukazatel umí navíc: Přesměrování jinam Speciální hodnotu – nulový ukazatel Ukazatelovou aritmetiku – posouvání na sousední objekty v poli Dealokaci – operator delete Referenci je možno použít jenom jedním z následujících způsobů: Typ proměnné Typ položky třídy Typ parametru funkce Typ návratové hodnoty funkce Odlišná syntaxe použití: Objekt a reference na objekt se syntakticky neliší Ukazatel se dereferencuje operátorem * Ukazatel na objekt se získá operátorem &

82 Pravidla pro začátečníky Kdy použít referenci: T &
Reference a ukazatelé Pravidla pro začátečníky Kdy použít referenci: T & Výstupní parametr Návratová hodnota funkce zpřístupňující objekt T & vector<T>::at(size_t i) Kdy použít konstantní referenci: const T & Obvykle pouze kvůli rychlosti Parametr typu struktura/třída Návratová hodnota funkce zpřístupňující objekt ke čtení const T & vector<T>::at(size_t i) const Kdy použít ukazatel (T *) Je-li objekt dynamicky alokován Je-li nutná schopnost přesměrování, null, nebo aritmetika Nelze-li referenci správně namířit v okamžiku inicializace Kdy použít konstantní ukazatel (const T *) Sekundární odkaz na objekt, schopný pouze čtení

83 Pravidla pro pokročilejší Vlastník dynamicky alokovaného objektu
Reference a ukazatelé Pravidla pro pokročilejší Vlastník dynamicky alokovaného objektu je zodpovědný za jeho zrušení - musí použít ukazatel “T *” nelze-li jednoznačně určit vlastníka, použijte “shared_ptr<T>” Uživatel objektu Pokud je životnost pozorovatele kratší než životnost objektu lze použít referenci – “T &” nebo “const T &” Pokud je životnost delší než životnost objektu nebo jinak komplikovaná je nutné použít ukazatel – “T *” nebo “const T *”

84 Novinky související s existencí reference
Inicializaci reference nelze nahradit přiřazením Třídy obsahující referenci musí mít konstruktor Nelze rozlišit skutečné parametry předávané hodnotou a odkazem Návratová hodnota funkce může být l-hodnota a.at( i) = x; Zvýšené nebezpečí nekorektních konstrukcí int & f() { int x; return x; // funkce vrátí referenci na neexistující objekt }

85 Funkce jako add nemůže vracet referenci
Vracení odkazem Funkce jako add nemůže vracet referenci add vrací hodnotu různou od všech svých parametrů hodnotu parametrů nesmí měnit reference nemá na co ukazovat Špatné řešení č. 1: Lokální proměnná Complex & add( const Complex & a, const Complex & b) { Complex r( a.Re + b.Re, a.Im + b.Im); return r; } BĚHOVÁ CHYBA: r zaniká při návratu z funkce

86 Funkce jako add nemůže vracet referenci
Vracení odkazem Funkce jako add nemůže vracet referenci add vrací hodnotu různou od všech svých parametrů hodnotu parametrů nesmí měnit reference nemá na co ukazovat Špatné řešení č. 2: Dynamická alokace Complex & add( const Complex & a, const Complex & b) { Complex * r = new Complex( a.Re + b.Re, a.Im + b.Im); return * r; } PROBLÉM: kdo to odalokuje ?

87 Funkce jako add nemůže vracet referenci
Vracení odkazem Funkce jako add nemůže vracet referenci add vrací hodnotu různou od všech svých parametrů hodnotu parametrů nesmí měnit reference nemá na co ukazovat Špatné řešení č. 3: Globální proměnná Complex g; Complex & add( const Complex & a, const Complex & b) { g = Complex( a.Re + b.Re, a.Im + b.Im); return g; } CHYBA: globální proměnná je sdílená Complex a, b, c, d, e = add( add( a, b), add( c, d));

88 Funkce jako add musí vracet hodnotou
Vracení odkazem Funkce jako add musí vracet hodnotou add vrací hodnotu různou od všech svých parametrů hodnotu parametrů nesmí měnit reference nemá na co ukazovat Správné řešení Complex add( const Complex & a, const Complex & b) { Complex r( a.Re + b.Re, a.Im + b.Im); return r; } Zkrácený (ekvivalentní) zápis return Complex( a.Re + b.Re, a.Im + b.Im);

89 Funkce jako add musí vracet hodnotou
Vracení odkazem Funkce jako add musí vracet hodnotou Complex add( const Complex & a, const Complex & b) { Complex r( a.Re + b.Re, a.Im + b.Im); return r; } Data se při vracení z funkce (několikrát) kopírují z = add( x, y); plnění proměnné r [constructor] kopie ven z funkce [copy-constructor] přiřazení [operator=] [C++11] rvalue reference mohou některá kopírování usnadnit Řešení bez kopírování existuje za cenu dynamické alokace u malých dat (Complex, string) se nevyplatí

90 Vracení odkazem Řešení bez kopírování Ekvivalent garbage-collection
class ComplexBody { public: ComplexBody( double r, double i) : re( r), im( i) {} double re, im; }; class Complex Complex( double r, double i) : b( new ComplexBody( r, i)) {} double re() const { return b->re; } double im() const { return b->im; } private: std::shared_ptr< ComplexBody> b; Complex add( const Complex & a, const Complex & b) { return Complex( a.re() + b.re(), a.im() + b.im()); return r; } Ekvivalent garbage-collection ComplexBody je sdíleno několika instancemi Complex a zaniká s posledním z nich Garbage-collection metodou mark-and-sweep bývá rychlejší než počítání odkazů (shared_ptr) Pro malé třídy (Complex) je kopírování levnější než dynamická alokace

91 Pravidla pro vracení hodnot odkazem
Reference a ukazatelé Pravidla pro vracení hodnot odkazem Pokud hodnota, kterou funkce vrací, existuje v nějakém objektu i po návratu z funkce, lze vrátit odkaz na tento objekt (konstantní) referencí T & vector<T>::back(); const T & vector<T>::back() const; T & T::operator+=(const T & b); T & T::operator++();// prefixová verze ++ vrací novou hodnotu Pokud se hodnota, kterou funkce vrací, nově spočítala a není nikde uložena, funkce musí vracet hodnotou T operator+( const T & a, const T & b); T T::operator++(int);// postfixová verze ++ vrací starou hodnotu

92 Standard Template Library
STL Standard Template Library

93 Prefabrikáty základních datových struktur
STL Kontejnery Prefabrikáty základních datových struktur Šablony parametrizované typem ukládaného objektu Všechny kontejnery pracují s kopiemi vkládaných hodnot Typ hodnot musí mít alespoň copy-constructor a destruktor Některé druhy kontejnerů či operací s nimi vyžadují i operator= nebo konstruktor bez parametrů Hodnoty se přidávají a odebírají metodami odpovídajícími druhu kontejneru K hodnotám je možno přistupovat pomocí iterátoru, který reprezentuje inteligentní ukazatel dovnitř kontejneru Prostřednictvím iterátoru je možno měnit uložené hodnoty

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

95 STL – Kontejnery Sekvenční kontejnery Odvozené kontejnery
Seřazeny podle vzrůstající režie [C++11] array< T, N> - pole se staticky danou velikostí vector< T> - pole prvků s přidáváním zprava basic_string< T> - vektor s terminátorem string = basic_string< char> - řetězec (ASCII) wstring = basic_string< wchar_t> - řetězec (Unicode) deque< T> - fronta s přidáváním a odebíráním z obou stran [C++11] forward_list< T> - jednosměrně vázaný seznam list< T> - obousměrně vázaný seznam Odvozené kontejnery queue< T> - fronta (maskovaná deque) priority_queue< T> - prioritní fronta (vylepšený vector) stack< T> - zásobník (maskovaný vector)

96 Pomocné metody kontejneru Test prázdnosti
STL – Kontejnery Pomocné metody kontejneru Test prázdnosti bool empty() const Počet prvků size_t size() const nepoužívat pro testy prázdnosti

97 Metody kontejneru, vracející iterátory
STL – Kontejnery Metody kontejneru, vracející iterátory Odkaz na začátek kontejneru - první platný prvek iterator begin() const_iterator begin() const Odkaz za konec kontejneru - za poslední platný prvek iterator end() const_iterator end() const iterator a const_iterator jsou typy definované uvnitř kontejneru, zvané iterátory přístupné konstrukcemi jako vector< int>::iterator vlastnosti iterátorů jsou mírně závislé na druhu kontejneru Iterátor kontejneru obsahujícího typ T je třída s operátory definovanými tak, aby se chovala podobně jako "T *" "(ukazatel na typ T) resp. "const T *" Vytváří se tak iluze, že kontejner je pole

98 Kategorie iterátorů Norma C++ definuje 5 kategorií iterátorů
random_access bidirectional forward output input Kategorie určuje, které syntaktické konstrukce musí iterátor umožňovat vector, basic_string a deque list forward_list Pro iterátor I jsou definovány tyto operace: output *I = x input *I /* pouze pro čtení */ random_access, bidirectional, forward *I /* čtení i zápis */ všechny kategorie ++I, I++ random_access, bidirectional, forward, input I1 == I2, I1 != I2 random_access, bidirectional --I, I-- random_access I += n, I + n, n + I I -= n, I - n, I1 - I2 I[ n] I1 < I2, I1 > I2, I1 <= I2, I1 >= I2

99 Operátory definované na iterátorech
STL – Iterátory Operátory definované na iterátorech přístup k prvku, na který iterátor ukazuje T & iterator::operator *() const const T & const_iterator::operator *() const posouvání iterátoru směrem ke konci jednosměrný iterátor iterator & iterator::operator++() posouvání iterátoru směrem k začátku obousměrný iterátor iterator & iterator::operator--() libovolný posun iterátor s přímým přístupem iterator operator+( iterator, int) iterator operator-( iterator, int)

100 Metody kontejneru pro vkládání
STL – Kontejnery Metody kontejneru pro vkládání iterator insert( iterator p, T x) vsune (kopii) x před prvek, na který ukazuje iterátor p vrací iterátor ukazující na vložený prvek void insert( iterator p, int n, T x) vsune n kopií x před prvek, na který ukazuje iterátor p template< typename other_iterator> void insert( iterator p, other_iterator b, other_iterator e) před prvek, na který ukazuje iterátor p, vsune kopii posloupnosti prvků ohraničené iterátory b a e Tato posloupnost nesmí být uvnitř téhož kontejneru Tato posloupnost může být součástí jiného druhu kontejneru je-li p == end(), vkládání připojuje na konec kontejneru všechny dříve existující iterátory odkazující na tento kontejner jsou po použití insert neplatné, včetně p výjimka: kontejnery list a forward_list iterátory mířící na původní prvky nebo end() zachovávají

101 Konstruktory kontejneru
STL – Kontejnery Konstruktory kontejneru K() Vytvoří prázdný kontejner (array: kontejner dané velikosti) K( int n, T x = T()) Vytvoří kontejner s n kopiemi hodnoty x Má-li typ T konstruktor bez parametrů, není třeba udávat x template< typename other_iterator> K( other_iterator b, other_iterator e) Vytvoří kontejner naplněný kopií posloupnosti prvků ohraničené iterátory b a e Tato posloupnost může být součástí jiného druhu kontejneru

102 STL – Kontejnery [C++11] move
Původní vkládací metody kopírují vkládané prvky Zbytečná práce, nemožnost použití některých typů (unique_ptr) [C++11] move Metody insert a push_back/front mají move varianty iterator insert( iterator p, T && x) Překladač ji použije, je-li parametrem pomocná proměnná... k.insert( it, a + b); // operator nebo funkce vracejici hodnotou k.insert( it, T( x, y, z)); // bezejmenny objekt ... nebo pokud je použito std::move k.insert( it, std::move( a)); Move-semantika: Proměnná a bude (může být) vyprázdněna Move-semantika poskytuje úsporu času (a prostoru), pokud typ T obsahuje dynamicky alokovaná data je na move semantiku připraven (má move-konstruktory) std::vector< std::string> k; std::string a = "..."; k.push_back( a + ".kzr");

103 STL – Kontejnery [C++11] emplace
Původní vkládací metody kopírují vkládané prvky Zbytečná práce, nemožnost použití některých typů (unique_ptr) [C++11] emplace Metody insert a push_back/front mají emplace varianty iterator emplace( iterator p, T1 && x1, ..., Tn && xn); void emplace_back( T1 && x1, ..., Tn && xn); void emplace_front( T1 && x1, ..., Tn && xn); Do kontejneru je přidán nový prvek inicializovaný konstruktorem s parametry x1, ..., xn std::vector< std::vector< int> > k; k.emplace_back( 100, 0); Šetří kopírování vkládaného prvku oproti původnímu zápisu k.push_back( std::vector< int>( 100, 0)); Šetří i v případě nepřipraveného typu bez move-semantiky V případě vector< int> by to kopírování ušetřila sama move-semantika Poznámka: rvalue reference v hlavičce emplace funkcí dovolují i lvalue operandy pomocí skládání referencí a funkce std::forward

104 Metody kontejneru pro odebírání
STL – Kontejnery Metody kontejneru pro odebírání iterator erase( iterator p) vyjme prvek, na který ukazuje iterátor p p nesmí být rovno end() vrací iterátor ukazující na prvek za vyjmutým prvkem (nebo end()) iterator erase( iterator p, iterator e) vyjme všechny prvky mezi p a e, včetně p a vyjma e vrací iterátor odkazující na prvek e (původní iterátor e nebude platný) všechny iterátory odkazující na tento kontejner jsou po použití erase neplatné, včetně p a e výjimka: kontejnery list a forward_list iterátory mířící na nesmazané prvky zachovávají void clear() { erase( begin(), end()); }

105 Odvozené funkce manipulace s konci kontejneru
STL – Kontejnery Odvozené funkce manipulace s konci kontejneru Přidání jednotlivého prvku void push_front( T x) { return insert( begin(), x); } list, deque void push_back( T x) { return insert( end(), x); } list, deque, vector Odebrání jednotlivého prvku void pop_front() { return erase( begin()); } void pop_back() { return erase( --end()); }

106 Odvozené funkce kontejneru pro přístup k prvkům Prvky na koncích
STL – Kontejnery Odvozené funkce kontejneru pro přístup k prvkům Prvky na koncích list, deque, vector podmínka: ! empty() T & front() const T & front() const obě provádějí { return * begin(); } T & back() const T & back() const { auto it = end(); --it; return * it; } [C++11] auto umožňuje deklaraci proměnné bez uvedení typu typ si odvodí překladač z inicializace, v tomto případě K::[const_]iterator

107 Odvozené funkce kontejneru pro přístup k prvkům Prvky uprostřed
STL – Kontejnery Odvozené funkce kontejneru pro přístup k prvkům Prvky uprostřed deque, vector, string podmínka: n < size() at: porušení podmínky vyvolá výjimku operator[]: porušení podmínky způsobí nedefinované chování T & at( int n) T & operator[]( int n) const T & at( int n) const const T & operator[]( int n) const všechny provádějí return * ( begin() + n);

108 STL - Kontejnery složitost operace na kontejneru s n prvky list deque
vector basic_string přídání / odebrání jednoho prvku na začátku push_front pop_front konstantní funkce neexistuje přídání / odebrání jednoho prvku na i-té pozici insert erase min( i, n - i) n - i přídání / odebrání m prvků na i-té pozici m přesuny mezi seznamy (splice) jsou konstantní m +min( i, n - i) m + n - i přídání / odebrání jednoho prvku na konci push_back pop_back nalezení i-tého prvku begin() + i paměťová náročnost kontejneru s prvky velikosti s (s + K) * n K řádově 16 B q * s * n q kolem 1.2

109 Asociativní kontejnery

110 Asociativní kontejnery
STL - Kontejnery Asociativní kontejnery Uspořádané (samovyvažující se stromy) set<T> - množina multiset<T> - množina s opakováním map<K,T> - asociativní pole, tj. parciální zobrazení K -> T multimap<K,T> - relace s rychlým vyhledáváním podle klíče K [C++11] Hashované [C++11] unordered_set<T> - množina [C++11] unordered_multiset<T> - množina s opakováním [C++11] unordered_map<K,T> - asociativní pole, tj. parciální zobrazení K -> T [C++11] unordered_multimap<K,T> - relace s rychlým vyhledáváním podle klíče K pair<A,B> - pomocná šablona uspořádané dvojice s položkami first, second

111 Uspořádané kontejnery vyžadují uspořádání na klíčích
STL - Kontejnery Uspořádané kontejnery vyžadují uspořádání na klíčích Klíčem se rozumí první parametr šablony kontejneru Uspořádání se obvykle definuje operátorem < definovaným na typu klíče Pozor na konstrukce typu set< char *> Uspořádání lze rovněž zadat přídavným parametrem šablony Definované uspořádání nemusí být antisymetrická relace pokud platí ! (x < y) && ! (y < x) pak jsou prvky x a y považovány za ekvivalentní

112 Uspořádané kontejnery vyžadují uspořádání na klíčích
STL - Kontejnery Uspořádané kontejnery vyžadují uspořádání na klíčích Vystačí si s operací < V nejjednodušším případě to funguje samo std::map< std::string, int> mapa; Pokud typ uspořádání nemá, lze jej definovat obecně bool operator<( const Klic & a, const Klic & b) { return ...; } std::map< Klic, int> mapa; Pokud obecná definice uspořádání nevyhovuje, lze definovat uspořádání funktorem pouze pro daný typ kontejneru struct Usporadani { bool operator()( const Klic & a, const Klic & b) const { return ...; } }; std::map< Klic, int, Usporadani> mapa; Pokud různé instance kontejneru mají mít různé uspořádání, lze do funktoru doplnit datové položky struct Usporadani { Usporadani( bool a); /*...*/ bool ascending; }; std::map< Klic, int, Usporadani> mapa( Usporadani( true));

113 Uspořádání na klíčích – implementace
STL - Kontejnery Uspořádání na klíčích – implementace Knihovny definují funktor std::less< K> template< typename K> class less { public: bool operator()( const K & a, const K & b) const { return a < b; } }; Šablona kontejneru má typový parametr - typ funktoru template< typename K, typename T, typename L = std::less< K> > class map { public: Konstruktor kontejneru dostává hodnotu funktoru explicit map( const L & c = L()) : cmp_( c) { /*...*/ } /*...*/ private: Kontejner drží jednu instanci funktoru L cmp_; Metody kontejneru volají operátor () na instanci funktoru iterator find_( /*...*/) { /*...*/ if ( cmp_( x, y) ) /*...*/ }

114 Uspořádání na klíčích – hashující kontejnery
STL - Kontejnery Uspořádání na klíčích – hashující kontejnery Kontejner vyžaduje funktory pro hashování a pro porovnání template< typename K> class hash { public: std::size_t operator()( const K & a) const { /*...*/ } }; class equal_to { public: bool operator()( const K & a, const K & b) const { return a == b; } Šablona kontejneru má dva další parametry template< typename K, typename T, typename H = std::hash< K>, typename E = std::equal_to< K> > class unordered_map; Konstruktor kontejneru dostává hodnoty funktorů explicit unordered_map( std::size_t initial_bucket_count = /*...*/, const H & h = L(), const E & e = E());

115 Asociativní kontejnery – procházení Kontejnery lze procházet iterátory
STL - Kontejnery Asociativní kontejnery – procházení Kontejnery lze procházet iterátory Uspořádané kontejnery jsou prezentovány v uspořádání podle klíčů Iterátor je bidirectional Hashující kontejnery jsou prezentovány v implementačně-definovaném pořadí Iterátor je forward Metody begin() a end() a operátory iterátorů mají stejný význam, jako u sekvenčních kontejnerů Kontejnery [unordered_][multi]map< K, T> obsahují uspořádané dvojice - typ std::pair< const K, T> Klíč (it->first) nelze modifikovat, hodnotu (it->second) ano Procházení celého asociativního kontejneru se užívá málokdy Iterátory se častěji získávají vyhledáváním

116 Asociativní kontejnery – vyhledávání
STL - Kontejnery Asociativní kontejnery – vyhledávání iterator set::find( T x) iterator multiset::find( T x) iterator map::find( K x) iterator multimap::find( K x) iterator unordered_set::find( T x) iterator unordered_multiset::find( T x) iterator unordered_map::find( K x) iterator unordered_multimap::find( K x) pokud v kontejneru existuje prvek s klíčem ekvivalentním x: vrací iterátor ukazující na první takový prvek multiset, multimap: další takové prvky jsou dostupné operací ++ jinak vrací end() složitost operace uspořadné kontejnery: O( log( size())) hashující kontejnery: průměrně O(1), nejhůře O( size())

117 Uspořádané kontejnery - intervalové dotazy
STL - Kontejnery Uspořádané kontejnery - intervalové dotazy iterator set::lower_bound( T x) iterator multiset::lower_bound( T x) iterator map::lower_bound( K x) iterator multimap::lower_bound( K x) vrací první prvek jehož klíč není menší než x, případně end() iterator set::upper_bound( T x) iterator multiset::upper_bound( T x) iterator map::upper_bound( K x) iterator multimap::upper_bound( K x) vrací první prvek jehož klíč je větší než x, případně end() dvojici takto získaných iterátorů lze využít v jiných funkcích téhož i jiného kontejneru operace mají logaritmickou složitost

118 Asociativní kontejnery – vyhledávání s opakováním
STL - Kontejnery Asociativní kontejnery – vyhledávání s opakováním pair<iterator,iterator> multiset::equal_range( T x) pair<iterator,iterator> multimap::equal_range( K x) pair<iterator,iterator> unordered_multiset::equal_range( T x) pair<iterator,iterator> unordered_multimap::equal_range( K x) vrací polouzavřený interval [first,second) obsahující prvky s daným klíčem pokud prvky neexistují, vrací prázdný interval na místě, kde by byly odpovídá std::make_pair( lower_bound( x), upper_bound( x)) existuje i pro kontejnery bez opakování pair<iterator,iterator> set::equal_range( T x) pair<iterator,iterator> map::equal_range( K x) pair<iterator,iterator> unordered_set::equal_range( T x) pair<iterator,iterator> unordered_map::equal_range( K x) složitost operace uspořadné kontejnery: O( log( size())) hashující kontejnery: průměrně O(1), nejhůře O( size())

119 Asociativní kontejnery - vkládání set a map
STL - Kontejnery Asociativní kontejnery - vkládání set a map pair< iterator, bool> set::insert( T x) pair< iterator, bool> unordered_set::insert( T x) pair< iterator, bool> map::insert( pair< const K, T> x) pair< iterator, bool> unordered_map::insert( pair< const K, T> x) pokud prvek ekvivalentní x (resp. x.first) v kontejneru není: kopie x se vloží do kontejneru vrací pair< iterator, bool>( p, true) kde p je iterátor ukazující na vložený prvek pokud prvek ekvivalentní x (resp. x.first) v kontejneru již je: vrací pair< iterator, bool>( p, false) kde p je iterátor ukazující na existující prvek ekvivalentní x [C++11] existuje též move-verze insert a emplace složitost operace uspořadné kontejnery: O( log( size())) hashující kontejnery: průměrně O(1), nejhůře O( size())

120 Asociativní kontejnery - vkládání multiset a multimap
STL - Kontejnery Asociativní kontejnery - vkládání multiset a multimap iterator multiset::insert( T x) iterator unordered_multiset::insert( T x) iterator multimap::insert( pair< const K, T> x) iterator unordered_multimap::insert( pair< const K, T> x) kopie x se vloží do kontejneru vrací iterátor ukazující na vložený prvek [C++11] existuje též move-verze insert a emplace složitost operace uspořadné kontejnery: O( log( size())) hashující kontejnery: průměrně O(1), nejhůře O( size())

121 Asociativní kontejnery - odebírání podle klíče
STL - Kontejnery Asociativní kontejnery - odebírání podle klíče size_type [unordered_][multi]set::erase( T x) size_type [unordered_][multi]map::erase( K x) odebere všechny prvky s klíčem ekvivalentním zadanému x vrací počet odebraných prvků složitost operace pro N odebraných prvků uspořadné kontejnery: O( log( size()) + N) hashující kontejnery: průměrně O(N), nejhůře O( size()) po operaci budou iterátory na odebrané prvky neplatné asociativní kontejnery neinvalidují iterátory při insert/erase vyjma odebraných prvků

122 Asociativní kontejnery - odebírání podle iterátoru - jeden prvek
STL - Kontejnery Asociativní kontejnery - odebírání podle iterátoru - jeden prvek void erase( iterator p) odebere prvek na který odkazuje iterátor p p nesmí být rovno end() operace má konstantní složitost rozumí se amortizovaná/průměrná složitost podle iterátorů - interval void erase( iterator p, iterator e) odebere všechny prvky mezi p a e, včetně p a vyjma e složitost operace pro N odebraných prvků uspořadné kontejnery: O( log( size()) + N) hashující kontejnery: průměrně O(N), nejhůře O( size()) po operaci budou iterátory na odebrané prvky neplatné

123 map/unordered_map – at/operator [ ]
STL - Kontejnery map/unordered_map – at/operator [ ] T & [unordered_]map::at( K x) Vrátí referenci na hodnotovou (second) složku prvku s klíčem ekvivalentním x Pokud takový prvek neexistuje, vyvolá výjimku T & [unordered_]map::operator[]( K x) { return (*((insert(make_pair( x, T()))).first)).second; } Pokud takový prvek neexistuje, vytvoří jej Jeho hodnotová složka bude T() Tento operátor je možno používat pro vkládání a přepisování prvků kontejneru Kontejner [unordered_]map se chová jako asociativní pole (PHP) Pozor: U sekvenčních kontejnerů automatické přidání nefunguje Pozor: Pro čtení nemusí být vhodné – přidává neexistující prvky

124 Algoritmy

125 Šablona funkce for_each
STL – Algoritmy Šablona funkce for_each <algorithm> template<class InputIterator, class Function> Function for_each( InputIterator first, InputIterator last, Function f); first a last jsou iterátory, určující procházený úsek nějakého kontejneru (všetně first, mimo last) f je buďto globální funkce (ukazatel na funkci), nebo funktor, tj. třída obsahující operator() Funkce f (případně metoda operator()) je zavolána na každý prvek v zadaném intervalu prvek se předává jako * iterator, což může být odkazem funkce f tedy může modifikovat prvky seznamu

126 Šablona funkce for_each
STL – Algoritmy Šablona funkce for_each <algorithm> template<class InputIterator, class Function> Function for_each( InputIterator first, InputIterator last, Function f) { for (; first != last; ++first) f( * first); return f; } Takto napsanou šablonu lze zkompilovat pro jakékoliv f, na které lze aplikovat operátor (), tedy jak pro funkci, tak pro funktor

127 Použití funkce for_each
STL – Algoritmy Použití funkce for_each void my_function( double & x) { x += 1; } void increment( list< double> & c) for_each( c.begin(), c.end(), my_function); [C++11] Lambda Nová syntaktická konstrukce generující funktor for_each( c.begin(), c.end(), []( double & x){ x += 1;});

128 Použití funkce for_each
STL – Algoritmy Použití funkce for_each class my_functor { public: double v; void operator()( double & x) const { x += v; } my_functor( double p) : v( p) {} }; void add( list< double> & c, double value) { for_each( c.begin(), c.end(), my_functor( value)); } [C++11] Lambda for_each( c.begin(), c.end(), [=]( double & x){ x += value;});

129 Použití funkce for_each
STL – Algoritmy Použití funkce for_each class my_functor { public: double s; void operator()( const double & x) { s += x; } my_functor() : s( 0.0) {} }; double sum( const list< double> & c) { my_functor f; f = for_each( c.begin(), c.end(), f); return f.s; } [C++11] Lambda { double s = 0.0; for_each( c.begin(), c.end(), [&]( const double & x){ s += x;}); return s;

130 Použití funkce for_each
STL – Algoritmy Použití funkce for_each class my_functor { public: double s; void operator()( const double & x) { s += x; } my_functor() : s( 0.0) {} }; double sum( const list< double> & c) { my_functor f; for_each( c.begin(), c.end(), f); return f.s; } Pozor: f se předává hodnotou - tato implementace vždy vrací 0.0

131 Použití funkce for_each
STL – Algoritmy Použití funkce for_each class my_functor { public: double s; void operator()( const double & x) { s += x; } my_functor() : s( 0.0) {} }; double sum( const list< double> & c) { return for_each( c.begin(), c.end(), my_functor()).s; }

132 Lambda

133 C++11 Lambda výrazy Motivace Řešení class ftor { public:
ftor(int a, int b) : a_(a),b_(b) { } bool operator()(int x) const { return x*a_<b_; } private: int a_, b_; }; typedef std::vector<int> v_t; v_t v; v_t::iterator vi=remove_if(v.begin(), v.end(), ftor(m, n)); Řešení std::vector<int> v; auto vi=remove_if(v.begin(), v.end(), [=](int x){ return x*m<n; }); C++11

134 C++11 Lambda výrazy Lambda výraz Deklaruje třídu ve tvaru
[ capture ]( params ) mutable -> rettype { body } Deklaruje třídu ve tvaru class ftor { public: ftor( TList ... plist) : vlist( plist) ... { } rettype operator()( params ) const { body } private: TList ... vlist; }; vlist je určen proměnnými použitými v body TList je určen jejich typy a upraven podle capture operator() je const pokud není uvedeno mutable Lambda výraz je nahrazen vytvořením objektu ftor( vlist ...) C++11

135 Lambda výrazy – návratový typ a typ funkce
Návratový typ operátoru Explicitně definovaný návratový typ []() -> int { … } Automaticky určen pro tělo lambda funkce ve tvaru []() { return V; } Jinak void C++11

136 Lambda výrazy – capture
[ capture ]( params ) mutable -> rettype { body } Způsob zpřístupnění vnějších entit Určuje typy datových položek a konstruktoru funktoru Explicitní capture Programátor vyjmenuje všechny vnější entity v capture [a,&b,c,&d] entity označené & předány odkazem, ostatní hodnotou Implicitní capture Překladač sám určí vnější entity, capture určuje způsob předání [=] [=,&b,&d] předání hodnotou, vyjmenované výjimky odkazem [&] [&,a,c] předání odkazem, vyjmenované výjimky hodnotou C++11

137 Lambda výrazy – příklad
int a = 1, b = 1, c = 1; auto m1 = [a, &b, &c]() mutable { auto m2 = [a, b, &c]() mutable { std::cout << a << b << c; a = 4; b = 4; c = 4; }; a = 3; b = 3; c = 3; m2(); a = 2; b = 2; c = 2; m1(); Co to vypíše? C++11 123234

138 Další algoritmy

139 STL – Algoritmy Šablona funkce find <algorithm>
template<class InputIterator, class T> InputIterator find( InputIterator first, InputIterator last, const T & value) { for (; first != last; ++first) if ( * first == value ) break; return first; }

140 Šablona funkce find_if
STL – Algoritmy Šablona funkce find_if <algorithm> template<class InputIterator, class Predicate> InputIterator find_if( InputIterator first, InputIterator last, Predicate pred) { for (; first != last; ++first) if ( pred( * first) ) break; return first; } Predikát pred může být funkce nebo funktor

141 STL – Algoritmy Přehled algoritmů Průchod kontejnerem Čtení kontejnerů
for_each Čtení kontejnerů find, find_if - první prvek s danou vlastností find_end - poslední výskyt druhé sekvence v první find_first_of - první výskyt některého prvku druhé sekvence v první adjacent_find - první prvek ekvivalentní sousednímu count, count_if - počet prvků s danou vlastností mismatch - první odlišnost dvou sekvencí equal - test shody dvou sekvencí search - první výskyt druhé sekvence v první search_n - první n-násobný výskyt dané hodnoty

142 Modifikace kontejnerů výměnou prvků
STL – Algoritmy Přehled algoritmů Swap swap - výměna obsahu dvou objektů Pomocí parciální/explicitní specializace bývá implementována rychleji, než kopírování Modifikace kontejnerů výměnou prvků swap_ranges - výměna obsahu dvou sekvencí (volá swap) iter_swap - výměna obsahu dvou jednoprvkových sekvencí Modifikace kontejnerů permutací (voláním swap) partition, stable_partition - přemístění prvků s danou vlastností dopředu random_shuffle - náhodná permutace dle zadaného náhodného generátoru reverse - otočení posloupnosti rotate, rotate_copy - rotace prvků

143 Modifikace kontejnerů přiřazením
STL – Algoritmy Přehled algoritmů Modifikace kontejnerů přiřazením copy, copy_backward - kopie první sekvence do druhé transform - aplikace zadané unární/binární operace na každý prvek první/první a druhé sekvence a zapsání výsledku do druhé/třetí sekvence replace, replace_if - nahrazení prvků s danou vlastností jinou hodnotou replace_copy, replace_copy_if - kopie s nahrazením fill, fill_n - naplnění sekvence danou hodnotou generate, generate_n - naplnění sekvence z daného generátoru Modifikace kontejnerů odebráním remove, remove_if - smazání prvků s danou vlastností unique, unique_copy - smazání opakujících se sousedních prvků vhodné pro setříděné nebo asociativní kontejnery Pozor: Tyto funkce neprodlužují ani nezkracují kontejner

144 Pozor: Algoritmy neprodlužují ani nezkracují kontejner
STL – Algoritmy Přehled algoritmů Pozor: Algoritmy neprodlužují ani nezkracují kontejner vector< int> a, b; a.push_back( 1); a.push_back( 2); a.push_back( 3); copy( a.begin(), a.end(), b.begin()); // ilegální použití Pro tyto účely existují "vkládající iterátory" <iterator> obsahuje tyto funkce vracející iterátory back_inserter( K) - iterátor vkládající na konec kontejneru K front_inserter( K) - iterátor vkládající na začátek kontejneru K inserter( K, I) - iterátor vkládající před iterátor I do kontejneru K tyto iterátory jsou pouze výstupní lze je použít jako cíl ve funkcích typu copy copy( a.begin(), a.end(), back_inserter( b));

145 Operace na setříděných kontejnerech
STL – Algoritmy Přehled algoritmů min, max - minimum a maximum ze dvou hodnot Třídění a spol. sort, stable_sort - třídění partial_sort, partial_sort_copy, nth_element - polotovary třídění push_heap, pop_heap, make_heap, sort_heap - operace na haldě min_element, max_element lexicographical_compare next_permutation, prev_permutation Operace na setříděných kontejnerech lower_bound, upper_bound, equal_range - hledání prvku binary_search - test na přítomnost prvku includes - test podmnožinovosti merge, inplace_merge - sjednocení s opakováním set_union, set_intersection - sjednocení, průnik set_difference, set_symmetric_difference - množinový rozdíl

146 Konstruktory a destruktory
Constructors and Destructors

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

148 Speciální metody tříd Konstruktor bez parametrů (default constructor)
XXX(); Používán u proměnných bez inicializace Kompilátor se jej pokusí vygenerovat, je-li to třeba a pokud třída nemá vůbec žádný konstruktor: Položky, které nejsou třídami, nejsou generovaným konstruktorem inicializovány Generovaný konstruktor volá konstruktor bez parametrů na všechny předky a položky To nemusí jít např. pro neexistenci takového konstruktoru Destruktor ~XXX(); Používán při zániku objektu Kompilátor se jej pokusí vygenerovat, je-li to třeba a třída jej nemá To nemusí jít kvůli ochraně přístupu Pokud je objekt destruován operátorem delete aplikovaným na ukazatel na předka, musí být destruktor v tomto předku deklarován jako virtuální virtual ~XXX();

149 Speciální metody tříd – C++11
copy/move Speciální metody tříd – C++11

150 C++11 copy/move Speciální metody tříd Copy constructor
T( const T & x); Move constructor T( T && x); Copy assignment operator T & operator=( const T & x); Move assignment operator T & operator=( T && x); C++11

151 C++11 copy/move Překladačem definované chování (default)
Copy constructor T( const T & x) = default; aplikuje copy constructor na složky Move constructor T( T && x) = default; aplikuje move constructor na složky Copy assignment operator T & operator=( const T & x) = default; aplikuje copy assignment operator na složky Move assignment operator T & operator=( T && x) = default; aplikuje move assignment operator na složky default umožňuje vynutit defaultní chování C++11

152 C++11 copy/move Podmínky automatického defaultu
Copy constructor/assignment operator pokud není explicitně deklarován move constructor ani assignment operator budoucí normy pravděpodobně zakážou automatický default i v případě přítomnosti druhé copy metody nebo destruktoru Move constructor/assignment operator pokud není deklarována žádná ze 4 copy/move metod ani destruktor C++11

153 C++11 copy/move Nejběžnější kombinace Neškodná třída
Nedeklaruje žádnou copy/move metodu ani destruktor Neobsahuje složky vyžadující zvláštní péči (ukazatele) Složky vyžadující zvláštní péči Překladačem generované chování (default) nevyhovuje Bez podpory move (před C++11) T( const T & x); T & operator=( const T & x); ~T(); Plná podpora copy/move T( T && x); T & operator=( T && x); C++11

154 C++11 copy/move Další kombinace Nekopírovatelná třída
Např. dynamicky alokované živé objekty v simulacích T( const T & x) = delete; T & operator=( const T & x) = delete; delete zakazuje generování překladačem Destruktor může ale nemusí být nutný Přesouvatelná nekopírovatelná třída Např. unikátní vlastník jiného objektu (viz std::unique_ptr< U>) T( T && x); T & operator=( T && x); ~T(); Pravidla jazyka zakazují generování copy metod překladačem Destruktor typicky bývá nutný C++11

155 Konverze

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

157 Konstruktory a spol. Typické situace

158 Inicializace je v zodpovědnosti uživatele
Konstruktory a spol. POD: Plain-Old-Data Položky jsou veřejné Inicializace je v zodpovědnosti uživatele class T { public: std::string x_; }; Často se používá struct struct T {

159 Všechny položky jsou neškodné Položky mají svoje konstruktory
Konstruktory a spol. Všechny položky jsou neškodné Položky mají svoje konstruktory Třída nemusí mít žádný konstruktor class T { public: // ... const std::string & get_x() const { return x_; } void set_x( const std::string & s) { x_ = s; } private: std::string x_; };

160 Všechny položky jsou neškodné Položky mají svoje konstruktory
Konstruktory a spol. Všechny položky jsou neškodné Položky mají svoje konstruktory Konstruktor se hodí pro pohodlnou inicializaci V takovém případě je (většinou) nutný i konstruktor bez parametrů Konstruktory s jedním parametrem označeny explicit class T { public: T() {} explicit T( const std::string & s) : x_( s) {} T( const std::string & s, const std::string & t) : x_( s), y_( t) {} // ... metody ... private: std::string x_, y_; };

161 Některé položky jsou mírně nebezpečné
Konstruktory a spol. Některé položky jsou mírně nebezpečné Některé položky nemají vhodné konstruktory Číselné typy včetně bool, char Konstruktor je nutný pro inicializaci V takovém případě je (většinou) nutný i konstruktor bez parametrů Konstruktory s jedním parametrem označeny explicit class T { public: T() : x_( 0), y_( 0) {} explicit T( int s) : x_( s), y_( 0) {} T( int s, int t) : x_( s), y_( t) {} // ... metody ... private: int x_, y_; };

162 Některé položky jsou hodně nebezpečné
Konstruktory a spol. Některé položky jsou hodně nebezpečné Některé položky nemají použitelnou semantiku kopírování Ukazatele (E *) na dynamicky alokovaná data Semantika definovaná překladačem nevyhovuje Je nutný copy constructor, operator= a destruktor Je nutný i jiný konstruktor, např. bez parametrů class T { public: T() : p_( new Data) {} T( const T & x) : p_( new Data( * x.p_)) {} T & operator=( const T & x) { T tmp( x); swap( tmp); return * this;} ~T() { delete p_; } void swap( T & y) { std::swap( p_, y.p_); } private: Data * p_; };

163 C++11 Konstruktory a spol. Některé položky jsou hodně nebezpečné
Je nutný copy/move constructor/operator= a destruktor Je nutný i jiný konstruktor, např. bez parametrů class T { public: T() : p_( new Data) {} T( const T & x) : p_( new Data( * x.p_)) {} T( T && x) : p_( x.p_) { x.p_ = 0; } T & operator=( const T & x) { T tmp( x); swap( tmp); return * this;} T & operator=( T && x) { T tmp( std::move( x)); swap( tmp); return * this;} ~T() { delete p_; } void swap( T & y) { std::swap( p_, y.p_); } private: Data * p_; }; C++11

164 C++11 Konstruktory a spol. Některé položky jsou hodně nebezpečné
Třída se zakázaným kopírováním Ale schopná přesunu class T { public: T() : p_( new Data) {} T( const T & x) = delete; T( T && x) : p_( x.p_) { x.p_ = 0; } T & operator=( const T & x) = delete; T & operator=( T && x) { T tmp( std::move( x)); swap( tmp); return * this;} ~T() { delete p_; } void swap( T & y) { std::swap( p_, y.p_); } private: Data * p_; }; C++11

165 C++11 Konstruktory a spol. Použití unique_ptr
Třída se zakázaným kopírováním Ale schopná přesunu class T { public: T() : p_( new Data) {} private: std::unique_ptr< Data> p_; }; C++11

166 C++11 Konstruktory a spol. Použití unique_ptr
Třída s povoleným kopírováním class T { public: T() : p_( new Data) {} T( const T & x) : p_( new Data( * x.p_)) {} T( T && x) = default; T & operator=( const T & x) { T tmp( x); swap( tmp); return * this;} T & operator=( T && x) = default; void swap( T & y) { std::swap( p_, y.p_); } private: std::unique_ptr< Data> p_; }; C++11

167 C++11 Konstruktory a spol. Abstraktní třída
Se zákazem kopírování/přesouvání class T { protected: T() {} T( const T & x) = delete; T & operator=( const T & x) = delete; public: virtual ~T() {} }; C++11

168 C++11 Konstruktory a spol. Abstraktní třída Se podporou klonování
class T { protected: T() {} T( const T & x) = default; T & operator=( const T & x) = delete; public: virtual ~T() {} virtual T * clone() const = 0; }; C++11

169 Vznik a zánik objektů

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

171 Vznik a zánik objektů Parametry předávané hodnotou
Předání parametru je realizováno voláním kopírovacího konstruktoru Tento konstruktor je volán na místě volání funkce před jejím zavoláním Kompilátor dokáže tento konstruktor vytvořit vlastními prostředky Destruktor objektu je vyvolán před návratem z funkce Pokud je skutečný parametr jiného typu, před voláním kopírovacího konstruktoru dojde ke konverzi Tato konverze může zahrnovat vznik pomocného objektu a tedy volání dalšího konstruktoru (a destruktoru) void f( XXX a) } void g() { XXX b; f( b); // konstruktor XXX( const XXX &) f( 1); // konstruktor XXX( int) – po něm se volá XXX( const XXX &)

172 Vznik a zánik objektů Globální proměnné
Pro každý globální objekt (a statickou položku třídy) je vyvolán konstruktor (pokud je neprázdný) před vstupem řízení do funkce main Po jejím opuštění (nebo po zavolání funkce exit) je pro každý globální objekt vyvolán destruktor. V rámci jednoho překládaného modulu jsou globální proměnné inicializovány v pořadí zápisu a destruovány v opačném pořadí. Pořadí inicializací mezi moduly není definováno XXX a( 1), b, c( 2, 3, 4); XXX d( b); // b již je inicializováno class C { static XXX h; // statická datová položka - deklarace }; XXX C::h( 1, 2, 3); // statická datová položka - definice

173 Lokální statické proměnné
Vznik a zánik objektů Lokální statické proměnné Konstruktory lokálních statických proměnných se volají v okamžiku prvního vstupu do funkce Destruktory jsou volány po výstupu z main v opačném pořadí f() { static XXX e( 1); // lokální statická proměnná } Typické použití - singleton std::ostream & log_file() { static std::ofstream x( "file.log"); // lokální statická proměnná return x;

174 Dynamicky alokované objekty
Vznik a zánik objektů Dynamicky alokované objekty Pro dynamickou alokaci slouží dvojice operátorů new a delete. Operátor new alokuje potřebnou oblast paměti pro objekt specifikovaného typu a vyvolává konstruktor podle zadaných parametrů. Vrací ukazatel na tento objekt. Pokud se z důvodu nedostatku paměti alokace nezdaří: Vyvolá se výjimka std::bad_alloc Operátor delete vyvolává destruktor objektu a poté dealokuje paměť zabranou objektem. Je odolný proti nulovým ukazatelům XXX * p, * q; p = new XXX; // konstruktor bez parametrů q = new XXX( 1, p); // XXX( int, XXX *) /* ... */ delete p; delete q;

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

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

177 Dědičnost a virtuální funkce

178 Třída Derived je odvozena z třídy Base
Dědičnost class Base { /* ... */ }; class Derived : public Base { /* ... */ } Třída Derived je odvozena z třídy Base Obsahuje všechny datové položky i metody třídy Base Může k nim doplnit další Není vhodné novými zakrývat staré, vyjma virtuálních Může změnit chování metod, které jsou v Base deklarovány jako virtuální class Base { virtual void f() { /* ... */ } }; class Derived : public Base {

179 Virtuální funkce class Base { virtual void f() { /* ... */ } virtual void f( int) { /* ... */ } virtual void g() = 0; // čistě virtuální funkce bez těla virtual void h() const { /* ... */ } void j() { /* ... */ } }; Třída obsahující čistě virtuální funkci nemůže být samostatně instanciována. class Derived : public Base { virtual void f( int) const { /* ... */ } virtual void g() { /* ... */ } virtual void h() { /* toto není nové tělo pro Base::h */ } virtual void j() { /* toto není nové tělo pro Base::j */ } Mechanismus redefinování virtuálních funkcí je vázán na jméno i parametry včetně „const“ Je možné redefinovat i tělo privátní funkce

180 V jiné situaci není virtuálnost funkcí užitečná
Virtuální funkce class Base { virtual void f() { /* ... */ } }; class Derived : public Base { Mechanismus virtuálních funkcí se uplatní pouze v přítomnosti ukazatelů nebo referencí Base * p = new Derived; p->f(); // volá Derived::f V jiné situaci není virtuálnost funkcí užitečná Derived d; d.f(); // volá Derived::f i kdyby nebyla virtuální Base b = d; // slicing = kopie části objektu b.f(); // volá Base::f ikdyž je virtuální Slicing je specifikum jazyka C++

181 Názvosloví Abstraktní třída Definice v C++: Třída obsahující alespoň jednu čistě virtuální funkci Běžná definice: Třída, která sama nebude instanciována Představuje rozhraní, které mají z ní odvozené třídy (potomci) implementovat Konkrétní třída Třída, určená k samostatné instanciaci Implementuje rozhraní, předepsané abstraktní třídou, ze které je odvozena

182 Dědičnost a destruktor
class Base { public: virtual ~Base() {} }; class Derived : public Base { virtual ~Derived() { /* ... */ } Base * p = new Derived; delete p; Pokud je objekt destruován operátorem delete aplikovaným na ukazatel na předka, musí být destruktor v tomto předku deklarován jako virtuální Odvozené pravidlo: Každá abstraktní třída má mít virtuální destruktor Je to zadarmo Může se to hodit

183 Mechanismus dědičnosti v C++ je velmi silný
Bývá používán i pro nevhodné účely Ideální použití dědičnosti je pouze toto ISA hierarchie (typicky pro objekty s vlastní identitou) Živočich-Obratlovec-Savec-Pes-Jezevčík Objekt-Viditelný-Editovatelný-Polygon-Čtverec Vztah interface-implementace Readable-InputFile Writable-OutputFile (Readable+Writable)-IOFile Jiná použití dědičnosti obvykle signalizují chybu v návrhu Výjimky samozřejmě existují (traits...)

184 Dědičnost ISA hierarchie C++: Jednoduchá nevirtuální veřejná dědičnost
class Derived : public Base Abstraktní třídy někdy obsahují datové položky Vztah interface-implementace C++: Násobná virtuální veřejná dědičnost class Derived : virtual public Base1, virtual public Base2 Abstraktní třídy obvykle neobsahují datové položky Interface nebývají využívány k destrukci objektu Oba přístupy se často kombinují class Derived : public Base, virtual public Interface1, virtual public Interface2

185 Nesprávné užití dědičnosti
class Real { public: double Re; }; class Complex : public Real { public: double Im; }; Porušuje pravidlo "každý potomek má všechny vlastnosti předka" např. pro vlastnost "má nulovou imaginární složku" Důsledek - slicing: double abs( const Real & p) { return p.Re > 0 ? p.Re : - p.Re; } Complex x; double a = abs( x); // tento kód LZE přeložit, a to je špatně Důvod: Referenci na potomka lze přiřadit do reference na předka Complex => Complex & => Real & => const Real &

186 Nesprávné užití dědičnosti
class Real { public: double Re; }; class Complex : public Real { public: double Im; }; Slicing nastává i u předávání hodnotou double abs( Real p) { return p.Re > 0 ? p.Re : - p.Re; } Complex x; double a = abs( x); // tento kód LZE přeložit, a to je špatně Důvod: Předání hodnoty x do parametru p je provedeno implicitně vytvořeným konstruktorem: Real::Real( const Real & y) { Re = y.Re; } Parametr x typu Complex do tohoto konstruktoru lze předat Complex => Complex & => Real & => const Real &

187 Nesprávné užití dědičnosti
class Complex { public: double Re, Im; }; class Real : public Complex { public: Real( double r); }; Vypadá jako korektní specializace: "každé reálné číslo má všechny vlastnosti komplexního čísla" Chyba: Objekty v C++ nejsou hodnoty v matematice Třída Complex má vlastnost "lze do mne přiřadit Complex" Tuto vlastnost třída Real logicky nemá mít, s touto dědičností ji mít bude

188 Nesprávné užití dědičnosti
class Complex { public: double Re, Im; }; class Real : public Complex { public: Real( double r); }; Vypadá jako korektní specializace: "každé reálné číslo má všechny vlastnosti komplexního čísla" Chyba: Objekty v C++ nejsou hodnoty v matematice Třída Complex má vlastnost "lze do mne přiřadit Complex" Tuto vlastnost třída Real logicky nemá mít, s touto dědičností ji mít bude void set_to_i( Complex & p) { p.Re = 0; p.Im = 1; } Real x; set_to_i( x); // tento kód LZE přeložit, a to je špatně Důvod: Referenci na potomka lze přiřadit do reference na předka Real => Real & => Complex &

189 Nesprávné užití dědičnosti
class Complex { public: double Re, Im; }; class Real : public Complex { public: Real( double r); }; Vypadá jako korektní specializace: "každé reálné číslo má všechny vlastnosti komplexního čísla" Chyba: Objekty v C++ nejsou hodnoty v matematice Třída Complex má vlastnost "lze do mne přiřadit Complex" Tuto vlastnost třída Real logicky nemá mít, s touto dědičností ji mít bude Poznámka: při přímem přiřazování tento problém nenastane Complex y; Real x; x = y; // tento kód NELZE přeložit Důvod: operátor = se nedědí Complex & Complex::operator=( const Complex &); // nezdědí se Real & Real::operator=( const Real &); // nesouhlasí typ argumentu

190 Šablony Templates

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

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

193 Šablony tříd – závislé typy
Šablony tříd (včetně těl metod) se při deklaraci kontrolují pouze syntakticky Některé překladače nedělají ani to Překladač potřebuje odlišit jména typů od ostatních jmen U jmen ve tvaru A::B to překladač někdy nedokáže Programátor musí pomoci klíčovým slovem typename template< typename T> class X { typedef typename T::B U; // T::B je typ typename U::D p; // T::B::D je typ typename Y<T>::C q; // Y<T>::C je typ void f() { T::D(); } // T::D není typ } typename je nutné uvést před jmény typu ve tvaru A::B, kde A je závislé jméno Závislé jméno je jméno obsahující přímo či nepřímo parametr šablony

194 Pokud je mezi předkem třídy závislé jméno
Šablony tříd - this Pokud je mezi předkem třídy závislé jméno překladač pak neví, které identifikátory jsou zděděny uživatel musí pomoci konstrukcí this-> template< typename T> class X : public T { void f() { return this->a; } }

195 Šablony funkcí lze volat dvěma způsoby
Šablona funkce je generická funkce (globální nebo metoda) prefixovaná konstrukcí template se stejnými druhy formálních parametrů šablony jako u šablon tříd template< typename T, int k> // parametry šablony int f( T * p, int q); // parametry funkce template< typename T, typename U> // parametry šablony int g( T * p, vector< U> q); // parametry funkce Šablony funkcí lze volat dvěma způsoby Explicitně f< int, 729>( a, b) Automaticky g( a, b) Překladač dopočte parametry šablony z typů parametrů funkce Všechny formální argumenty šablony by měly být užity v typech formálních parametrů funkce

196 Šablony funkcí Pod stejným identifikátorem může být deklarováno několik různých šablon funkce a navíc několik obyčejných funkcí. Obyčejné funkce mají přednost před generickými template< class T> T max( T a, T b) { return a < b ? b : a; }; char * max( char * a, char * b) { return strcmp( a, b) < 0 ? b : a; }; template< int n, class T> T max( Array< n, T> a) { /* ... */ } Příklad ze standardních knihoven: template< class T> void swap( T & a, T & b) { T tmp(a); a = b; b = tmp; }; K tomu řada chytřejších implementací swap pro některé třídy

197 Šablony – pokročilé konstrukce jazyka
Parciální specializace Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice template< int n> class Array< n, bool> { /* specializace pro pole typu bool */ }; Krajním případem parciální specializace je explicitní specializace Explicitní specializace template<> class Array< 32, bool> { /* ... */ }; U šablon funkcí nahrazena obyčejnou funkcí Explicitní instanciace Překladač je možné donutit ke kompletní instanciaci šablony template class Array< 128, char>;

198 Explicitní specializace
Příklad template< int N> struct Fib { enum { value = Fib< N-1>::value + Fib< N-2>::value }; }; template<> struct Fib< 0> { enum { value = 1 }; template<> struct Fib< 1> { Kontrolní otázka: Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::value

199 Teoretický pohled na šablony
Jiný příklad template< int N> struct Fib { enum { value = Fib< N-1>::value + Fib< N-2>::value }; }; template<> struct Fib< 0> { enum { value = 1 }; template<> struct Fib< 1> { Kontrolní otázka: Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::value MS Visual C++ 7.1: Build Time 0:00 Kompilátory ukládají již vytvořené instanciace a nepočítají je znovu

200 Parciální specializace
Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice Parciálně specializovat lze šablony funkcí, celé šablony tříd i jednotlivě těla jejich metod Obsah specializace šablony třídy (teoreticky) nemusí nijak souviset se základní definicí - může mít zcela jiné položky, předky apod. Základní definice dokonce nemusí vůbec existovat (ale musí být deklarována) template< class X, class Y> class C; // základní deklarace template< class P, class Q> class C< P *, Q *> { // specializace bool cmp( P *, Q *); }; template< class Z> class C< Z, Z> : public Z { // jiná specializace bool set( Z &); template< class X, class Y> class C { // základní definice X add( Y);

201 Parciální specializace
Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice Parciální specializace může mít stejný, menší i větší počet formálních parametrů než základní definice, jejich hodnoty se odvozují ze skutečných parametrů šablony (kterých je vždy tolik, kolik určuje základní definice) template< class T, class U, int n> class C< T[n], U[n]> { /* specializace pro dvě pole stejné velikosti */ };

202 Parciální specializace
Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice Parciální specializace může mít stejný, menší i větší počet formálních parametrů než základní definice, jejich hodnoty se odvozují ze skutečných parametrů šablony (kterých je vždy tolik, kolik určuje základní definice) template< class T, class U, int n> class C< T[n], U[n]> { /* specializace pro dvě pole stejné velikosti */ }; Krajním případem parciální specializace je explicitní specializace Explicitní specializace template<> class C< char, int[ 8]> { /* ... */ }; Explicitní specializace šablony není šablona Podléhá trochu jiným (jednodušším) pravidlům Překlad se neodkládá Těla metod se nepíší do hlavičkových souborů

203 Parciální specializace
Typická použití parciální a explicitní specializace Výhodnější implementace ve speciálních případech Šablona je používána přímo z nešablonovaného kódu Programátor - uživatel šablony o specializaci nemusí vědět Příklad: Implementace vector<char> může být jednodušší

204 Parciální specializace
Typická použití parciální a explicitní specializace Výhodnější implementace ve speciálních případech Šablona je používána přímo z nešablonovaného kódu Programátor - uživatel šablony o specializaci nemusí vědět Příklad: Implementace vector<char> může být jednodušší Mírná změna rozhraní ve speciálních případech Uživatel by měl být o specializaci informován Příklad: vector< bool> nedovoluje vytvořit ukazatel na jeden prvek

205 Parciální specializace
Typická použití parciální a explicitní specializace Výhodnější implementace ve speciálních případech Šablona je používána přímo z nešablonovaného kódu Programátor - uživatel šablony o specializaci nemusí vědět Příklad: Implementace vector<char> může být jednodušší Mírná změna rozhraní ve speciálních případech Uživatel by měl být o specializaci informován Příklad: vector< bool> nedovoluje vytvořit ukazatel na jeden prvek Modifikace chování jiné šablony Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde Někdy dokonce ani nedefinuje základní obsah volané šablony Autor specializace tak upravuje chování volající šablony Příklad: šablona basic_string<T> volá šablonu char_traits<T>, ve které je např. definována porovnávací funkce

206 Parciální specializace
Modifikace chování jiné šablony Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde Někdy dokonce ani nedefinuje základní obsah volané šablony Autor specializace tak upravuje chování volající šablony Příklad: šablona basic_string<T> volá šablonu char_traits<T>, ve které je např. definována porovnávací funkce template< class T> struct char_traits; template< class T> class basic_string { /* ... */ int compare( const basic_string & b) const { /*...*/ char_traits< T>::compare( /* ... */) /*...*/ } }; template<> struct char_traits< char> { /* ... */ static int compare(const char* s1, const char* s2, size_t n) { return memcmp( s1, s2, n); }

207 Parciální specializace
Modifikace chování jiné šablony Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde Někdy dokonce ani nedefinuje základní obsah volané šablony Autor specializace tak upravuje chování volající šablony Traits Šablony, ze kterých nejsou vytvářeny objekty Obsahují pouze: Definice typů Statické funkce Určeny k doplnění informací o nějakém typu Příklad: char_traits<T> doplňuje informace o typu T, např. porovnávací funkci

208 Šablony, ze kterých nejsou vytvářeny objekty Obsahují pouze:
Traits & policies Traits Šablony, ze kterých nejsou vytvářeny objekty Obsahují pouze: Definice typů Statické funkce Určeny k doplnění informací o nějakém typu Příklad: char_traits<T> doplňuje informace o typu T, např. porovnávací funkci Policy classes Třídy, ze kterých obvykle nejsou vytvářeny objekty Předávány jako parametr šablonám Defaultní hodnotou parametru často bývá šablona traits Určeny k definování určitého chování Příklad: Alokační strategie

209 Porovnání typů s booleovským výstupem
Triky s šablonami Porovnání typů s booleovským výstupem template< class A, class B> struct Equal { enum { value = false }; }; template< class A> struct Equal< A, A> { enum { value = true }; Equal< X, Y>::value je konstantní výraz Použití template< class T1> class Test { enum { T1_is_int = Equal< int, T1>::value}; enum { T1_is_long = Equal< long, T1>::value}; /* ... */

210 Porovnání typů s typovým výstupem
Triky s šablonami Porovnání typů s typovým výstupem template< class A, class B, class C, class D> struct IfEqual { typedef D Result; }; template< class A, class C, class D> struct Equal< A, A, C, D> { typedef C Result; IfEqual< X, Y, U, V>::Result je typ Význam: X == Y ? U : V Použití template< class T1> class Test { typedef Equal< T1, unsigned, unsigned long, long>::Result longT1; /* ... */

211 Kompilační ověření invariantu
Triky s šablonami Kompilační ověření invariantu template< int x> struct AssertNot; template<> struct AssertNot< 0> { enum { value = true }; }; struct Assert { enum { value = AssertNot< ! x>::value };

212 Kompilační ověření invariantu
Triky s šablonami Kompilační ověření invariantu template< int x> struct AssertNot; template<> struct AssertNot< 0> { enum { value = true }; }; struct Assert { enum { value = AssertNot< ! x>::value }; Použití template< int N> class Array { enum { check = Assert< (N > 0)>::value }; /* ... */ Array< -3> x; error C2027: use of undefined type 'AssertNot<x>' with [x=1]

213 Teoretický pohled na šablony
Příklad template< int N> struct Fact { enum { value = Fact< N-1>::value * N }; }; template<> struct Fact< 0> { enum { value = 1 };

214 Teoretický pohled na šablony
Příklad template< int N> struct Fact { enum { value = Fact< N-1>::value * N }; }; template<> struct Fact< 0> { enum { value = 1 }; Použití enum { N = 10 }; int permutations[ Fact< N>::value];

215 Teoretický pohled na šablony
Příklad template< int N> struct Fact { enum { value = Fact< N-1>::value * N }; }; template<> struct Fact< 0> { enum { value = 1 }; Kontrolní otázka: Kolik je Fact< -1>::value

216 Teoretický pohled na šablony
Příklad template< int N> struct Fact { enum { value = Fact< N-1>::value * N }; }; template<> struct Fact< 0> { enum { value = 1 }; Kontrolní otázka: Kolik je Fact< -1>::value MS Visual C++ 7.1: fatal error C1202: recursive type or function dependency context too complex Řetěz instanciací Fact< -1>, Fact< -2>, Fact< -3>, ... způsobí přetečení tabulek kompilátoru

217 Šablony tříd – pravidla použití
Uvnitř těla šablony (nebo jako její předky) je možno užívat libovolné typy včetně: Instancí jiných šablon Téže šablony s jinými argumenty Téže šablony se stejnými argumenty V tomto případě se argumenty mohou, ale nemusí opisovat Ekvivalentní varianty šablony s copy-constructorem: template< typename T> class X { X( const X< T> &); }; X( const X &); Některé překladače připouštějí i tuto variantu X< T>( const X< T> &);

218 Šablony tříd – pravidla použití
Metody šablon mohou mít těla uvnitř třídy nebo vně Vně uvedená těla metod musejí být připojena k šabloně takto: template< typename T> void X< T>::f( int a, int b) { /* ... */ } V kvalifikovaném jméně metody je nutné uvést patřičný seznam argumentů, tj. X< T>::f a nikoliv X::f Těla metod musejí být viditelná z každého místa, kde jsou pro nějakou instanci šablony volána Musejí tedy typicky být v témže hlavičkovém souboru jako sama šablona. Uvedení těla metody vně třídy tedy u šablon typicky nic nepřináší, může být však vynuceno rekurzivními odkazy mezi šablonami apod.

219 Šablony tříd – pravidla použití
Šablona třídy se překládá až v okamžiku instanciace, tj. použití s konkrétními parametry Překladač instanciuje (tj. překládá) pouze ty metody, které jsou zapotřebí (tj. jsou volány nebo jsou virtuální) Některá těla metod tedy nemusí být pro některé případy parametrů přeložitelná template< typename Container> class Proxy { public: void pop_front() { c->pop_front(); } // jen pro list/deque /* ... */ private: Container * c; }; Explicitní instanciace Překladač je možné donutit ke kompletní instanciaci šablony template class Array< 128, char>;

220 Dopředná deklarace šablony
Šablony tříd – triky Dopředná deklarace šablony template< typename T> class X; /* ... zde může být použito X<U> s jakýmikoliv argumenty U... ... pouze v kontextech, kde kompilátor nepotřebuje znát tělo šablony ... */ template< typename T> class X { /* ... */ };

221 Různé druhy přetypování
Cast Různé druhy přetypování

222 Různé varianty syntaxe C-style cast
Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C

223 Různé varianty syntaxe C-style cast
Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C Function-style cast T(e) Ekvivalentní (T)e T musí být syntakticky identifikátor nebo klíčové slovo označující typ

224 Různé varianty syntaxe C-style cast
Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C Function-style cast T(e) Ekvivalentní (T)e T musí být syntakticky identifikátor nebo klíčové slovo označující typ Type conversion operators Pro odlišení účelu (síly a nebezpečnosti) přetypování: const_cast<T>(e) static_cast<T>(e) reinterpret_cast<T>(e)

225 Různé varianty syntaxe C-style cast
Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C Function-style cast T(e) Ekvivalentní (T)e T musí být syntakticky identifikátor nebo klíčové slovo označující typ Type conversion operators Pro odlišení účelu (síly a nebezpečnosti) přetypování: const_cast<T>(e) static_cast<T>(e) reinterpret_cast<T>(e) Novinka - přetypování s běhovou kontrolou: dynamic_cast<T>(e)

226 Odstranění konstantnosti
Přetypování const_cast<T>(e) Odstranění konstantnosti const U & => U & const U * => U * Obvykle používáno pro měnění pomocných položek v logicky konstantních objektech Příklad: Čítač odkazů na logicky konstantní objekt class Data { public: void register_pointer() const { const_cast< Data *>( this)->references++; } private: /* ... data ... */ int references; }; Jiný příklad: datové struktury s amortizovaným vyhledáváním

227 Odstranění konstantnosti
Přetypování const_cast<T>(e) Odstranění konstantnosti const U & => U & const U * => U * U moderních překladačů lze nahradit specifikátorem mutable Příklad: Čítač odkazů na logicky konstantní objekt class Data { public: void register_pointer() const { references++; } private: /* ... data ... */ mutable int references; };

228 Všechny implicitní konverze
Přetypování static_cast<T>(e) Umožňuje Všechny implicitní konverze Bezztrátové i ztrátové aritmetické konverze (int <=> double apod.) Konverze přidávající modifikátory const a volatile Konverze ukazatele na void * Konverze odkazu na odvozenou třídu na odkaz na předka: Derived & => Base & Derived * => Base * Aplikace copy-constructoru; v kombinaci s implicitní konverzí též: Derived => Base (slicing: okopírování části objektu) Aplikace libovolného konstruktoru T::T s jedním parametrem Uživatelská konverze libovolného typu na třídu T Aplikace konverzního operátoru: operator T() Uživatelská konverze nějaké třídy na libovolný typ T

229 Všechny implicitní konverze
Přetypování static_cast<T>(e) Umožňuje Všechny implicitní konverze Ekvivalentní použití pomocné proměnné tmp deklarované takto: T tmp(e); Používá se tehdy, pokud se vynucením jedné z možných implicitních konverzí odstraní nejednoznačnost nebo vynutí volání jiné varianty funkce class A { /* ... */ }; class B { /* ... */ }; void f( A *); void f( B*); class C : public A, public B { /* ... */ void g() { f( static_cast< A>( this)); } };

230 Všechny implicitní konverze Konverze na void - zahození hodnoty výrazu
Přetypování static_cast<T>(e) Umožňuje Všechny implicitní konverze Konverze na void - zahození hodnoty výrazu Používá se v makrech a podmíněných výrazech Konverze odkazu na předka na odkaz na odvozenou třídu Base & => Derived & Base * => Derived * Pokud objekt, na nějž konvertovaný odkaz ukazuje, není typu Derived či z něj odvozený, je výsledek nedefinovaný K chybě obvykle dojde později! Konverze celého čísla na výčtový typ Pokud hodnota čísla neodpovídá žádné výčtové konstantě, výsledek je nedefinovaný Konverze void * na libovolný ukazatel

231 Konverze odkazu na předka na odkaz na odvozenou třídu
Přetypování static_cast<T>(e) Nejčastější použití Konverze odkazu na předka na odkaz na odvozenou třídu class Base { public: enum Type { T_X, T_Y }; virtual Type get_type() const = 0; }; class X : public Base { /* ... */ virtual Type get_type() const { return T_X; } class Y : public Base { /* ... */ virtual Type get_type() const { return T_Y; } Base * p = /* ... */; switch ( p->get_type() ) { case T_X: { X * xp = static_cast< X *>( p); /* ... */ } break; case T_Y: { Y * yp = static_cast< Y *>( p); /* ... */ } break; }

232 Konverze ukazatele na dostatečně velké celé číslo
Přetypování reinterpret_cast<T>(e) Umožňuje Konverze ukazatele na dostatečně velké celé číslo Konverze celého čísla na ukazatel Konverze mezi různými ukazateli na funkce Konverze odkazu na odkaz na libovolný jiný typ U * => V * U & => U & Neuvažuje příbuzenské vztahy tříd, neopravuje hodnoty ukazatelů Většina použití je závislá na platformě Příklad: Přístup k reálné proměnné po bajtech Typické použití: Čtení a zápis binárních souborů void put_double( std::ostream & o, const double & d) { o.write( reinterpret_cast< char *>( & d), sizeof( double)); } Obsah souboru je nepřenositelný

233 Konverze odkazu na odvozenou třídu na odkaz na předka:
Přetypování dynamic_cast<T>(e) Umožňuje Konverze odkazu na odvozenou třídu na odkaz na předka: Derived & => Base & Derived * => Base * Implicitní konverze, chová se stejně jako static_cast Konverze odkazu na předka na odkaz na odvozenou třídu Base & => Derived & Base * => Derived * Podmínka: Base musí obsahovat alespoň jednu virtuální funkci Pokud konvertovaný odkaz neodkazuje na objekt typu Derived nebo z něj odvozený, je chování definováno takto: Konverze ukazatelů vrací nulový ukazatel Konverze referencí vyvolává výjimku std::bad_cast Umožňuje přetypování i v případě virtuální dědičnosti

234 Konverze odkazu na předka na odkaz na odvozenou třídu
Přetypování dynamic_cast<T>(e) Nejčastější použití Konverze odkazu na předka na odkaz na odvozenou třídu class Base { public: virtual ~Base(); /* alespoň jedna virtuální funkce */ }; class X : public Base { /* ... */ class Y : public Base { /* ... */ Base * p = /* ... */; X * xp = dynamic_cast< X *>( p); if ( xp ) { /* ... */ } Y * yp = dynamic_cast< Y *>( p); if ( yp ) { /* ... */ }

235 Exception handling Mechanismus výjimek

236 Exception handling Srovnání: goto Start: příkaz goto Cíl: návěští
Určen při kompilaci Skok může opustit blok Proměnné korektně zaniknou voláním destruktorů Cíl musí být v téže proceduře int f() { if ( something == wrong ) goto label; } else MyClass my_variable; if ( anything != good ) /* ... */ return 0; label: std::cerr << "Error" << std::endl; return -1;

237 Exception handling Srovnání: goto Start: příkaz goto Cíl: návěští
Určen při kompilaci Skok může opustit blok Proměnné korektně zaniknou voláním destruktorů Cíl musí být v téže proceduře Srovnání 2: <csetjmp> Pro pokročilé Start: volání longjmp Cíl: volání setjmp Skok může opustit proceduru Neřeší lokální proměnné Nelze použít v C++ Předává hodnotu typu int int f() { if ( something == wrong ) goto label; } else MyClass my_variable; if ( anything != good ) /* ... */ return 0; label: std::cerr << "Error" << std::endl; return -1;

238 Exception handling Mechanismus výjimek Start: příkaz throw
Cíl: try-catch blok Určen za běhu Skok může opustit proceduru Proměnné korektně zaniknou voláním destruktorů Předává hodnotu libovolného typu Typ hodnoty se podílí na určení cíle skoku void f() { if ( something == wrong ) throw 729; else MyClass my_variable; if ( anything != good ) throw 123; /* ... */ } void g() try { f(); catch ( int e ) { std::cerr << "Exception in f(): " << e << std::endl;

239 Exception handling Mechanismus výjimek Start: příkaz throw
Cíl: try-catch blok Určen za běhu Skok může opustit proceduru Proměnné korektně zaniknou voláním destruktorů Předává hodnotu libovolného typu Typ hodnoty se podílí na určení cíle skoku Obvykle se používají pro tento účel zhotovené třídy class WrongException { /*...*/ }; class BadException { /*...*/ }; void f() { if ( something == wrong ) throw WrongException( something); if ( anything != good ) throw BadException( anything); } void g() try { f(); catch ( const WrongException & e1 ) { /*...*/ catch ( const BadException & e2 ) {

240 Exception handling Mechanismus výjimek Start: příkaz throw
Cíl: try-catch blok Určen za běhu Skok může opustit proceduru Proměnné korektně zaniknou voláním destruktorů Předává hodnotu libovolného typu Typ hodnoty se podílí na určení cíle skoku Obvykle se používají pro tento účel zhotovené třídy Mechanismus výjimek respektuje hierarchii dědičnosti class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException void f() { if ( something == wrong ) throw WrongException( something); if ( anything != good ) throw BadException( anything); } void g() try { f(); catch ( const AnyException & e1 ) { /*...*/

241 Exception handling Mechanismus výjimek Start: příkaz throw
Cíl: try-catch blok Určen za běhu Skok může opustit proceduru Proměnné korektně zaniknou voláním destruktorů Předává hodnotu libovolného typu Typ hodnoty se podílí na určení cíle skoku Obvykle se používají pro tento účel zhotovené třídy Mechanismus výjimek respektuje hierarchii dědičnosti Hodnotu není třeba využívat class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException void f() { if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException(); } void g() try { f(); catch ( const AnyException &) { /*...*/

242 Exception handling Mechanismus výjimek Start: příkaz throw
Cíl: try-catch blok Určen za běhu Skok může opustit proceduru Proměnné korektně zaniknou voláním destruktorů Předává hodnotu libovolného typu Typ hodnoty se podílí na určení cíle skoku Obvykle se používají pro tento účel zhotovené třídy Mechanismus výjimek respektuje hierarchii dědičnosti Hodnotu není třeba využívat Existuje univerzální catch blok class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException void f() { if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException(); } void g() try { f(); catch (...) { /*...*/

243 Fáze zpracování výjimky Vyhodnocení výrazu v příkaze throw
Exception handling Fáze zpracování výjimky Vyhodnocení výrazu v příkaze throw Hodnota je uložena "stranou" Stack-unwinding Postupně se opouštějí bloky a funkce, ve kterých bylo provádění vnořeno Na zanikající lokální a pomocné proměnné jsou volány destruktory Stack-unwinding končí dosažením try-bloku, za kterým je catch-blok odpovídající typu výrazu v příkaze throw Provedení kódu v catch-bloku Původní hodnota throw je stále uložena pro případné pokračování: Příkaz throw bez výrazu pokračuje ve zpracování téže výjimky počínaje dalším catch-blokem - začíná znovu stack-unwinding Zpracování definitivně končí opuštěním catch-bloku Běžným způsobem nebo příkazy return, break, continue, goto Nebo vyvoláním jiné výjimky

244 Použití mechanismu výjimek
Exception handling Použití mechanismu výjimek Vyvolání a zpracování výjimky je relativně časově náročné Používat pouze pro chybové nebo řídké stavy Např. nedostatek paměti, ztráta spojení, chybný vstup, konec souboru Připravenost na výjimky také něco (málo) stojí Za normálního běhu je třeba zařídit, aby výjimka dokázala najít cíl a zrušit proměnné Výjimky se týkají i procedur, ve kterých není ani throw, ani try-blok Většina kompilátorů umí překládat ve dvou režimech "s" a "bez" Celý spojovaný program musí být přeložen stejně

245 Všechny standardní výjimky jsou potomky třídy exception
Exception handling Standardní výjimky <stdexcept> Všechny standardní výjimky jsou potomky třídy exception metoda what() vrací řetězec s chybovým hlášením bad_alloc: vyvolává operátor new při nedostatku paměti V režimu "bez výjimek" new vrací nulový ukazatel bad_cast, bad_typeid: Chybné použití RTTI Odvozené z třídy logic_error: domain_error, invalid_argument, length_error, out_of_range vyvolávány např. funkcí vector::operator[] Odvozené z třídy runtime_error: range_error, overflow_error, underflow_error

246 Všechny standardní výjimky jsou potomky třídy exception
Exception handling Standardní výjimky <stdexcept> Všechny standardní výjimky jsou potomky třídy exception metoda what() vrací řetězec s chybovým hlášením bad_alloc: vyvolává operátor new při nedostatku paměti V režimu "bez výjimek" new vrací nulový ukazatel bad_cast, bad_typeid: Chybné použití RTTI Odvozené z třídy logic_error: domain_error, invalid_argument, length_error, out_of_range vyvolávány např. funkcí vector::operator[] Odvozené z třídy runtime_error: range_error, overflow_error, underflow_error Aritmetické ani ukazatelové operátory na vestavěných typech NEHLÁSÍ běhové chyby prostřednictvím výjimek např. dělení nulou nebo dereference nulového ukazatele

247 Exception handling Typické použití Deklarace výjimek Vyvolání výjimek
class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException Vyvolání výjimek void f() { if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException(); } Částečné ošetření void g() { try { f(); } catch (...) { std::cout << "Exception in g()"; throw; Podrobné ošetření int main( /*...*/) g(); h(); catch ( WrongException ) { std::cout << "WrongException"; catch ( BadException ) { std::cout << "BadException";

248 Exception handling Použití se std::exception Deklarace výjimek
class WrongException : public std::exception { virtual const char * what() const { return "WrongException"; } }; class BadException { return "BadException"; } Vyvolání výjimek void f() { if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException(); } Částečné ošetření void g() { try { f(); } catch (...) { std::cout << "Exception in g()"; throw; Podrobné ošetření int main( /*...*/) g(); h(); catch ( const std::exception & e ) { std::cout << e.what();

249 Exception-safe programming
Používat throw a catch je jednoduché Těžší je programovat běžný kód tak, aby se choval korektně i za přítomnosti výjimek Exception-safety Exception-safe programming void f() { int * a = new int[ 100]; int * b = new int[ 200]; g( a, b); delete[] b; delete[] a; } Pokud new int[ 200] způsobí výjimku, procedura zanechá naalokovaný nedostupný blok Pokud výjimku vyvolá procedura g, zůstanou dva nedostupné bloky

250 Exception-safe programming
Používat throw a catch je jednoduché Těžší je programovat běžný kód tak, aby se choval korektně i za přítomnosti výjimek Exception-safety Exception-safe programming T & operator=( const T & b) { if ( this != & b ) delete body_; body_ = new TBody( b.length()); copy( body_, b.body_); } return * this; Pokud new TBody způsobí výjimku, operátor= zanechá v položce body_ původní ukazatel, který již míří na dealokovaný blok Pokud výjimku vyvolá procedura copy, operátor zanechá třídu v neúplném stavu

251 Exception-safe programming
Pravidla vynucená jazykem Destruktor nesmí skončit vyvoláním výjimky Výjimka může být vyvolána uvnitř, ale musí být zachycena nejpozději uvnitř destruktoru Zdůvodnění: V rámci ošetření výjimek (ve fázi stack-unwinding) se volají destruktory lokálních proměnných Výjimku zde vyvolanou nelze z technických i logických důvodů ošetřit (ztratila by se původní výjimka) Nastane-li taková výjimka, volá se funkce terminate() a program končí

252 Exception-safe programming
Pravidla vynucená jazykem Destruktor nesmí skončit vyvoláním výjimky Výjimka může být vyvolána uvnitř, ale musí být zachycena nejpozději uvnitř destruktoru Toto pravidlo jazyka sice platí pouze pro destruktory lokálních proměnných A z jiných důvodů též pro globální proměnné Je však vhodné je dodržovat vždy Bezpečnostní zdůvodnění: Destruktory lokálních proměnných často volají jiné destruktory Logické zdůvodnění: Nesmrtelné objekty nechceme

253 Exception-safe programming
Pravidla vynucená jazykem Destruktor nesmí skončit vyvoláním výjimky Konstruktor globálního objektu nesmí skončit vyvoláním výjimky Zdůvodnění: Není místo, kde ji zachytit Stane-li se to, volá se terminate() a program končí Jiné konstruktory ale výjimky volat mohou (a bývá to vhodné)

254 Exception-safe programming
Pravidla vynucená jazykem Destruktor nesmí skončit vyvoláním výjimky Konstruktor globálního objektu nesmí skončit vyvoláním výjimky Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky Zdůvodnění: Catch blok by nebylo možné vyvolat Stane-li se to, volá se terminate() a program končí Jiné copy-constructory ale výjimky volat mohou (a bývá to vhodné)

255 Exception-safe programming
Pravidla vynucená jazykem Destruktor nesmí skončit vyvoláním výjimky Konstruktor globálního objektu nesmí skončit vyvoláním výjimky Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky

256 Exception-safe programming
Poznámka: Výjimky při zpracování výjimky Výjimka při výpočtu výrazu v throw příkaze Tento throw příkaz nebude vyvolán Výjimka v destruktoru při stack-unwinding Povolena, pokud neopustí destruktor Po zachycení a normálním ukončení destruktoru se pokračuje v původní výjimce Výjimka uvnitř catch-bloku Pokud je zachycena uvnitř, ošetření původní výjimky může dále pokračovat (přikazem throw bez výrazu) Pokud není zachycena, namísto původní výjimky se pokračuje ošetřováním nové

257 Exception-safe programming
Kompilátory samy ošetřují některé výjimky Dynamická alokace polí Dojde-li k výjimce v konstruktoru některého prvku, úspěšně zkonstruované prvky budou destruovány Ve zpracování výjimky se poté pokračuje

258 Exception-safe programming
Kompilátory samy ošetřují některé výjimky Dynamická alokace polí Dojde-li k výjimce v konstruktoru některého prvku, úspěšně zkonstruované prvky budou destruovány Ve zpracování výjimky se poté pokračuje Výjimka v konstruktoru součásti (prvku nebo předka) třídy Sousední, již zkonstruované součásti, budou destruovány Uvnitř konstruktoru je možno výjimku zachytit speciálním try-blokem: X::X( /* formální parametry */) try : Y( /* parametry pro konstruktor součásti Y */) { /* vlastní tělo konstruktoru */ } catch ( /* parametr catch-bloku */ ) { /* ošetření výjimky v konstruktoru Y i ve vlastním těle */ } Konstrukci objektu nelze dokončit Opuštění speciálního catch bloku znamená throw;

259 Exception-safe programming
Definice (Weak) exception safety Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v případě výjimky zanechá veškerá data v konzistentním stavu Konzistentní stav znamená zejména: Nedostupná data byla korektně destruována a odalokována Ukazatele nemíří na odalokovaná data Platí další invarianty dané logikou aplikace

260 Exception-safe programming
Definice (Weak) exception safety Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v případě výjimky zanechá veškerá data v konzistentním stavu Konzistentní stav znamená zejména: Nedostupná data byla korektně destruována a odalokována Ukazatele nemíří na odalokovaná data Platí další invarianty dané logikou aplikace Strong exception safety Funkce je silně bezpečná, pokud v případě, že skončí vyvoláním výjimky, zanechá data ve stejném stavu, ve kterém byla při jejím vyvolání Nazýváno též "Commit-or-rollback semantics"

261 Exception-safe programming
Poznámky (Weak) exception safety Tohoto stupně bezpečnosti lze většinou dosáhnout Stačí vhodně definovat nějaký konzistentní stav, kterého lze vždy dosáhnout, a ošetřit pomocí něj všechny výjimky Konzistentním stavem může být třeba nulovost všech položek Je nutné upravit všechny funkce tak, aby je tento konzistentní stav nepřekvapil (mohou na něj ale reagovat výjimkou) Strong exception safety Silné bezpečnosti nemusí jít vůbec dosáhnout, pokud je rozhraní funkce navrženo špatně Obvykle jsou problémy s funkcemi s dvojím efektem Příklad: funkce pop vracející odebranou hodnotu

262 Exception-safe programming
Příklad: String č. 2 operator= Nebezpečná implementace: Pokud new char způsobí výjimku, operátor= zanechá v položce str_ původní ukazatel, který již míří na dealokovaný blok class String { public: // ...  private: char * str_; }; String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } return * this;

263 Exception-safe programming
Příklad: String č. 2 operator= Nebezpečná implementace: Pokud new char způsobí výjimku, operátor= zanechá v položce str_ původní ukazatel, který již míří na dealokovaný blok K jiné výjimce zde dojít nemůže: std::operator delete výjimky nikdy nevyvolává char je vestavěný typ a nemá tedy konstruktory které by mohly výjimku vyvolávat strlen a strcpy jsou C-funkce Parametry a návratová hodnota se předávají odkazem class String { public: // ...  private: char * str_; }; String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } return * this;

264 Exception-safe programming
Příklad: String č. 2 operator= Naivní pokus o opravu: Pokud new char způsobí výjimku, ošetří se Objekt se uvede do konzistentního stavu Výjimka se propaguje dál - ven z funkce Problém: V catch bloku teoreticky může vzniknout nová výjimka String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; try { str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } catch ( ... ) str_ = new char[ 1]; * str_ = 0; throw; return * this;

265 Exception-safe programming
Příklad: String č. 2 operator= Lepší pokus o opravu: Pokud new char způsobí výjimku, ošetří se Je nutné pozměnit invariant třídy String: Položka str_ nyní smí obsahovat nulový ukazatel String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; try { str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } catch ( ... ) str_ = 0; throw; return * this;

266 Exception-safe programming
Příklad: String č. 2 operator= Lepší pokus o opravu: Pokud new char způsobí výjimku, ošetří se Je nutné pozměnit invariant třídy String: Položka str_ nyní smí obsahovat nulový ukazatel Takový exemplář String je považován za konzistentní Konzistentnost nemusí znamenat, že to je z uživatelského pohledu platná hodnota Může být považována i za chybovou a každá operace s takovou hodnotou může vyvolávat výjimku String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; try { str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } catch ( ... ) str_ = 0; throw; return * this;

267 Exception-safe programming
Příklad: String č. 2 operator= Ekvivalentní řešení: Nulovat str_ po delete Pokud new způsobí výjimku, v str_ zůstane nulový ukazatel String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; str_ = 0; str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } return * this;

268 Exception-safe programming
Příklad: String č. 2 operator= Chyba: změnili jsme invariant str_ nyní může být nulové delete _str je v pořádku operator delete je vždy proti nulovému ukazateli ošetřen (nedělá nic) strlen a strcpy ale fungovat nebudou String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; str_ = 0; str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } return * this;

269 Exception-safe programming
Příklad: String č. 2 operator= Opraveno String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; str_ = 0; if ( b.str_ ) str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } return * this;

270 Exception-safe programming
Příklad: String č. 2 operator= Vylepšení: operator= může vyvolávat výjimku, pokud se přiřazuje neplatná hodnota Tato výjimka může být definována např. takto: #include <exception> class InvalidString : public std::exception { virtual const char * what() const { return "Invalid string"; } String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; str_ = 0; if ( b.str_ ) str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } else throw InvalidString(); return * this;

271 Exception-safe programming
Příklad: String č. 2 operator= Toto řešení je slabě bezpečné Silně bezpečné ale není: Pokud dojde k výjimce, nezachovává se původní stav dat To bude pro uživatele nepříjemné: String x, y; /* ... */ try { x = y + x; } catch (...) { /* ... */ Uživatel nedokáže rozlišit mezi výjimkami v operátorech + a = Náš operator= ale v případě výjimky ztratí hodnotu x String & String::operator=( const String & b) { if ( this != & b ) delete[] str_; str_ = 0; if ( b.str_ ) str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } else throw InvalidString(); return * this;

272 Exception-safe programming
Příklad: String č. 2 operator= Silně bezpečné řešení Pokud dojde k výjimce v new, nestane se nic Ani před throw nenastane žádná změna String & String::operator=( const String & b) { if ( this != & b ) if ( b.str_ ) char * aux = new char[ strlen( b.str_) + 1]; strcpy( aux, b.str_); delete[] str_; str_ = aux; } else throw InvalidString(); return * this;

273 Exception-safe programming
Příklad: String č. 2 operator= Silně bezpečné řešení Pozorování: Toto řešení je "shodou okolností" imunní proti this == & b String & String::operator=( const String & b) { if ( this != & b ) if ( b.str_ ) char * aux = new char[ strlen( b.str_) + 1]; strcpy( aux, b.str_); delete[] str_; str_ = aux; } else throw InvalidString(); return * this;

274 Exception-safe programming
Příklad: String č. 2 operator= Silně bezpečné řešení Pozorování: Toto řešení je "shodou okolností" imunní proti this == & b Test je možno zrušit String & String::operator=( const String & b) { if ( b.str_ ) char * aux = new char[ strlen( b.str_) + 1]; strcpy( aux, b.str_); delete[] str_; str_ = aux; } else throw InvalidString(); return * this;

275 Exception-safe programming
Příklad: String č. 2 operator= Silně bezpečné řešení Pokud je copy-constructor silně bezpečný Standardní řešení: Copy-constructor naplní lokální proměnnou c kopií parametru b Zde může dojít k výjimce Metoda swap vyměňuje obsah this a proměnné c Metoda swap je rychlá a nevyvolává výjimky Před návratem z operatoru se volá destruktor c Tím zaniká původní obsah this void String::swap( String & x) { char * aux = str_; str_ = x.str_; x.str_ = aux; } String & String::operator=( const String & b) String c( b); swap( c); return * this;

276 Exception-safe programming
Příklad: String č. 2 operator= Silně bezpečné řešení Metodu swap je vhodné publikovat ve formě globální funkce Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám void String::swap( String & x) { char * aux = str_; str_ = x.str_; x.str_ = aux; } String & String::operator=( const String & b) String c( b); swap( c); return * this; void swap( String & x, String & y) x.swap( y);

277 Exception-safe programming
Příklad: String č. 2 operator= Silně bezpečné řešení Metodu swap je vhodné publikovat ve formě globální funkce Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám Sama metoda swap může využívat šablonu swap pro typ char * #include <algorithm> void String::swap( String & x) { swap( str_, x.str_); } String & String::operator=( const String & b) String c( b); swap( c); return * this; void swap( String & x, String & y) x.swap( y);

278 Exception-safe programming
Příklad: String č. 2 copy-constructor Silně bezpečné řešení Pokud tělo dorazí na konec, budou datové položky korektně vyplněny Tělo může vyvolávat výjimky V takovém případě není třeba datové položky vyplňovat Objekt nebude považován za platný a nebude používán ani destruován Obecně je však třeba ošetřit try-blokem situace, kdy je v objektu více dynamicky alokovaných ukazatelů String( const String & b) { if ( b.str_ ) str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } else throw InvalidString();

279 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Slabě bezpečná implementace: Při výjimce v konstruktoru proměnné s se nestane nic operator delete nezpůsobuje výjimky struct Box { String v; Box * next; }; class StringStack { public: // ...  private: Box * top_; }; String StringStack::pop() { if ( ! top_ ) throw StackEmpty(); Box * p = top_; String s = p->v; top_ = p->next; delete p; return s; }

280 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Slabě bezpečná implementace Není silně bezpečná: Funkce vrací hodnotou Pokud při vracení dojde k výjimce v copy-constructoru, zásobník již bude zkrácen struct Box { String v; Box * next; }; class StringStack { public: // ...  private: Box * top_; }; String StringStack::pop() { if ( ! top_ ) throw StackEmpty(); Box * p = top_; String s = p->v; top_ = p->next; delete p; return s; }

281 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Slabě bezpečná implementace Není silně bezpečná: Funkce vrací hodnotou Pokud při vracení dojde k výjimce v copy-constructoru, zásobník již bude zkrácen Tuto výjimku lze ošetřit try-blokem okolo příkazu return Uvést zásobník do původního stavu Ale: co když se uvedení do původního stavu nezdaří? String StringStack::pop() { if ( ! top_ ) throw StackEmpty(); Box * p = top_; String s = p->v; top_ = p->next; delete p; try { return s; } catch ( ...) p = new Box; p->v = s; p->next = top_; top_ = p; throw;

282 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Nefunkční implementace Není silně bezpečná: Funkce vrací hodnotou Pokud při vracení dojde k výjimce v copy-constructoru, zásobník již bude zkrácen Tuto výjimku lze ošetřit try-blokem okolo příkazu return Dokážeme udělat obnovení původního stavu bez nebezpečí výjimky Ale: jak zrušíme proměnnou p, když k výjimce nedojde? String StringStack::pop() { if ( ! top_ ) throw StackEmpty(); Box * p = top_; String s = p->v; top_ = p->next; // tady bylo delete p; try { return s; // tady by delete p; nepomohlo } catch ( ...) top_ = p; throw;

283 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Silně bezpečná implementace Jak zrušíme proměnnou p, když k výjimce nedojde? std::auto_ptr< T> "chytrý" ukazatel na T, který se chová jako "jediný vlastník objektu": po zkopírování se vynuluje při zániku volá delete Pozor: auto_ptr má nestandardní copy-constructor a operator= modifikují svůj parametr pro auto_ptr nefungují kontejnery apod. #include <memory> String StringStack::pop() { if ( ! top_ ) throw StackEmpty(); std::auto_ptr< Box> p = top_; top_ = p->next; try { return p->v; } catch ( ...) top_ = p; // toto přiřazení nuluje p throw; // při návratu se automaticky zruší * p // pokud je p nenulové

284 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Silně bezpečná implementace Uživatel ji nedokáže použít tak, aby to bylo silně bezpečné Vracenou hodnotu je nutné okopírovat Nedá se poznat, zda výjimku vyvolala metoda pop nebo operator= V prvním případě je zásobník nedotčen, ale ve druhém je již zkrácen StringStack stk; String a; /* ... */ try { a = stk.pop(); } catch (...) { /* ??? */

285 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Řešení A Jako v STL Rozdělit pop na dvě funkce top vrací vrchol zásobníku může jej vracet odkazem nemodifikuje data pop pouze zkracuje je silně bezpečná StringStack stk; String a; /* ... */ try { a = stk.top(); } catch (...) { /* chyba kopírování nebo prázdný zásobník, proměnná a nezměněna, zásobník nedotčen */ stk.pop(); /* chyba zkracování, proměnná a změněna,

286 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Řešení B Namísto vracení hodnoty funkce pop vyplňuje parametr předávaný odkazem tím se vyloučí nutnost kombinovat volání pop s dalším kopírováním Pro uživatele jednodušší, implementace pop je však těžší StringStack stk; String a; /* ... */ try { stk.pop( a); } catch (...) { /* chyba zkracování nebo kopírování, proměnná a nezměněna, zásobník nedotčen */

287 Exception-safe programming
Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem Řešení B Lze implementovat nad řešením A #include <memory> class StringStack { public: /* A */ String & top(); void pop(); /* B */ void pop( String & out) { String & t = top(); swap( out, t); try { pop(); } catch (...) { throw; };

288 Exception specifications
U každé funkce (operátoru, metody) je možno určit seznam výjimek, kterými smí být ukončena Na výjimky ošetřené uvnitř funkce se specifikace nevztahuje Pokud není specifikace uvedena, povoleny jsou všechny výjimky Specifikace respektuje dědičnost, to jest automaticky povoluje i všechny potomky uvedené třídy void a() { /* tahle smí všechno */ } void b() throw () /* tahle nesmí nic */ void c() throw ( std::bad_alloc) /* tahle smí std::bad_alloc */ void d() throw ( std::exception, MyExc) /* tahle smí potomky std::exception a MyExc */

289 Exception specifications
Kompilátor zajistí, že nepovolená výjimka neopustí funkci: Pokud by se tak mělo stát, volá se unexpected() unexpected() smí vyvolat "náhradní" výjimku Pokud ani náhradní výjimka není povolena, zkusí se vyvolat std::bad_exception Pokud ani std::bad_exception není povoleno, volá se terminate() a program končí

290 Exception specifications
Kompilátor zajistí, že nepovolená výjimka neopustí funkci Toto je běhová kontrola Kompilátor smí vydávat nejvýše varování Funkce smí volat jinou, která by mohla vyvolat nepovolenou výjimku (ale nemusí) void f() throw ( std::exception) { } void g() throw () f(); /* tohle se smí */

291 Exception specifications
Kompilátor (a runtime) zajistí, že nepovolená výjimka neopustí funkci Microsoft Visual C to ovšem neimplementuje Kompilátor to může využít Speciálně při volání funkce s prázdným throw () se nemusí generovat ošetřující kód Program se zmenší a možná i zrychlí Užitek pro programátory: Komentář Ladicí prostředek

292 Přetěžování funkcí

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

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

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

296 Koenig lookup

297 iostream Problém: namespace namespace prostor {
class Souradnice { public: int x, y; }; std::ostream & operator<<( std::ostream & s, const Souradnice & a) { return s << '[' << a.x << ',' << a.y << ']'; } }; prostor::Souradnice p; std::cout << p; // správný operator<< je v namespace prostor, // který není přímo vidět

298 iostream Problém: namespace namespace prostor {
class Souradnice { public: int x, y; }; std::ostream & operator<<( std::ostream & s, const Souradnice & a) { return s << '[' << a.x << ',' << a.y << ']'; } }; prostor::Souradnice p; std::cout << p; // správný operator<< je v namespace prostor, // který není přímo vidět std::cout << std::endl; // tentýž problém je ale už tady: // tento operator<< je v namespace std

299 Oba případy jsou překládány správně
Koenig lookup prostor::Souradnice p; std::cout << p; // správný operator<< je v namespace prostor, // který není přímo vidět std::cout << std::endl; // tentýž problém je ale už tady: // tento operator<< je v namespace std Oba případy jsou překládány správně Je k tomu nutná složitá definice vyhledávání identifikátoru tzv. Koenigovo vyhledávání používá se, je-li význam identifikátoru závislý na parametrech volání funkce použití operátoru

300 Koenigovo vyhledávání (zjednodušeno)
Koenig lookup Koenigovo vyhledávání (zjednodušeno) Argument-dependent name lookup (ISO C++) Pro každý skutečný parametr se z jeho typu T určí množina asociovaných namespace Je-li T číselný, tyto množiny jsou prázdné Je-li T union nebo enum, jeho asociovaným namespace je ten, ve kterém je definován Je-li T ukazatel na U nebo pole U, přejímá asociované namespace od typu U Je-li T funkce nebo ukazatel na funkci, přejímá (sjednocením) asociované namespace všech parametrů a návratového typu Je-li T třída, asociovanými namespace jsou ty, v nichž jsou definovány tato třída a všichni její přímí i nepřímí předkové Je-li T instancí šablony, přejímá kromě asociovaných tříd a namespace definovaných pro třídu také asociované třídy a namespace všech typových argumentů šablony

301 Koenigovo vyhledávání (zjednodušeno)
Koenig lookup Koenigovo vyhledávání (zjednodušeno) Argument-dependent name lookup (ISO C++) Pro každý skutečný parametr se z jeho typu T určí množina asociovaných namespace Identifikátor funkce se pak vyhledává v těchto prostorech Globální prostor a aktuální namespace Všechny namespace přidané direktivami using Sjednocení asociovaných namespace všech parametrů funkce Všechny varianty funkce nalezené v těchto namespace jsou rovnocenné Mezi nimi se vybírá podle počtu a typu parametrů Pokud není jednoznačně určena nejlepší varianta, je to chyba Volání v kontextu třídy: Je-li identifikátor nalezen uvnitř této třídy nebo některého předka (jako metoda), má přednost před výše uvedenými variantami (globálními funkcemi)

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

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

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

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

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

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

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

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

310 Complex Komplexní číslo

311 Complex class Complex { public: Complex() : re_( 0.0), im_( 0.0) {}
Complex( double re, double im = 0.0) : re_( re), im_( im) {} double Re() const { return re_; } double Im() const { return im_; } Complex & operator+=( const Complex & b); Complex operator-() const; // unární - Complex & operator++(); // prefixové ++ Complex operator++( int); // postfixové ++ // a mnoho dalších... private: double re_, im_; }; Complex operator+( const Complex & a, const Complex & b);

312 Poučení - konstruktory
Ve třídě Complex nejsou odkazy na data uložená jinde Vyhovuje chování těchto kompilátorem vytvořených metod: Complex( const Complex &); Kopíruje datové položky Complex & operator=( const Complex &); ~Complex(); Nedělá nic Tyto metody není třeba psát vlastní

313 Poučení - konstruktory
Ve třídě Complex jsou datové položky atomických typů Ty nemají konstruktory a zůstávají neinicializované Nevyhovalo by tedy chování kompilátorem vytvořeného konstruktoru bez parametrů: Complex(); Nedělá nic Navíc je zde jiný konstruktor, takže kompilátor má zakázáno konstruktor bez parametrů vytvořit Nebylo by tedy možné deklarovat proměnnou typu Complex bez explicitní inicializace Konstruktor bez parametrů musí být napsán ručně Měl by nastavit datové položky na vhodnou hodnotu

314 Poučení - konstruktory
Speciální konstruktor Complex( double re, double im = 0.0); Lze zavolat s jedním parametrem Slouží jako konverzní konstruktor Implementuje konverzi double => Complex Důsledky: Bude fungovat přiřazení Complex=double Není nutné psát sčítání pro Complex+double: Complex operator+( const Complex & a, double b) const; Dělává se to kvůli rychlosti Kompilátor umí použít konverzi (jednu) a najít náhradní metodu: Complex operator+(const Complex & a, const Complex & b) const; Totéž funguje pro sčítání double+Complex Pouze pokud je sčítání Complex+Complex globální funkce U metod se levý operand nekonvertuje

315 Poučení - vracení hodnotou
Poučení: operator+ vždy vrací hodnotou Vrací novou hodnotu, která jinde neexistuje return Complex( ...); Ale: operator+= může vracet odkazem Vrací hodnotu levého operandu return * this;

316 Poučení – binární operátory +, +=
Kanonické řešení class Complex { public: Complex( double re, double im = 0.0); // konverzní konstruktor // ... Complex & operator+=( const Complex & b) { re_ += b.re_; im_ += b.im_; return * this; } }; Complex operator+( const Complex & a, const Complex & b) Complex tmp( a); tmp += b; return tmp;

317 Poučení – unární operátory -, ++
Kanonické řešení Unární operátory jsou vždy metodami Není zapotřebí schopnost konverze operandů class Complex { public: // ... Complex operator-() const { return Complex( -re_, -im_); } Complex & operator++() { _re += 1.0; return * this; } Complex operator++( int) { Complex tmp( * this); operator++(); return tmp; } }; Prefixové ++, -- vrací odkazem Může a nemusí být const Postfixové ++, -- vrací hodnotou

318 Nepoužité slajdy

319 Globální proměnná /* abc.hpp */ extern int x;
extern const double a[ N]; extern my_class y, z; /* abc.cpp */ int x = 729; const double a[ N] = { 1.2, 3.4 }; my_class y; my_class z( 10, 20);

320 Statická položka třídy
/* abc.hpp */ class a_class { private: static int x; static my_class y; }; /* abc.cpp */ int a_class::x = 729; my_class a_class::y;

321 Statická lokální proměnná
Typické použití: Singleton Třída, vyskytující se v jediné instanci Zpřístupněna voláním f() První volání inicializuje objekt /* abc.cpp */ my_class & f() { static my_class z( 10, 20); return z; }

322 Lokální proměnná void f() { int x = 729; double u;
for ( int i = 0; i < x; ++i ) my_class y( i, 30); y.f(); } my_class z;

323 Pomocná proměnná /* abc.cpp */ void f() { my_class x;
x = my_class( 20, 30); typedef std::complex< double> my_complex; my_complex p( 1.0, 0.0); my_complex q = 2.0 * (p + 1.0); }

324 Ideální užití dědičnosti a virtuálních funkcí
Abstraktní třída Definuje rozhraní objektu jako množinu předepsaných virtuálních funkcí class GraphicObject { public: virtual ~GraphicObject(); // každá abstraktní třída má mít v.d. virtual void paint() = 0; // čistě virtuální funkce virtual void move( int dx, int dy) = 0; // čistě virtuální funkce };

325 Příklad: dědičnost a virtuální funkce
ISA hierarchie Osoba Student Zaměstnanec Učitel Matikář Fyzikář Chemikář Ředitel Ostatní Školník Kuchař Které abstraktní třídy potřebujeme? Ty, jejichž specifické rozhraní někdo potřebuje Třída „Ostatní“ není potřeba

326 Příklad: dědičnost a virtuální funkce
Virtuální funkce a schopnosti tříd Osoba (dá se evakuovat) Student (platí školné) Zaměstnanec (přijímá výplatu) Učitel (umí uspořádat třídní schůzku) Matikář (umí učit Pythagorovu větu) Fyzikář (umí učit Archimedův zákon) Chemikář (umí předvést výbuch) Ředitel (umí se podepsat) Školník (umí odemykat) Kuchař (umí počítat knedlíky)

327 Příklad: dědičnost a virtuální funkce
Násobná dědičnost Absolvent učitelství na MFF umí uspořádat třídní schůzku učit Pythagorovu větu učit Archimedův zákon Implementuje rozhraní Učitel, Matikář, Fyzikář Má být potomkem všech těchto tříd class Matfyzak : virtual public Ucitel, virtual public Matikar, virtual public Fyzikar { virtual void tridni_schuzka() { ... } virtual void pythagorova_veta() { ... } virtual void archimeduv_zakon() { ... } ... }

328 Příklad: dědičnost a virtuální funkce
Násobná dědičnost class Matfyzak : virtual public Ucitel, virtual public Matikar, virtual public Fyzikar { virtual void tridni_schuzka() { ... } virtual void pythagorova_veta() { ... } virtual void archimeduv_zakon() { ... } ... } na třídu vedou odkazy z různých míst: class Trida { ... Ucitel * tridni; ... } class III : public Trida { ... Matikar * matikar; ... } class IV : public Trida { ... Fyzikar * fyzikar; ... } kdo z nich je vlastník ? Nikdo. Jednoznačným vlastníkem je seznam zaměstnanců.

329 Příklad: dědičnost a virtuální funkce
class Matfyzak : public Zamestnanec, virtual public Ucitel, virtual public Matikar, virtual public Fyzikar { virtual void evakuace() { ... } virtual void vyplata() { ... } virtual void tridni_schuzka() { ... } virtual void pythagorova_veta() { ... } virtual void archimeduv_zakon() { ... } } class Trida { ... Ucitel * tridni; ... } class III : public Trida { ... Matikar * matikar; ... } class IV : public Trida { ... Fyzikar * fyzikar; ... } class Skola { vector< Zamestnanec *> zamestnanci; vector< Trida *> tridy; };

330 Příklad: dědičnost a virtuální funkce
ISA hierarchie Osoba Student Zaměstnanec Ředitel Školník Kuchař Potřebujeme třídu Osoba? Máme nějaký seznam osob? Další rozhraní Učitel Matikář Fyzikář Chemikář Má být Matikář odvozen z Učitele? Mají Matikář a Fyzikář něco společného?

331 Příklad přesněji: Abstraktní třídy v ISA hierarchii
class Osoba { public: virtual ~Osoba() {} virtual void evakuace() = 0; protected: Osoba() {} private: Osoba( const Osoba &); Osoba & operator=( const Osoba &); }; class Zamestnanec : public Osoba { virtual void vyplata() = 0;

332 Příklad přesněji: Abstraktní třídy pro rozhraní
class Ucitel { public: virtual void tridni_schuzka() = 0; protected: virtual ~Osoba() {} Ucitel() {} private: Ucitel( const Ucitel &); Ucitel & operator=( const Ucitel &); }; class Matikar : public Ucitel { virtual void pythagorova_veta() = 0;

333 Příklad přesněji: Konkrétní třída
class Matfyzak : public Zamestnanec, virtual public Ucitel, virtual public Matikar, virtual public Fyzikar { public: Matfyzak( const string & j ) : jmeno_( j) {} private: virtual void evakuace(); virtual void vyplata(); virtual void tridni_schuzka(); virtual void pythagorova_veta(); virtual void archimeduv_zakon(); string jmeno_; }; void Matfyzak::evakuace() { ... } ...

334 Příklad přesněji: Kontejner odkazů
class Zamestnanci { public: ~Zamestnanci(); private: typedef vector< Zamestnanec *> my_vector; my_vector v; }; Zamestnanci::~Zamestnanci() for ( my_vector::iterator it = v.begin(); it != v.end(); ++it) delete * it; } class Skola { ... Zamestnanci zamestnanci;

335 Příklad přesněji: Kontejner odkazů na rozhraní
class Ucitele { public: Matikar * najdi_matikare() const; private: typedef vector< Ucitel *> my_vector; my_vector v; }; Matikar * Ucitele::najdi_matikare() const for ( my_vector::iterator it = v.begin(); it != v.end(); ++it) Matikar * p = dynamic_cast< Matikar *>( * it); if ( p ) return p; } return 0;

336 Základní datové typy

337 Co umí hardware Datové typy podporované procesorem Celá čísla o 8/16/32/64 bitech Několik formátů čísel s pohyblivou čárkou

338 Operace podporované procesorem Celočíselná aritmetika a bitové operace
Co umí hardware Operace podporované procesorem Celočíselná aritmetika a bitové operace Operace určuje význam bitů Bez znaménka = mod 2N Se znaménkem = dvojkový doplněk Aritmetika s pohyblivou čárkou (vč. konverzí) Čtení z paměti, zápis do paměti Přesouvá pouze podporované datové typy Adresou je celé číslo o 32 resp. 64 bitech Konstanta (součástí instrukce) Registr + konstanta Registr Zarovnání: Adresa dělitelná velikostí přesouvaných dat (v bajtech) Vždy výhodné, někdy nutné Překlad adres: Zadaná adresa je virtuální

339 Datové typy podporované procesorem Celá čísla o 8/16/32/64 bitech
Elementární typy v C++ Datové typy podporované procesorem Celá čísla o 8/16/32/64 bitech 8: bool, char, signed char, unsigned char 16: short, unsigned short 16 nebo 32: wchar_t 32: int, unsigned int, long, unsigned long, výčtové typy 64: [C++11] long long, unsigned long long 32 nebo 64: ukazatel (T *), reference (T &), std::size_t, std::ptrdiff_t Počty bitů nejsou stanoveny normou Několik formátů čísel s pohyblivou čárkou float, double, long double

340 Elementární operace v C++
Celočíselná aritmetika a bitové operace Ukazatelová aritmetika Aritmetika v pohyblivé čárce Booleovská algebra a podmíněné výrazy Přístup do paměti Formální operace

341 Elementární operace v C++
Celočíselná aritmetika a bitové operace + - * / % ~ << >> & ^ | < > <= >= == != Konverze int na long long apod. Typy argumentů určují počet bitů a přítomnost znaménka Typy argumentů určují typ výsledku (delší vyhrává, nejméně int) Ukazatelová aritmetika Aritmetika v pohyblivé čárce Booleovská algebra a podmíněné výrazy Přístup do paměti Formální operace

342 Elementární operace v C++
Celočíselná aritmetika a bitové operace Ukazatelová aritmetika Realizováno celočíselnou aritmetikou procesoru + - < > <= >= == != Ukazatel +/- číslo Posun ukazatele mezi prvky pole Překladač doplní vynásobení čísla velikostí typu Ukazatel – ukazatel Vzdálenost mezi prvky pole Překladač doplní vydělení výsledku velikostí typu Aritmetika v pohyblivé čárce Booleovská algebra a podmíněné výrazy Přístup do paměti Formální operace

343 Elementární operace v C++
Celočíselná aritmetika a bitové operace Ukazatelová aritmetika Aritmetika v pohyblivé čárce + - * / < > <= >= == != Konverze int na double apod. Typy argumentů určují typ výsledku (delší vyhrává, nejméně double) Booleovská algebra a podmíněné výrazy Přístup do paměti Formální operace

344 Elementární operace v C++
Celočíselná aritmetika a bitové operace Ukazatelová aritmetika Aritmetika v pohyblivé čárce Booleovská algebra a podmíněné výrazy Realizováno podmíněnými skoky nebo bitovými operacemi ! && || ? : Zaručeno zkrácené vyhodnocování Přístup do paměti Formální operace

345 Elementární operace v C++
Celočíselná aritmetika a bitové operace Ukazatelová aritmetika Aritmetika v pohyblivé čárce Booleovská algebra a podmíněné výrazy Přístup do paměti Ke globálním proměnným (konstantní adresa) K lokálním proměnným (vrchol zásobníku + konstanta) Dereference ukazatele (*) Odpovídá operaci čtení nebo zápisu (podle kontextu) V některých kontextech pouze formální operace K prvkům třídy (konstantní posunutí) K prvkům pole (viz ukazatelová aritmetika) Formální operace

346 Elementární operace v C++
Celočíselná aritmetika a bitové operace Ukazatelová aritmetika Aritmetika v pohyblivé čárce Booleovská algebra a podmíněné výrazy Přístup do paměti Formální operace Reference tj. získání adresy (&) Dereference (*) v kontextu předání odkazem Konverze T * na U * Konverze signed na unsigned apod Většinou pouze změna v záznamech překladače Některé konverze T * na U * mohou vyžadovat operaci přičtení/odečtení konstanty

347 main

348 Start programu z pohledu OS
„Někdo“ (shell,...) požádá OS o spuštění programu Jméno spustitelného souboru Parametry (MS: vcelku, unix: po částech) OS (loader) vytvoří proces, vyhradí základní kvantum paměti načte instrukce (a inicializační data) ze souboru do paměti doplní referencované DLL, pospojuje odkazy a relokuje oživí proces na definovaném místě Proces běží a prostřednictvím systémových volání žádá o interakci s okolím (soubory, uživatelská rozhraní, komunikace,...) přidělení další paměti, vytvoření dalších vláken, ... ukončení (s návratovým kódem) Proces je zlikvidován (na vlastní žádost nebo po pádu), zabit, ... nadřazenému procesu je předán návratový kód význam návratového kódu je závislý na konvencích

349 Start programu z pohledu C++ knihoven
OS (loader) oživí proces na definovaném místě v kódu standardní knihovny Knihovní kód požádá OS o první kvantum paměti pro dynamickou alokaci inicializuje struktury dynamické alokace naváže standardní vstupy a výstupy na OS inicializuje všechny globální proměnné zkonvertuje parametry programu zavolá main uklidí globální proměnné zavře všechny otevřené soubory požádá o ukončení s návratovým kódem vráceným z main Proces je zlikvidován

350 Funkce main Kontejnery počítají pozice od 0 do size()-1
#include <vector> #include <string> #include <iostream> int main( int argc, char * * argv) { std::vector<std::string> arg( argv, argv + argc); if ( arg.size() > 1 && arg[1] == "--help" ) std::cout << "Usage: myprog [OPTION]... [FILE]..." << std::endl; return -1; } // ... return 0; Kontejnery počítají pozice od 0 do size()-1 arg[0] je jméno souboru spouštěného programu arg[1] je první parametr

351 Podmíněný překlad V C++ lze programovat tak, aby se program choval stejně na všech platformách (hardware, překladačích, operačních systémech) Pokud se má program chovat jinak, lze použít direktivy #if... int main( int argv, char * * argc) { std::vector<std::string> arg( argv, argv + argc); #ifdef unix if ( arg.size() > 1 && arg[1] == "--help" ) #else if ( arg.size() > 1 && arg[1] == "/HELP" ) #endif std::cout << "Usage: myprog [OPTION]... [FILE]..." << std::endl; return -1; } // ... return 0;

352 Ladění Makro assert Pokud podmínka neplatí, vypíše pozici ve zdrojovém textu a ukončí program Některé překladače (MSVC) v některých režimech (Release) mají makro assert prázdné Nepoužívat assert jako if #include <cassert> int main( int argv, char * * argc) { std::vector<std::string> arg( argv, argv + argc); assert( arg.size() > 0 ); // ... return 0; }

353 Preprocesor je dědictví jazyka C z roku 1970
#include #define/#ifndef #if... assert Umí ještě řadu dalších věcí – ty se ale dají v C++ udělat lépe

354 Paměť procesu

355 Organizace paměti procesu
Kódový segment Datový segment Heap Zásobník (stack segment) Segmenty (vyjma zásobníku) nemusejí být souvislé Dynamicky-linkované knihovny sdílené mezi procesy Postupná alokace heapu IP R0 R1 ... SP

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

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

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

359 Organizace paměti procesu
Kódový segment Datový segment Heap Zásobník (stack segment) Připraven op. systémem, knihovny mohou požádat OS o zvětšení Explicitně inicializované nebo neinicializované lokální proměnné Pomocné proměnné generované kompilátorem Návratové adresy Další pomocná data Vícevláknové aplikace mají více zásobníků IP R0 R1 ... SP

360 Organizace paměti vícevláknového procesu
thread 1 Vlákno z pohledu OS IP – Ukazatel instrukcí SP – Ukazatel zásobníku Další registry procesoru (Identifikátor vlákna) Paměťový prostor je společný Vlákno v paměťovém prostoru Zásobník Thread-local storage Na dně zásobníku, nebo lokalizováno dle id vlákna IP R0 R1 ... SP thread 2 IP R0 R1 ... SP


Stáhnout ppt "David Bednárek www.ksi.mff.cuni.cz/~bednarek Programování v C++ David Bednárek www.ksi.mff.cuni.cz/~bednarek."

Podobné prezentace


Reklamy Google