p rogramování v c++

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

Upload: sammy

Post on 23-Feb-2016

37 views

Category:

Documents


3 download

DESCRIPTION

P rogramování v C++. David Bednárek www.ksi.mff.cuni.cz/~bednarek. Pravidla studia. N PRG0 41 2/2 Z,Zk. 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 18 .10. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: P rogramování v C++

Programování v C++David Bednárek

www.ksi.mff.cuni.cz/~bednarek

Page 2: P rogramování v C++

Pravidla studiaNPRG041

2/2Z,Zk

Page 3: P rogramování v C++

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 18.10. Kapacita laboratoře je omezena, skupiny nelze přeplňovat Zvláštní skupina pro repetenty

• Repetenti kontaktují cvičícího do 18.10.

Udělit zápočet může jen cvičící,ke kterému je student zapsán

Kdo nebude do 18.10. zapsán, zápočet v tomto šk. roce nedostane

Page 4: P rogramování v C++

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í ... 3. 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 31.3.2014• Doladění a odevzdání do 23.5.2014

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

Page 5: P rogramování v C++

Zkouška

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

Page 6: P rogramování v C++

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

Page 7: P rogramování v C++

Historie C++

Page 8: P rogramování v C++

Historie C++B

(Bell Labs. 1969)BCPL

(Cambridge 1966)

C with classes(Stroustrup 1979)

The C++ programming language(Stroustrup 1985)

C++98(ISO/IEC 14882 1998)

C++03(ISO/IEC 14882 2003)

C++TR1(ISO/IEC 19768 2007)

C++11(ISO/IEC 14882 2011)

C(Bell Labs. 1971)

K&R C(Kernigan & Ritchie 1978)

C++14(2014+)

téměř nadmnožina

inspirace

nadmnožina

významná změna

šablony

paralelismus

Page 9: P rogramování v C++

Historie C++ a CB

(Bell Labs. 1969)BCPL

(Cambridge 1966)

C with classes(Stroustrup 1979)

The C++ programming language(Stroustrup 1985)

C++98(ISO/IEC 14882 1998)

C++03(ISO/IEC 14882 2003)

C++TR1(ISO/IEC 19768 2007)

C++11(ISO/IEC 14882 2011)

C(Bell Labs. 1971)

K&R C(Kernigan & Ritchie 1978)

C++14(2014+)

ANSI C(ANSI X3J11 1989)

C11(ISO/IEC 9899 2011)

C99(ISO/IEC 9899 1999)

téměř nadmnožina

inspirace

nadmnožina

významná změna

šablony

paralelismus

Page 10: P rogramování v C++

Historie C++ - Objective-CB

(Bell Labs. 1969)BCPL

(Cambridge 1966)

C with classes(Stroustrup 1979)

The C++ programming language(Stroustrup 1985)

C++98(ISO/IEC 14882 1998)

C++03(ISO/IEC 14882 2003)

C++TR1(ISO/IEC 19768 2007)

C++11(ISO/IEC 14882 2011)

C(Bell Labs. 1971)

K&R C(Kernigan & Ritchie 1978)

C++14(2014+)

Objective-C(Cox & Love 1981)

Object-Oriented Programing(Cox 1986)

Objective-C 2.0(Apple 2006)

Objective-C++(Apple 2010)

ANSI C(ANSI X3J11 1989)

C11(ISO/IEC 9899 2011)

C99(ISO/IEC 9899 1999)

téměř nadmnožina

inspirace

nadmnožina

významná změna

šablony

paralelismus

Page 11: P rogramování v C++

Historie C++ - významné příbuzné jazykyB

(Bell Labs. 1969)BCPL

(Cambridge 1966)

C with classes(Stroustrup 1979)

The C++ programming language(Stroustrup 1985)

C++98(ISO/IEC 14882 1998)

C++03(ISO/IEC 14882 2003)

C++TR1(ISO/IEC 19768 2007)

C++11(ISO/IEC 14882 2011)

C(Bell Labs. 1971)

K&R C(Kernigan & Ritchie 1978)

C++14(2014+)

Objective-C(Cox & Love 1981)

Object-Oriented Programing(Cox 1986)

Objective-C 2.0(Apple 2006)

Objective-C++(Apple 2010)

ANSI C(ANSI X3J11 1989)

C11(ISO/IEC 9899 2011)

C99(ISO/IEC 9899 1999)

téměř nadmnožina

inspirace

nadmnožina

významná změna

C#(Microsoft 2002)

Java(Sun 1995)

C++/CLI(Microsoft 2005)

šablony

paralelismus

Page 12: P rogramování v C++

Historie C++ - použití C v jádrech OSB

(Bell Labs. 1969)BCPL

(Cambridge 1966)

C with classes(Stroustrup 1979)

The C++ programming language(Stroustrup 1985)

C++98(ISO/IEC 14882 1998)

C++03(ISO/IEC 14882 2003)

C++TR1(ISO/IEC 19768 2007)

C++11(ISO/IEC 14882 2011)

C(Bell Labs. 1971)

K&R C(Kernigan & Ritchie 1978)

C++14(2014+)

Objective-C(Cox & Love 1981)

Object-Oriented Programing(Cox 1986)

Objective-C 2.0(Apple 2006)

Objective-C++(Apple 2010)

ANSI C(ANSI X3J11 1989)

C11(ISO/IEC 9899 2011)

C99(ISO/IEC 9899 1999)

téměř nadmnožina

inspirace

nadmnožina

významná změna

C#(Microsoft 2002)

Java(Sun 1995)

C++/CLI(Microsoft 2005)

Linux1991

Unix1973

Windows NT1993

OS-X2000

MacOS1984

šablony

paralelismus

Page 13: P rogramování v C++

Literatura

Page 14: P rogramování v C++

Literatura

Pro začátečníky - před C++11Bruce 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)

Page 15: P rogramování v C++

Literatura

Pro středně pokročilé - před C++11Andrei 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)

Page 16: P rogramování v C++

Literatura

Až si budete myslet, že všechno umíte - před C++11Andrei Alexandrescu:

Modern C++ Design (2001)Moderní programování v C++ (Computer Press 2004)

David Vandevoorde, Nicolai M. Josuttis:C++ Templates (2003)

Page 17: P rogramování v C++

Literatura

C++11Scott 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 978-0321563842. May 2013 Učebnice celého C++ Zatím jediná učebnice obsahující C++11

Page 18: P rogramování v C++

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

Page 19: P rogramování v C++

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

Page 20: P rogramování v C++

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

Page 21: P rogramování v C++

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

Page 22: P rogramování 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 */ }

Page 23: P rogramování v C++

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

Page 24: P rogramování v 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

Page 25: P rogramování v C++

Hello, World!#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

globální proměnná

Page 26: P rogramování v C++

Hello, World!

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

// world.hpp#ifndef WORLD_HPP_#define WORLD_HPP_

void world();

#endif

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

Page 27: P rogramování v C++

Hello, World!

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

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

Page 28: P rogramování v C++

ArchitekturaPřekladače / interpretry

Page 29: P rogramování v C++

CPU

C P U

Page 30: P rogramování v C++

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

C P U

01010000 01110100 11010111 10010110 00100010 10110001

Page 31: P rogramování v C++

1940... – programování ve strojovém kódu

C P U

01010000 01110100 11010111 10010110 00100010 10110001

Page 32: P rogramování v C++

1940... – programování ve strojovém kódu

C P U

01010000 01110100 11010111 10010110 00100010 10110001

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

Page 33: P rogramování v C++

1940... – programování ve strojovém kódu

C P U

01010000 01110100 11010111 10010110 00100010 10110001

X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

Page 34: P rogramování v C++

1950... – assembler

C P U

01010000 01110100 11010111 10010110 00100010 10110001

X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

C P U

assembler

PRINT NOGENBEGIN BEGIN REGS SR R2,R2 SR R3,R3LOOP AR R2,R3 LA R3,1(R0,R3) C R3,=F'10' BNE LOOP CVD R2,DBL ED RESULT,DBL+6 WTO RESULT RETURN LTORGRESULT DC X'40202120'DBL DC D'0' END BEGIN

Page 35: P rogramování v C++

1950... – operační systém

C P

U

01010000 01110100 11010111 10010110 00100010 10110001

X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

C P U

assembler

PRINT NOGENBEGIN BEGIN REGS SR R2,R2 SR R3,R3LOOP AR R2,R3 LA R3,1(R0,R3) C R3,=F'10' BNE LOOP CVD R2,DBL ED RESULT,DBL+6 WTO RESULT RETURN LTORGRESULT DC X'40202120'DBL DC D'0' END BEGIN

operačnísystém

loader

Page 36: P rogramování v C++

1950... – operační systém

C P

U

01010000 01110100 11010111 10010110 00100010 10110001

X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

C P U

assembler

PRINT NOGENBEGIN BEGIN REGS SR R2,R2 SR R3,R3LOOP AR R2,R3 LA R3,1(R0,R3) C R3,=F'10' BNE LOOP CVD R2,DBL ED RESULT,DBL+6 WTO RESULT RETURN LTORGRESULT DC X'40202120'DBL DC D'0' END BEGIN

operačnísystém

loader

myprog.exe

Page 37: P rogramování v C++

1950... – překladač

C P

U

01010000 01110100 11010111 10010110 00100010 10110001

X X X X X X X X X XXXX X X X X X X X X X X X XX X X X X XX X X XX X X X XXX X X X X X XX XXXX X XXXX X XX X XXX X X X XX X X 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

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

C P U

překladač Fortran

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 , AREASTOP END

operačnísystém

loader

myprog.exe

Page 38: P rogramování v C++

1970... – překladač C

C P

U

01010000 01110100 11010111 10010110 00100010 10110001

Hello,world!

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

C P U

překladač C

#include <stdio.h>

int main(int,char**) { printf( "Hello, world!\n"); }

operačnísystém

loader

myprog.exe

Page 39: P rogramování v C++

1980... – překladač C++

C P

U

01010000 01110100 11010111 10010110 00100010 10110001

Hello,world!

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

C P U

překladač C++

#include <iostream>

int main(int,char**) { std::cout << "Hello, world!\n"; }

operačnísystém

loader

myprog.exe

Page 40: P rogramování v C++

1960... – interpret(er)

C P

U

X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

10 INPUT "What is your name: ", U$20 PRINT "Hello "; U$30 INPUT "How do you want: ", N40 S$ = ""50 FOR I = 1 TO N60 S$ = S$ + "*"70 NEXT I80 PRINT S$90 INPUT "Do you want? ", A$100 IF LEN(A$) = 0 THEN 90110 A$ = LEFT$(A$, 1)120 IF A$ = "Y" THEN 30130 PRINT "Goodbye ";U$140 END

operačnísystém

interpret

Page 41: P rogramování v C++

Interpretace s mezikódem

C P

U

X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

04FBC41E 77AB20001AE04E33

operačnísystém

interpretpřekladač

10 INPUT "What is your name: ", U$20 PRINT "Hello "; U$30 INPUT "How do you want: ", N40 S$ = ""50 FOR I = 1 TO N60 S$ = S$ + "*"70 NEXT I80 PRINT S$90 INPUT "Do you want? ", A$100 IF LEN(A$) = 0 THEN 90110 A$ = LEFT$(A$, 1)120 IF A$ = "Y" THEN 30130 PRINT "Goodbye ";U$140 END

Page 42: P rogramování v C++

interpretovaný mezikód (bytecode)

C P

U

Hello,world!

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

04FBC41E 77AB20001AE04E33

operačnísystém

interpret

C P U

překladač

public class HelloWorld { public static void main(String[] args) { System.out.println( "Hello, world!"); } }

myprog.class

Page 43: P rogramování v C++

JIT překladač

C P

U

Hello,world!

13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0

operačnísystém

JIT překladač

C P U

překladač

public class HelloWorld { public static void main(String[] args) { System.out.println( "Hello, world!"); } }

myprog.class

01010000 01110100 11010111 10010110 00100010 10110001

Page 44: P rogramování v C++

Srovnání JIT/non-JIT

C P

U

01010000 01110100 11010111 10010110 00100010 10110001

#include <iostream>

int main(int,char**) { std::cout << "Hello, world!\n"; }

operačnísystém

loader

myprog.exe

C P

U

operačnísystém

JIT překladač

C P U

překladač

public class HelloWorld { public static void main(String[] args) { System.out.println( "Hello, world!"); } }

myprog.class

01010000 01110100 11010111 10010110 00100010 10110001

C P U

překladač

Page 45: P rogramování v C++

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

Page 46: P rogramování v C++

#include <iostream>

int main() { std::cout << "Hello, world!\n"; }

} }

Dynamické spojování…?

C P

U

01010000 01110100 11010111 10010110 00100010 10110001int main()

{ doit(); }

operačnísystém

loader

myprog.exe

C P

U

operačnísystém

JIT překladač

public class HelloWorld { public static void main(String[] args) { mylib.doit(); } }

myprog.class

01010000 01110100 11010111 10010110 00100010 10110001

překladač

mylib.dll

mylib.class

překladač

překladač

překladač

Page 47: P rogramování v C++

#include <iostream>

int main() { std::cout << "Hello, world!\n"; }

public class HelloWorld { public static void main(String[] args) { System.out.println( "Hello, world!"); } }

Dynamické spojování

C P

U

01010000 01110100 11010111 10010110 00100010 10110001

#include "mylib.hpp"

int main() { doit(); }

operačnísystém

loader

myprog.exe

C P

U

operačnísystém

JIT překladač

import acme.mylib;public class HelloWorld { public static void main(String[] args) { mylib.doit(); } }

myprog.class

01010000 01110100 11010111 10010110 00100010 10110001

“překladač”

mylib.dll

mylib.class

“překladač”

překladač

překladač

// mylib.hpp

void doit();

Page 48: P rogramování v C++

// iostream

#include <fstream>namespace std { extern ofstream cout, cerr;};

Překlad jednoduchého programu - statické spojování

Kompilátor myprog.obj Linker myprog.exe

iostream.obj

msvcrt.lib

// myprog.cpp

#include <iostream>

int main() { std::cout << "Hello, world!\n"; }

// iostream

#include <fstream>namespace std { extern ofstream cout, cerr;};

Page 49: P rogramování v C++

Oddělený překlad a spojování modulů

Uživatelské

.hpp

Standardní

KompilátorUživatelské

.cpp

Přeložené

.obj

Linker Spustitelný soubor

.exe

Standardní

.obj

Standardní .lib

Page 50: P rogramování v C++

Spojování modulů

myprog.cpp#include "bee.hpp"int main(int,char**) { return B( 7);}

myprog.obj0000: 01010000 ???????? 11010111

export main(int,argv**)import B(int)

bee.cpp#include "bee.hpp"int B( int q) { return q+1; }

bee.hpp#ifndef bee_hpp#define bee_hppint B( int q);#endif

bee.obj0000: 10010110 00100010 10110001

export B(int)

Kompilátor

Kompilátor

Linkermyprog.exe

0000: 01010000 00001100 110101111100: 10010110 00100010 10110001

Page 51: P rogramování v C++

make

Uživatelské

.hpp

Standardní

KompilátorUživatelské

.cpp

Přeložené

.obj

Linker Spustitelný soubor

.exe

Standardní

.obj

Standardní .lib

Make

makefile

Page 52: P rogramování v C++

Integrované prostředí

Uživatelské

.hpp

Standardní

KompilátorUživatelské

.cpp

Přeložené

.obj

Linker Spustitelný soubor

.exe

Standardní

.obj

Standardní .lib

Editor

projekt

Debugger

Page 53: P rogramování v C++

Statické knihovny

Uživatelské

.hpp

Standardní

KompilátorUživatelské

.cpp

Přeložené

.obj

Linker Spustitelný soubor

.exe

Standardní

.obj

Standardní .lib

Knihovní

.hpp

KompilátorKnihovní

.cpp

Přeložené

.obj

Librarian

Knihovna .lib

Page 54: P rogramování v C++

Dynamické knihovny (Microsoft)

Uživatelské

.hpp

Standardní

KompilátorUživatelské

.cpp

Přeložené

.obj

Linker Spustitelný soubor

.exe

Standardní

.obj

Standardní .lib

Knihovní

.hpp

KompilátorKnihovní

.cpp

Přeložené

.obj

Linker

Knihovna .lib

Knihovna

.dll

Page 55: P rogramování v C++

Dynamické knihovny (GNU)

Uživatelské

.hpp

Standardní

KompilátorUživatelské

.cpp

Přeložené

.o

Linker Spustitelný soubor

Standardní

.o

Standardní .a

Knihovní

.hpp

KompilátorKnihovní

.cpp

Přeložené

.o

Librarian Knihovna

.so

Page 56: P rogramování v C++

Deklarace a definice

Page 57: P rogramování v C++

Deklarace a definiceDeklarace

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 definiceDefinice

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ěhuKaždá definice je i deklaraceDeklarace 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

Page 58: P rogramování v C++

Deklarace a definiceOne-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

Page 59: P rogramování v C++

Deklarace a definice tříd a typůDeklarace Definice

Třída 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;

Page 60: P rogramování v C++

Deklarace a definice proměnnýchDeklarace Definice

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

class A { 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

class A { int x, y;};

Nestatická lokální proměnná

void f() { int x; int y = 7, z( 7);};

Page 61: P rogramování v C++

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

class A { int f( int p);};

int A::f( int p){ return p + 1;}

Virtuální metoda class A { virtual int f( int p);};

int A::f( int){ return 0;}

inline Deklarace (.hpp nebo .cpp) Definice (.hpp nebo .cpp)

Globální inline funkce

inline int f( int p, int q){ return p + q;}

Nestatická inline metoda (a)

class A { int f( int p);};

inline int A::f( int p){ return p + 1;}

Nestatická inline metoda (b)

class A { int f( int p) { return p+1;}};

Page 62: P rogramování v C++

Umístění dat

Page 63: P rogramování v C++

Umístění datStatická 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áknoZá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)

Page 64: P rogramování v C++

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

Page 65: P rogramování v C++

Okamžik inicializace a destrukce Inicializace Destrukce

Globální proměnná Před vstupem do main Po výstupu z main

Statická položka třídy Před vstupem do main Po výstupu z main

Statická lokální proměnná

Při prvním průchodu řízení deklarací

Po výstupu z main

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

Page 66: P rogramování v C++

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

Page 67: P rogramování v C++

Vybrané číselné typy bool false, truechar 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

Page 68: P rogramování v C++

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

Page 69: P rogramování v C++

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

Page 70: P rogramování v C++

Ukazatel vs. hodnota

Page 71: P rogramování v C++

Reference, ukazatelé, iterátory Reference

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++ Zvláštní operátory pro přístup k hodnotě (*p, p->a) 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 Zvláštní operátory pro přístup k hodnotě (*p, p->a) Napodobenina ukazatelové aritmetiky

K::iteratorK::const_iterator

Page 72: P rogramování v C++

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

class T {public: int a;};

void f( T * z){ z->a = 3;}

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

delete x; //zánik je nutno vyvolat ručně}

Page 73: P rogramování v C++

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

class T {public: int a;};

void f( T * z){ z->a = 3;}

void g(){ std::shared_ptr< T> x = new T; //vznik

x->a = 1;

std::shared_ptr< T> y = x; //druhý odkaz

y->a = 2; // x->a == 2

f( x); // x->a == 3

//zrušení při zániku posledního odkazu}

Page 74: P rogramování v C++

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); // x.a == 1

//zrušení v okamžiku zániku proměnné }}

class T {public: int a;};

void f( T z){ z.a = 3;}

void g(){ T x; //vznik

x.a = 1;

T y = x; //kopie

y.a = 2; // x.a == 1

f( x); // x.a == 1

//zrušení v okamžiku zániku proměnné}

Page 75: P rogramování v C++

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: int a;};

void f( T & z){ z.a = 3;}

void g(){ T x;

x.a = 1;

f( x); // x.a == 3}

Page 76: P rogramování v C++

Referenční semantika C#/Java vs. C++Předání odkazem (C#)Referenční typy

Předání odkazem (C++)Reference na ukazatelUží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(); //vznik

f( ref x); // x je nyní jiný objekt

//zrušení zařídí garbage collector }

}

class T {public: int a;};

void f( T * & z){ delete z; //zánik je nutno vyvolat ručně z = new T; //vznik}

void g(){ T * x = new T; //vznik

f( x); // *x je nyní jiný objekt

delete x; //zánik je nutno vyvolat ručně}

Page 77: P rogramování v C++

Dynamická alokacemy_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ř

Page 78: P rogramování v C++

Chytré ukazatelevoid 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 = new my_class( 20, 30);

std::shared_ptr< my_class> q = p;

q.reset( new my_class( 10, 20));

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

Page 79: P rogramování v C++

Reference

Page 80: P rogramování v C++

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 {...}

void g(){ T x = new T; //vznik

T y = x; //druhý odkaz

y.f();}

//zrušení zařídí//garbage//collector

class T {...}

void g(){ T x; //vznik

T y = x; //kopie

y.f();}

//zánik při//výstupu z bloku

class T {...}

void g(){ T x; //vznik

T & y = x; //odkaz

y.f();}

//zánik při//výstupu z bloku

class T {...}

void g(){ T * x = new T; //vznik

T * y = x; //druhý odkaz

y->f();

delete y; //zánik

x->f(); //chyba}

Page 81: P rogramování v C++

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 &

Page 82: P rogramování v C++

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

Page 83: P rogramování v C++

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

Page 84: P rogramování v C++

Reference

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 }

Page 85: P rogramování v C++

Vracení odkazemFunkce jako add nemůže vracet referenci

add vrací hodnotu různou od všech svých parametrůhodnotu parametrů nesmí měnitreference 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

Page 86: P rogramování v C++

Vracení odkazemFunkce jako add nemůže vracet referenci

add vrací hodnotu různou od všech svých parametrůhodnotu parametrů nesmí měnitreference 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 ?

Page 87: P rogramování v C++

Vracení odkazemFunkce jako add nemůže vracet referenci

add vrací hodnotu různou od všech svých parametrůhodnotu parametrů nesmí měnitreference 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));

Page 88: P rogramování v C++

Vracení odkazemFunkce jako add musí vracet hodnotou

add vrací hodnotu různou od všech svých parametrůhodnotu parametrů nesmí měnitreference 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);

Page 89: P rogramování v C++

Vracení odkazemFunkce 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í

Page 90: P rogramování v C++

Vracení odkazemŘešení bez kopírování

class ComplexBody{public: ComplexBody( double r, double i) : re( r), im( i) {} double re, im;};

class Complex{public: 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

Page 91: P rogramování v C++

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

Page 92: P rogramování v C++

STLStandard Template Library

Page 93: P rogramování v C++

STLKontejnery

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

Page 94: P rogramování v C++

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(); // 1the_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(); // 6the_deque.pop_back()int z = the_deque.back(); // 5

Page 95: P rogramování v C++

STL – KontejnerySekvenční 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)

Page 96: P rogramování v C++

STL – KontejneryPomocné metody kontejneru

Test prázdnostibool empty() const

Počet prvkůsize_t size() const

nepoužívat pro testy prázdnosti

Page 97: P rogramování v C++

STL – KontejneryMetody kontejneru, vracející iterátory

Odkaz na začátek kontejneru - první platný prvekiterator begin()const_iterator begin() const

Odkaz za konec kontejneru - za poslední platný prvekiterator 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

Page 98: P rogramování v C++

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 random_access

list bidirectional

forward_list forward

Pro iterátor I jsou definovány tyto operace:

output *I = xinput *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 != I2random_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

Page 99: P rogramování v C++

STL – IterátoryOperátory definované na iterátorech

přístup k prvku, na který iterátor ukazujeT & iterator::operator *() constconst 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)

Page 100: P rogramování v C++

STL – KontejneryMetody 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ý prvekvoid insert( iterator p, int n, T x)

vsune n kopií x před prvek, na který ukazuje iterátor ptemplate< 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í

Page 101: P rogramování v C++

STL – KontejneryKonstruktory 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

Page 102: P rogramování v C++

STL – Kontejnery 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 variantyiterator 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 hodnotouk.insert( it, T( x, y, z)); // bezejmenny objekt

... nebo pokud je použito std::movek.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");

Page 103: P rogramování v C++

STL – Kontejnery 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 variantyiterator 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ápisuk.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

Page 104: P rogramování v C++

STL – KontejneryMetody 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

• p nesmí být rovno end()• 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()); }

Page 105: P rogramování v C++

STL – KontejneryOdvozené funkce manipulace s konci kontejneru

Přidání jednotlivého prvkuvoid 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 prvkuvoid pop_front(){ return erase( begin()); }

• list, deque

void pop_back(){ return erase( --end()); }

• list, deque, vector

Page 106: P rogramování v C++

STL – KontejneryOdvozené 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

obě provádějí{ 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

Page 107: P rogramování v C++

STL – KontejneryOdvozené 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) constconst T & operator[]( int n) const

všechny provádějíreturn * ( begin() + n);

Page 108: P rogramování v C++

STL - Kontejnerysložitost operace

na kontejneru s n prvky

list deque vector basic_string

přídání / odebrání jednoho prvku na začátku

push_frontpop_front

konstantní konstantní funkce neexistuje

funkce neexistuje

přídání / odebrání jednoho prvku na i-té pozici

inserterase

konstantní min( i, n - i) n - i n - i

přídání / odebrání m prvků na i-té pozici

inserterase

mpřesuny mezi seznamy (splice) jsou konstantní

m +min( i, n - i) m + n - i m + n - i

přídání / odebrání jednoho prvku na konci

push_backpop_back

konstantní konstantní konstantní funkce neexistuje

nalezení i-tého prvku

begin() + i funkce neexistuje

konstantní konstantní konstantní

paměťová náročnost

kontejneru s prvky velikosti s

(s + K) * nK řádově 16 B

q * s * nq kolem 1.2

q * s * nq kolem 1.2

q * s * nq kolem 1.2

Page 109: P rogramování v C++

Asociativní kontejnery

Page 110: P rogramování v C++

STL - KontejneryAsociativní 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

Page 111: P rogramování v C++

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í

Page 112: P rogramování v C++

STL - KontejneryUspořá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));

Page 113: P rogramování v C++

STL - KontejneryUspořá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 funktorutemplate< 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) ) /*...*/ }};

Page 114: P rogramování v C++

STL - KontejneryUspořá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 { /*...*/ }};template< typename K>class equal_to { public: bool operator()( const K & a, const K & b) const { return a == b; }};

• Šablona kontejneru má dva další parametrytemplate< 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());

Page 115: P rogramování v C++

STL - KontejneryAsociativní 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

Page 116: P rogramování v C++

STL - KontejneryAsociativní 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())

Page 117: P rogramování v C++

STL - KontejneryUspořá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

Page 118: P rogramování v C++

STL - KontejneryAsociativní 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())

Page 119: P rogramování v C++

STL - KontejneryAsociativní kontejnery - vkládání

set a mappair< 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ý prvekpokud 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 emplacesložitost operace

uspořadné kontejnery: O( log( size())) hashující kontejnery: průměrně O(1), nejhůře O( size())

Page 120: P rogramování v C++

STL - KontejneryAsociativní 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 emplacesložitost operace

uspořadné kontejnery: O( log( size())) hashující kontejnery: průměrně O(1), nejhůře O( size())

Page 121: P rogramování v C++

STL - KontejneryAsociativní 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ů

Page 122: P rogramování v C++

STL - KontejneryAsociativní kontejnery - odebírání

podle iterátoru - jeden prvekvoid 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ů - intervalvoid 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é

Page 123: P rogramování v C++

STL - Kontejnerymap/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; }

Vrátí referenci na hodnotovou (second) složku prvku s klíčem ekvivalentním x

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

Page 124: P rogramování v C++

Algoritmy

Page 125: P rogramování v C++

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

Page 126: P rogramování v C++

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

Page 127: P rogramování v C++

STL – AlgoritmyPouž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] LambdaNová syntaktická konstrukce generující funktor

void increment( list< double> & c){ for_each( c.begin(), c.end(), []( double & x){ x += 1;});}

Page 128: P rogramování v C++

STL – AlgoritmyPouž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

void add( list< double> & c, double value){ for_each( c.begin(), c.end(), [=]( double & x){ x += value;});}

Page 129: P rogramování v C++

STL – AlgoritmyPouž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] Lambdadouble sum( const list< double> & c){ double s = 0.0; for_each( c.begin(), c.end(), [&]( const double & x){ s += x;}); return s;}

Page 130: P rogramování v C++

STL – AlgoritmyPouž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

Page 131: P rogramování v C++

STL – AlgoritmyPouž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;}

Page 132: P rogramování v C++

Lambda

Page 133: P rogramování v C++

Lambda výrazyMotivace

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

Page 134: P rogramování v C++

Lambda výrazyLambda výraz

[ capture ]( params ) mutable -> rettype { body }

Deklaruje třídu ve tvaruclass 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 objektuftor( vlist ...)

C++11

Page 135: P rogramování v C++

Lambda výrazy – návratový typ a typ funkceNá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

Page 136: P rogramování v C++

Lambda výrazy – captureCapture

[ 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

Page 137: P rogramování v C++

Lambda výrazy – příkladint 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();std::cout << a << b << c;

Co to vypíše?

123234

C++11

Page 138: P rogramování v C++

Další algoritmy

Page 139: P rogramování v C++

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

Page 140: P rogramování v C++

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

Page 141: P rogramování v C++

STL – AlgoritmyPřehled algoritmů

Průchod kontejnerem 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

Page 142: P rogramování v C++

STL – AlgoritmyPř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ů

Page 143: P rogramování v C++

STL – AlgoritmyPř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í kontejneryPozor: Tyto funkce neprodlužují ani nezkracují kontejner

Page 144: P rogramování v C++

STL – AlgoritmyPřehled algoritmů

Pozor: Algoritmy neprodlužují ani nezkracují kontejnervector< 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));

Page 145: P rogramování v C++

STL – AlgoritmyPřehled algoritmů

min, max - minimum a maximum ze dvou hodnotTří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

Page 146: P rogramování v C++

Konstruktory a destruktoryConstructors and Destructors

Page 147: P rogramování v C++

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římoDestruktor 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í

Page 148: P rogramování v C++

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

Page 149: P rogramování v C++

copy/moveSpeciální metody tříd – C++11

Page 150: P rogramování v C++

copy/moveSpeciální metody tříd

Copy constructorT( const T & x);

Move constructorT( T && x);

Copy assignment operatorT & operator=( const T & x);

Move assignment operatorT & operator=( T && x);

C++11

Page 151: P rogramování v C++

copy/movePřekladačem definované chování (default)

Copy constructorT( const T & x) = default;

aplikuje copy constructor na složkyMove constructor

T( T && x) = default; aplikuje move constructor na složky

Copy assignment operatorT & operator=( const T & x) = default;

aplikuje copy assignment operator na složkyMove assignment operator

T & operator=( T && x) = default; aplikuje move assignment operator na složky

default umožňuje vynutit defaultní chování

C++11

Page 152: P rogramování v C++

copy/movePodmí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

Page 153: P rogramování v C++

copy/moveNejběž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/moveT( const T & x);T( T && x);T & operator=( const T & x);T & operator=( T && x);~T();

C++11

Page 154: P rogramování v C++

copy/moveDalší 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

Page 155: P rogramování v C++

Konverze

Page 156: P rogramování v C++

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átoryclass XXX { operator YYY() const;};

Definuje uživatelskou konverzi typu XXX na YYY Vrací typ YYY hodnotou (tedy s použitím kopírovacího konstruktoru

YYY, pokud je YYY třída)

Kompilátor vždy použije nejvýše jednu uživatelskou konverzi

Page 157: P rogramování v C++

Konstruktory a spol.Typické situace

Page 158: P rogramování v C++

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 { std::string x_;};

Page 159: P rogramování v C++

Konstruktory a spol.Všechny položky jsou neškodné

Položky mají svoje konstruktoryTří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_;};

Page 160: P rogramování v C++

Konstruktory a spol.Všechny položky jsou neškodné

Položky mají svoje konstruktoryKonstruktor 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_;};

Page 161: P rogramování v C++

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

Page 162: P rogramování v C++

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

Page 163: P rogramování v C++

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

Page 164: P rogramování v C++

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

Page 165: P rogramování v C++

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

Page 166: P rogramování v C++

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

Page 167: P rogramování v C++

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

Page 168: P rogramování v C++

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

Page 169: P rogramování v C++

Vznik a zánik objektů

Page 170: P rogramování v C++

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 }

Page 171: P rogramování v C++

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

Page 172: P rogramování v C++

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

Page 173: P rogramování v C++

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

Page 174: P rogramování v C++

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;

Page 175: P rogramování v C++

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;

Page 176: P rogramování v C++

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

Page 177: P rogramování v C++

Dědičnost a virtuální funkce

Page 178: P rogramování v C++

Dědičnostclass Base { /* ... */ };class Derived : public Base { /* ... */ }

Třída Derived je odvozena z třídy BaseObsahuje všechny datové položky i metody třídy BaseMůže k nim doplnit další

•Není vhodné novými zakrývat staré, vyjma virtuálníchMůže změnit chování metod, které jsou v Base deklarovány jako virtuální

class Base { virtual void f() { /* ... */ } };

class Derived : public Base { virtual void f() { /* ... */ }};

Page 179: P rogramování v C++

Virtuální funkceclass 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() { /* ... */ } 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

Page 180: P rogramování v C++

Virtuální funkceclass Base { virtual void f() { /* ... */ } };class Derived : public Base { virtual void f() { /* ... */ }};

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 objektub.f(); // volá Base::f ikdyž je virtuální

Slicing je specifikum jazyka C++

Page 181: P rogramování v C++

Názvosloví

Abstraktní třídaDefinice v C++: Třída obsahující alespoň jednu čistě

virtuální funkciBěžná definice: Třída, která sama nebude instanciovánaPředstavuje rozhraní, které mají z ní odvozené třídy

(potomci) implementovat

Konkrétní třídaTřída, určená k samostatné instanciaciImplementuje rozhraní, předepsané abstraktní třídou, ze

které je odvozena

Page 182: P rogramování v C++

Dědičnost a destruktor

class Base {public: virtual ~Base() {}};

class Derived : public Base {public: 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

Page 183: P rogramování v C++

DědičnostMechanismus 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...)

Page 184: P rogramování v C++

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

Page 185: P rogramování v C++

Nesprávné užití dědičnostiNesprávné užití dědičnosti č. 1

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 &

Page 186: P rogramování v C++

Nesprávné užití dědičnostiNesprávné užití dědičnosti č. 1

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 &

Page 187: P rogramování v C++

Nesprávné užití dědičnostiNesprávné užití dědičnosti č. 2

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

Page 188: P rogramování v C++

Nesprávné užití dědičnostiNesprávné užití dědičnosti č. 2

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 &

Page 189: P rogramování v C++

Nesprávné užití dědičnostiNesprávné užití dědičnosti č. 2

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í seReal & Real::operator=( const Real &); // nesouhlasí typ argumentu

Page 190: P rogramování v C++

ŠablonyTemplates

Page 191: P rogramování v C++

Š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 šablonytemplate< formální-parametry>

lze použít před několika formami deklarací; oblastí platnosti formálních parametrů je celá prefixovaná deklarace

C++11

Page 192: P rogramování v C++

Šablony tříd - instanciaceInstanciace š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

Page 193: P rogramování v C++

Š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 toPř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

Page 194: P rogramování v C++

Šablony tříd - thisPokud 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; }}

Page 195: P rogramování v C++

Šablony funkcí

Š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řídtemplate< typename T, int k> // parametry šablony int f( T * p, int q); // parametry funkcetemplate< 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

Page 196: P rogramování v C++

Š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

Page 197: P rogramování v C++

Šablony – pokročilé konstrukce jazykaParciá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í specializaceExplicitní 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>;

Page 198: P rogramování v C++

Explicitní specializacePří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> { enum { value = 1 };};

Kontrolní otázka: Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::value

Page 199: P rogramování v C++

Teoretický pohled na šablonyJiný 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> { enum { value = 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

Page 200: P rogramování v C++

Parciální specializaceParciá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);};

Page 201: P rogramování v C++

Parciální specializaceParciá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 */ };

Page 202: P rogramování v C++

Parciální specializaceParciá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í specializaceExplicitní 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ů

Page 203: P rogramování v C++

Parciální specializaceTypická 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šší

Page 204: P rogramování v C++

Parciální specializaceTypická 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 Šablona je používána přímo z nešablonovaného kódu Uživatel by měl být o specializaci informován Příklad: vector< bool> nedovoluje vytvořit ukazatel na jeden prvek

Page 205: P rogramování v C++

Parciální specializaceTypická 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 Šablona je používána přímo z nešablonovaného kódu 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

Page 206: P rogramování v C++

Parciální specializaceModifikace 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); }};

Page 207: P rogramování v C++

Parciální specializaceModifikace 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 objektyObsahují 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

Page 208: P rogramování v C++

Traits & policiesTraits

Šablony, ze kterých nejsou vytvářeny objektyObsahují 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 classesTřídy, ze kterých obvykle nejsou vytvářeny objektyPředávány jako parametr šablonám

Defaultní hodnotou parametru často bývá šablona traitsUrčeny k definování určitého chování

Příklad: Alokační strategie

Page 209: P rogramování v C++

Triky s šablonamiPorovná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ýrazPoužití

template< class T1>class Test { enum { T1_is_int = Equal< int, T1>::value}; enum { T1_is_long = Equal< long, T1>::value}; /* ... */};

Page 210: P rogramování v C++

Triky s šablonamiPorovná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; /* ... */};

Page 211: P rogramování v C++

Triky s šablonamiKompilační ověření invariantu

template< int x>struct AssertNot;

template<>struct AssertNot< 0> { enum { value = true };};

template< int x>struct Assert { enum { value = AssertNot< ! x>::value };};

Page 212: P rogramování v C++

Triky s šablonamiKompilační ověření invariantu

template< int x>struct AssertNot;

template<>struct AssertNot< 0> { enum { value = true };};

template< int x>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]

Page 213: P rogramování v C++

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

Page 214: P rogramování v C++

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

Page 215: P rogramování v C++

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

Page 216: P rogramování v C++

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

Page 217: P rogramování v C++

Š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> &);};template< typename T> class X { X( const X &);};

• Některé překladače připouštějí i tuto variantutemplate< typename T> class X { X< T>( const X< T> &);};

Page 218: P rogramování v C++

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

Page 219: P rogramování v C++

Š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 parametryPř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>;

Page 220: P rogramování v C++

Š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 { /* ... */ };

Page 221: P rogramování v C++

CastRůzné druhy přetypování

Page 222: P rogramování v C++

PřetypováníRůzné varianty syntaxe

C-style cast(T)e

Převzato z C

Page 223: P rogramování v C++

PřetypováníRůzné varianty syntaxe

C-style cast(T)e

Převzato z CFunction-style cast

T(e) Ekvivalentní (T)e

• T musí být syntakticky identifikátor nebo klíčové slovo označující typ

Page 224: P rogramování v C++

PřetypováníRůzné varianty syntaxe

C-style cast(T)e

Převzato z CFunction-style cast

T(e) Ekvivalentní (T)e

• T musí být syntakticky identifikátor nebo klíčové slovo označující typType 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)

Page 225: P rogramování v C++

PřetypováníRůzné varianty syntaxe

C-style cast(T)e

Převzato z CFunction-style cast

T(e) Ekvivalentní (T)e

• T musí být syntakticky identifikátor nebo klíčové slovo označující typType 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)

Page 226: P rogramování v C++

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

Page 227: P rogramování v C++

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í objektclass Data { public: void register_pointer() const { references++; }private: /* ... data ... */ mutable int references; };

Page 228: P rogramování v C++

Přetypování

static_cast<T>(e)

UmožňujeVš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

Page 229: P rogramování v C++

Přetypování

static_cast<T>(e)

UmožňujeVš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)); }};

Page 230: P rogramování v C++

Přetypování

static_cast<T>(e)

UmožňujeVšechny implicitní konverzeKonverze na void - zahození hodnoty výrazu

Používá se v makrech a podmíněných výrazechKonverze 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

Page 231: P rogramování v C++

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

Page 232: P rogramování v C++

Přetypování

reinterpret_cast<T>(e)

UmožňujeKonverze ukazatele na dostatečně velké celé čísloKonverze celého čísla na ukazatelKonverze mezi různými ukazateli na funkceKonverze 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ý

Page 233: P rogramování v C++

Přetypování

dynamic_cast<T>(e)

UmožňujeKonverze odkazu na odvozenou třídu na odkaz na předka:

• Derived & => Base &• Derived * => Base *

Implicitní konverze, chová se stejně jako static_castKonverze 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

Page 234: P rogramování v C++

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

Page 235: P rogramování v C++

Exception handlingMechanismus výjimek

Page 236: P rogramování v C++

Exception handlingSrovnání: goto

Start: příkaz gotoCíl: návěští

Určen při kompilaciSkok může opustit blok

Proměnné korektně zaniknouvolá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 ) { goto label; } /* ... */ } return 0;

label: std::cerr << "Error" << std::endl; return -1;}

Page 237: P rogramování v C++

Exception handlingSrovnání: goto

Start: příkaz gotoCíl: návěští

Určen při kompilaciSkok může opustit blok

Proměnné korektně zaniknouvoláním destruktorů

Cíl musí být v téže proceduře

Srovnání 2: <csetjmp>Pro pokročiléStart: volání longjmpCíl: volání setjmpSkok může opustit proceduruNeř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 ) { goto label; } /* ... */ } return 0;

label: std::cerr << "Error" << std::endl; return -1;}

Page 238: P rogramování v C++

Exception handlingMechanismus výjimek

Start: příkaz throwCíl: try-catch blok

Určen za běhuSkok může opustit proceduru

Proměnné korektně zaniknouvolá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; }}

Page 239: P rogramování v C++

Exception handlingMechanismus výjimek

Start: příkaz throwCíl: try-catch blok

Určen za běhuSkok může opustit proceduru

Proměnné korektně zaniknouvolá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 ) { /*...*/ }}

Page 240: P rogramování v C++

Exception handlingMechanismus výjimek

Start: příkaz throwCíl: try-catch blok

Určen za běhuSkok může opustit proceduru

Proměnné korektně zaniknouvolá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 : public AnyException { /*...*/ };void f(){ if ( something == wrong ) throw WrongException( something); if ( anything != good ) throw BadException( anything);}void g(){ try { f(); } catch ( const AnyException & e1 ) { /*...*/ }}

Page 241: P rogramování v C++

Exception handlingMechanismus výjimek

Start: příkaz throwCíl: try-catch blok

Určen za běhuSkok může opustit proceduru

Proměnné korektně zaniknouvolá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 : public AnyException { /*...*/ };void f(){ if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException();}void g(){ try { f(); } catch ( const AnyException &) { /*...*/ }}

Page 242: P rogramování v C++

Exception handlingMechanismus výjimek

Start: příkaz throwCíl: try-catch blok

Určen za běhuSkok může opustit proceduru

Proměnné korektně zaniknouvolá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 : public AnyException { /*...*/ };void f(){ if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException();}void g(){ try { f(); } catch (...) { /*...*/ }}

Page 243: P rogramování v C++

Exception handlingFá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 throwProvedení 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-unwindingZpracová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

Page 244: P rogramování v C++

Exception handlingPouž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ě

Page 245: P rogramování v C++

Exception handlingStandardní výjimky

<stdexcept> Všechny standardní výjimky jsou potomky třídy exception

metoda what() vrací řetězec s chybovým hlášenímbad_alloc: vyvolává operátor new při nedostatku paměti

V režimu "bez výjimek" new vrací nulový ukazatelbad_cast, bad_typeid: Chybné použití RTTIOdvozené 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

Page 246: P rogramování v C++

Exception handlingStandardní výjimky

<stdexcept> Všechny standardní výjimky jsou potomky třídy exception

metoda what() vrací řetězec s chybovým hlášenímbad_alloc: vyvolává operátor new při nedostatku paměti

V režimu "bez výjimek" new vrací nulový ukazatelbad_cast, bad_typeid: Chybné použití RTTIOdvozené 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

Page 247: P rogramování v C++

Exception handlingTypické použití

Deklarace výjimekclass AnyException { /*...*/ };class WrongException : public AnyException { /*...*/ };class BadException : public AnyException { /*...*/ };

Vyvolání výjimekvoid 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( /*...*/){ try { g(); h(); } catch ( WrongException ) { std::cout << "WrongException"; } catch ( BadException ) { std::cout << "BadException"; }}

Page 248: P rogramování v C++

Exception handlingPoužití se std::exception

Deklarace výjimekclass WrongException : public std::exception { virtual const char * what() const { return "WrongException"; }};class BadException : public std::exception { virtual const char * what() const { return "BadException"; }};

Vyvolání výjimekvoid 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( /*...*/){ try { g(); h(); } catch ( const std::exception & e ) { std::cout << e.what(); }}

Page 249: P rogramování v C++

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

Page 250: P rogramování v C++

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

Page 251: P rogramování v C++

Exception-safe programmingPravidla 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čí

Page 252: P rogramování v C++

Exception-safe programmingPravidla 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

Page 253: P rogramování v C++

Exception-safe programmingPravidla 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é)

Page 254: P rogramování v C++

Exception-safe programmingPravidla 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é)

Page 255: P rogramování v C++

Exception-safe programmingPravidla 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

Page 256: P rogramování v C++

Exception-safe programmingPozná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é

Page 257: P rogramování v C++

Exception-safe programmingKompilá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

Page 258: P rogramování v C++

Exception-safe programmingKompilá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 Ve zpracování výjimky se poté pokračuje

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

Page 259: P rogramování v C++

Exception-safe programmingDefinice

(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

Page 260: P rogramování v C++

Exception-safe programmingDefinice

(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"

Page 261: P rogramování v C++

Exception-safe programmingPozná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

Page 262: P rogramování v C++

Exception-safe programmingPří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;}

Page 263: P rogramování v C++

Exception-safe programmingPří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;}

Page 264: P rogramování v C++

Exception-safe programmingPří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;}

Page 265: P rogramování v C++

Exception-safe programmingPříklad: String č. 2

operator=Lepší pokus o opravu:

Pokud new char způsobí výjimku, ošetří seJe 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;}

Page 266: P rogramování v C++

Exception-safe programmingPříklad: String č. 2

operator=Lepší pokus o opravu:

Pokud new char způsobí výjimku, ošetří seJe 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;}

Page 267: P rogramování v C++

Exception-safe programmingPří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;}

Page 268: P rogramování v C++

Exception-safe programmingPří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;}

Page 269: P rogramování v C++

Exception-safe programmingPří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;}

Page 270: P rogramování v C++

Exception-safe programmingPří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;}

Page 271: P rogramování v C++

Exception-safe programmingPří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;}

Page 272: P rogramování v C++

Exception-safe programmingPří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;}

Page 273: P rogramování v C++

Exception-safe programmingPříklad: String č. 2

operator=Silně bezpečné řešeníPozorování:

Toto řešení je "shodou okolností" imunní protithis == & 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;}

Page 274: P rogramování v C++

Exception-safe programmingPříklad: String č. 2

operator=Silně bezpečné řešeníPozorování:

Toto řešení je "shodou okolností" imunní protithis == & 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;}

Page 275: P rogramování v C++

Exception-safe programmingPří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;}

Page 276: P rogramování v C++

Exception-safe programmingPří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);}

Page 277: P rogramování v C++

Exception-safe programmingPří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);}

Page 278: P rogramování v C++

Exception-safe programmingPříklad: String č. 2

copy-constructorSilně 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(); }}

Page 279: P rogramování v C++

Exception-safe programmingPří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;}

Page 280: P rogramování v C++

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Slabě bezpečná implementaceNení 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;}

Page 281: P rogramování v C++

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Slabě bezpečná implementaceNení 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; }}

Page 282: P rogramování v C++

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Nefunkční implementaceNení 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; }}

Page 283: P rogramování v C++

Exception-safe programmingPří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é

Page 284: P rogramování v C++

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Silně bezpečná implementaceUž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 (...){ /* ??? */}

Page 285: P rogramování v C++

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Řešení AJako v STLRozdě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 */}try { stk.pop();}catch (...){ /* chyba zkracování, proměnná a změněna, zásobník nedotčen */}

Page 286: P rogramování v C++

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Řešení BNamí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 */}

Page 287: P rogramování v C++

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Řešení BLze 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 (...) { swap( out, t); throw; } }};

Page 288: P rogramování v C++

Exception specificationsException 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 */}

Page 289: P rogramování v C++

Exception specificationsException 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čí

Page 290: P rogramování v C++

Exception specificationsException specifications

Kompilátor zajistí, že nepovolená výjimka neopustí funkciToto 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í */}

Page 291: P rogramování v C++

Exception specificationsException specifications

Kompilátor (a runtime) zajistí, že nepovolená výjimka neopustí funkci

Microsoft Visual C++ 7.0 to ovšem neimplementujeKompilá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

Page 292: P rogramování v C++

Přetěžování funkcí

Page 293: P rogramování v C++

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

Page 294: P rogramování v C++

Přetěžování funkcí

C++ dovoluje existenci více funkcí téhož jménave 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

Page 295: P rogramování v C++

Přetěžování funkcí

int min( int a, int b) { return a < b ? a : b; }

double min( double a, double b) { return a < b ? a : b; }

min( 1, 2); // min( int, int) - přesná shoda min( 1, 2.0); // min( double, double) - levnější varianta min( 1.0, 2); // min( double, double) - levnější varianta min( 1.0, 2.0); // min( double, double) - přesná shoda

void f( int, double); void f( double, int);

f( 1, 2); // chyba: obě varianty jsou stejně drahé f( 1.0, 2.0); // chyba: obě varianty jsou stejně drahé

Page 296: P rogramování v C++

Koenig lookup

Page 297: P rogramování v C++

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

Page 298: P rogramování v C++

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

Page 299: P rogramování v C++

Koenig lookupprostor::Souradnice p;std::cout << p; // správný operator<< je v namespace prostor,

// který není přímo vidětstd::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

Page 300: P rogramování v C++

Koenig lookupKoenigovo 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

Page 301: P rogramování v C++

Koenig lookupKoenigovo 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 namespaceIdentifiká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)

Page 302: P rogramování v C++

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

Page 303: P rogramování v C++

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íchPř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í

Page 304: P rogramování v C++

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

Page 305: P rogramování v C++

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

MetodouA 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

Page 306: P rogramování v C++

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

MetodouA B::operator xxx() A B::operator xxx() const

Page 307: P rogramování v C++

Přetěžování operátorů - Unární operátoryPostfixové 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)

MetodouA B::operator xxx( int) A B::operator xxx( int) const

Page 308: P rogramování v C++

Přetěžování operátorů - Unární operátoryOperátor ->

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

Page 309: P rogramování v C++

Přetěžování operátorů - Unární operátoryOperá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.

Page 310: P rogramování v C++

ComplexKomplexní číslo

Page 311: P rogramování v C++

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

Page 312: P rogramování v C++

Poučení - konstruktory

Ve třídě Complex nejsou odkazy na data uložená jindeVyhovuje chování těchto kompilátorem vytvořených metod:

Complex( const Complex &); Kopíruje datové položky

Complex & operator=( const Complex &); Kopíruje datové položky

~Complex(); Nedělá nic

Tyto metody není třeba psát vlastní

Page 313: P rogramování v C++

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

Page 314: P rogramování v C++

Poučení - konstruktorySpeciá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

Page 315: P rogramování v C++

Poučení - vracení hodnotou

Poučení: operator+ vždy vrací hodnotouVrací novou hodnotu, která jinde neexistuje

return Complex( ...);

Ale: operator+= může vracet odkazemVrací hodnotu levého operandu

return * this;

Page 316: P rogramování v C++

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

Page 317: P rogramování v C++

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

Page 318: P rogramování v C++

Nepoužité slajdy

Page 319: P rogramování v C++

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

Page 320: P rogramování v C++

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;

Page 321: P rogramování v C++

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

Page 322: P rogramování v C++

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

Page 323: P rogramování v C++

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

Page 324: P rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Abstraktní třídaDefinuje 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};

Page 325: P rogramování v C++

Příklad: dědičnost a virtuální funkceISA hierarchieOsoba

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

Page 326: P rogramování v C++

Příklad: dědičnost a virtuální funkceVirtuální funkce a schopnosti třídOsoba (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)

Page 327: P rogramování v C++

Příklad: dědičnost a virtuální funkceNá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() { ... } ... }

Page 328: P rogramování v C++

Příklad: dědičnost a virtuální funkceNá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ů.

Page 329: P rogramování v C++

Příklad: dědičnost a virtuální funkceclass 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;};

Page 330: P rogramování v C++

Příklad: dědičnost a virtuální funkceISA hierarchieOsoba

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?

Page 331: P rogramování v C++

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

class Zamestnanec : public Osoba { public: virtual void vyplata() = 0;};

Page 332: P rogramování v C++

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 { public: virtual void pythagorova_veta() = 0;};

Page 333: P rogramování v C++

Příklad přesněji: Konkrétní třídaclass 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() { ... }...

Page 334: P rogramování v C++

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 { ...private: Zamestnanci zamestnanci;};

Page 335: P rogramování v C++

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

Page 336: P rogramování v C++

Základní datové typy

Page 337: P rogramování v C++

Co umí hardwareDatové typy podporované procesorem

Celá čísla o 8/16/32/64 bitechNěkolik formátů čísel s pohyblivou čárkou

Page 338: P rogramování v C++

Co umí hardwareOperace podporované procesorem

Celočíselná aritmetika a bitové operace Operace určuje význam bitů

• Bez znaménka = mod 2N

• Se znaménkem = dvojkový doplněkAritmetika 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í

Page 339: P rogramování v C++

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

Page 340: P rogramování v C++

Elementární operace v C++Celočíselná aritmetika a bitové operaceUkazatelová aritmetikaAritmetika v pohyblivé čárceBooleovská algebra a podmíněné výrazyPřístup do pamětiFormální operace

Page 341: P rogramování v C++

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á aritmetikaAritmetika v pohyblivé čárceBooleovská algebra a podmíněné výrazyPřístup do pamětiFormální operace

Page 342: P rogramování v C++

Elementární operace v C++Celočíselná aritmetika a bitové operaceUkazatelová 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é čárceBooleovská algebra a podmíněné výrazyPřístup do pamětiFormální operace

Page 343: P rogramování v C++

Elementární operace v C++Celočíselná aritmetika a bitové operaceUkazatelová aritmetikaAritmetika 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ýrazyPřístup do pamětiFormální operace

Page 344: P rogramování v C++

Elementární operace v C++Celočíselná aritmetika a bitové operaceUkazatelová aritmetikaAritmetika v pohyblivé čárceBooleovská 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ětiFormální operace

Page 345: P rogramování v C++

Elementární operace v C++Celočíselná aritmetika a bitové operaceUkazatelová aritmetikaAritmetika v pohyblivé čárceBooleovská algebra a podmíněné výrazyPří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

Page 346: P rogramování v C++

Elementární operace v C++Celočíselná aritmetika a bitové operaceUkazatelová aritmetikaAritmetika v pohyblivé čárceBooleovská algebra a podmíněné výrazyPřístup do pamětiFormá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

Page 347: P rogramování v C++

main

Page 348: P rogramování v C++

Start programuStart 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

Page 349: P rogramování v C++

Start programuStart 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

Page 350: P rogramování v C++

Funkce main#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

Page 351: P rogramování v C++

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

Page 352: P rogramování v C++

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

Page 353: P rogramování v C++

PreprocesorPreprocesor 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

Page 354: P rogramování v C++

Paměť procesu

Page 355: P rogramování v C++

Organizace paměti procesu

Kódový segmentDatový segmentHeapZásobník (stack segment)

Segmenty (vyjma zásobníku) nemusejí být souvislé• Dynamicky-linkované knihovny sdílené mezi procesy• Postupná alokace heapu

IP

R0R1...

SP

Page 356: P rogramování v C++

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ý segmentHeapZásobník (stack segment)

IP

R0R1...

SP

Page 357: P rogramování v C++

Organizace paměti procesu

Kódový segmentDatový 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

HeapZásobník (stack segment)

IP

R0R1...

SP

Page 358: P rogramování v C++

Organizace paměti procesu

Kódový segmentDatový segmentHeap

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

R0R1...

SP

Page 359: P rogramování v C++

Organizace paměti procesu

Kódový segmentDatový segmentHeapZá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

R0R1...

SP

Page 360: P rogramování v C++

thread 2

thread 1

Organizace paměti vícevláknového procesu

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

R0R1...

SP

IP

R0R1...

SP