p rogramov n v c n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
P rogramování v C++ PowerPoint Presentation
Download Presentation
P rogramování v C++

Loading in 2 Seconds...

play fullscreen
1 / 360

P rogramování v C++ - PowerPoint PPT Presentation


  • 91 Views
  • Uploaded on

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.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'P rogramování v C++' - sammy


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
p rogramov n v c

Programování v C++

David Bednárek

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

pravidla studia

Pravidla studia

NPRG041

2/2

Z,Zk

z pis na cvi en
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
ud len z po tu
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
zkou ka
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
pravidla pro budouc ne sp n
Pravidla pro budoucí neúspěšné
  • Zkouška
    • Pokud letos složíte zkoušku se známkou výborně nebo velmi dobřea 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
historie c1
Historie C++

inspirace

nadmnožina

B

(Bell Labs. 1969)

C

(Bell Labs. 1971)

BCPL

(Cambridge 1966)

téměř nadmnožina

významná změna

K&R C

(Kernigan & Ritchie 1978)

C with classes

(Stroustrup1979)

The C++ programming language

(Stroustrup1985)

šablony

C++98

(ISO/IEC 14882 1998)

C++03

(ISO/IEC 14882 2003)

C++TR1

(ISO/IEC 19768 2007)

paralelismus

C++11

(ISO/IEC 14882 2011)

C++14

(2014+)

historie c a c
Historie C++ a C

inspirace

nadmnožina

B

(Bell Labs. 1969)

C

(Bell Labs. 1971)

BCPL

(Cambridge 1966)

téměř nadmnožina

významná změna

K&R C

(Kernigan & Ritchie 1978)

C with classes

(Stroustrup1979)

The C++ programming language

(Stroustrup1985)

ANSI C

(ANSI X3J111989)

šablony

C++98

(ISO/IEC 14882 1998)

C99

(ISO/IEC 9899 1999)

C++03

(ISO/IEC 14882 2003)

C++TR1

(ISO/IEC 19768 2007)

paralelismus

C++11

(ISO/IEC 14882 2011)

C++14

(2014+)

C11

(ISO/IEC 9899 2011)

historie c obje ctive c
Historie C++ - Objective-C

inspirace

nadmnožina

B

(Bell Labs. 1969)

C

(Bell Labs. 1971)

BCPL

(Cambridge 1966)

téměř nadmnožina

významná změna

K&R C

(Kernigan & Ritchie 1978)

C with classes

(Stroustrup1979)

Objective-C

(Cox & Love 1981)

The C++ programming language

(Stroustrup1985)

Object-Oriented Programing

(Cox1986)

ANSI C

(ANSI X3J111989)

šablony

C++98

(ISO/IEC 14882 1998)

C99

(ISO/IEC 9899 1999)

C++03

(ISO/IEC 14882 2003)

Objective-C 2.0

(Apple2006)

C++TR1

(ISO/IEC 19768 2007)

Objective-C++

(Apple 2010)

paralelismus

C++11

(ISO/IEC 14882 2011)

C++14

(2014+)

C11

(ISO/IEC 9899 2011)

historie c v znamn p buzn jazyky
Historie C++ - významné příbuzné jazyky

inspirace

nadmnožina

B

(Bell Labs. 1969)

C

(Bell Labs. 1971)

BCPL

(Cambridge 1966)

téměř nadmnožina

významná změna

K&R C

(Kernigan & Ritchie 1978)

C with classes

(Stroustrup1979)

Objective-C

(Cox & Love 1981)

The C++ programming language

(Stroustrup1985)

Object-Oriented Programing

(Cox1986)

ANSI C

(ANSI X3J111989)

Java

(Sun 1995)

šablony

C++98

(ISO/IEC 14882 1998)

C99

(ISO/IEC 9899 1999)

C#

(Microsoft2002)

C++03

(ISO/IEC 14882 2003)

Objective-C 2.0

(Apple2006)

C++/CLI

(Microsoft2005)

C++TR1

(ISO/IEC 19768 2007)

Objective-C++

(Apple 2010)

paralelismus

C++11

(ISO/IEC 14882 2011)

C++14

(2014+)

C11

(ISO/IEC 9899 2011)

historie c pou it c v j drech os
Historie C++ - použití C v jádrech OS

inspirace

Unix

1973

nadmnožina

B

(Bell Labs. 1969)

C

(Bell Labs. 1971)

BCPL

(Cambridge 1966)

téměř nadmnožina

významná změna

K&R C

(Kernigan & Ritchie 1978)

C with classes

(Stroustrup1979)

Objective-C

(Cox & Love 1981)

MacOS

1984

The C++ programming language

(Stroustrup1985)

Object-Oriented Programing

(Cox1986)

ANSI C

(ANSI X3J111989)

Linux

1991

Java

(Sun 1995)

Windows NT

1993

šablony

C++98

(ISO/IEC 14882 1998)

C99

(ISO/IEC 9899 1999)

OS-X

2000

C#

(Microsoft2002)

C++03

(ISO/IEC 14882 2003)

Objective-C 2.0

(Apple2006)

C++/CLI

(Microsoft2005)

C++TR1

(ISO/IEC 19768 2007)

Objective-C++

(Apple 2010)

paralelismus

C++11

(ISO/IEC 14882 2011)

C++14

(2014+)

C11

(ISO/IEC 9899 2011)

literatura1
Literatura
  • Pro začátečníky - před C++11
    • Bruce Eckel:Thinking in C++ (2000)Myslíme v jazyku C++ (Grada 2000)
    • Miroslav Virius:Pasti a propasti jazyka C++ (Computer Press 2005)Programování v C++ (ČVUT 2001)
    • Andrew Koenig, Barbara E. Moo:Accelerated C++ (2000)
    • Stanley B. Lippman:Essential C++ (2000)
literatura2
Literatura
  • Pro středně pokročilé - před C++11
    • Andrei Alexandrescu, Herb Sutter:C++ Coding Standards (2005)
    • Scott Meyers:Effective C++ (1998)More Effective C++ (1996)Effective STL (2001)
    • Herb Sutter:Exceptional C++ (2000)More Exceptional C++ (2002)Exceptional C++ Style (2004)
    • Nicolai M. Josuttis:Object-Oriented Programming in C++ (2002)The C++ Standard Library (1999)
literatura3
Literatura
  • Až si budete myslet, že všechno umíte - před C++11
    • Andrei Alexandrescu:Modern C++ Design (2001)Moderní programování v C++ (Computer Press 2004)
    • David Vandevoorde, Nicolai M. Josuttis:C++ Templates (2003)
literatura4
Literatura
  • C++11
    • Scott Meyers: Overview of the New C++ (C++11)
      • 360 slajdů z přednášek
      • Vysvětluje motivaci k novým vlastnostem
    • Bjarne Stroustrup:The C++ Programming Language - Fourth Edition
      • Addison-Wesley. ISBN 978-0321563842. May 2013
      • Učebnice celého C++
      • Zatím jediná učebnice obsahující C++11
co programovat 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
co programovat v c1
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
co programovat v c2
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 */ }

co neprogramovat 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
pro 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
hello world
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á
hello world1
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
hello world2
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

architektura

Architektura

Překladače / interpretry

slide29
CPU

C P U

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

C P U

01010000 01110100 11010111 10010110 00100010 10110001

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

C P U

01010000 01110100 11010111 10010110 00100010 10110001

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

C P U

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

01010000 01110100 11010111 10010110 00100010 10110001

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

C P U

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

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

01010000 01110100 11010111 10010110 00100010 10110001

1950 assembler
1950... – assembler

C P U

C P U

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

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

01010000 01110100 11010111 10010110 00100010 10110001

PRINT NOGEN

BEGIN BEGIN

REGS

SR R2,R2

SR R3,R3

LOOP AR R2,R3

LA R3,1(R0,R3)

C R3,=F'10'

BNE LOOP

CVD R2,DBL

ED RESULT,DBL+6

WTO RESULT

RETURN

LTORG

RESULT DC X'40202120'

DBL DC D'0'

END BEGIN

assembler

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

C P U

C P U

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

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

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

loader

PRINT NOGEN

BEGIN BEGIN

REGS

SR R2,R2

SR R3,R3

LOOP AR R2,R3

LA R3,1(R0,R3)

C R3,=F'10'

BNE LOOP

CVD R2,DBL

ED RESULT,DBL+6

WTO RESULT

RETURN

LTORG

RESULT DC X'40202120'

DBL DC D'0'

END BEGIN

assembler

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

C P U

C P U

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

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

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

loader

PRINT NOGEN

BEGIN BEGIN

REGS

SR R2,R2

SR R3,R3

LOOP AR R2,R3

LA R3,1(R0,R3)

C R3,=F'10'

BNE LOOP

CVD R2,DBL

ED RESULT,DBL+6

WTO RESULT

RETURN

LTORG

RESULT DC X'40202120'

DBL DC D'0'

END BEGIN

myprog.exe

assembler

1950 p eklada
1950... – překladač

C P U

C P U

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

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

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

loader

READ INPUT TAPE 5, 501, IA, IB, IC

501 FORMAT (3I5)

IF (IA) 777, 777, 701

701 IF (IB) 777, 777, 702

702 IF (IC) 777, 777, 703

703 IF (IA+IB-IC) 777,777,704

704 IF (IA+IC-IB) 777,777,705

705 IF (IB+IC-IA) 777,777,799

777 STOP 1

799 S = FLOATF (IA + IB + IC) / 2.0

AREA = SQRT( S * (S - FLOATF(IA))

* (S - FLOATF(IB)) *

+ (S - FLOATF(IC)))

WRITE OUTPUT TAPE 6, 601, IA, IB, IC

, AREA

STOP

END

myprog.exe

překladač Fortran

1970 p eklada c
1970... – překladač C

C P U

C P U

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

Hello,world!

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

loader

#include <stdio.h>

int main(int,char**)

{

printf(

"Hello, world!\n");

}

myprog.exe

překladač C

19 8 0 p eklada c
1980... – překladač C++

C P U

C P U

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

Hello,world!

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

loader

#include <iostream>

int main(int,char**)

{

std::cout <<

"Hello, world!\n";

}

myprog.exe

překladač C++

1960 interpret er
1960... – interpret(er)

C P U

10 INPUT "What is your name: ", U$

20 PRINT "Hello "; U$

30 INPUT "How do you want: ", N

40 S$ = ""

50 FOR I = 1 TO N

60 S$ = S$ + "*"

70 NEXT I

80 PRINT S$

90 INPUT "Do you want? ", A$

100 IF LEN(A$) = 0 THEN 90

110 A$ = LEFT$(A$, 1)

120 IF A$ = "Y" THEN 30

130 PRINT "Goodbye ";U$

140 END

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

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

interpret

operačnísystém

interpretace s mezik dem
Interpretace s mezikódem

C P U

10 INPUT "What is your name: ", U$

20 PRINT "Hello "; U$

30 INPUT "How do you want: ", N

40 S$ = ""

50 FOR I = 1 TO N

60 S$ = S$ + "*"

70 NEXT I

80 PRINT S$

90 INPUT "Do you want? ", A$

100 IF LEN(A$) = 0 THEN 90

110 A$ = LEFT$(A$, 1)

120 IF A$ = "Y" THEN 30

130 PRINT "Goodbye ";U$

140 END

04FBC41E 77AB2000

1AE04E33

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

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

překladač

interpret

operačnísystém

interpretovan mezik d bytecode
interpretovaný mezikód (bytecode)

C P U

C P U

publicclass

HelloWorld {

publicstaticvoid

main(String[] args)

{ System.out.println(

"Hello, world!");

}

}

myprog.class

překladač

04FBC41E 77AB2000

1AE04E33

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

Hello,world!

interpret

operačnísystém

jit p eklada
JIT překladač

C P U

C P U

publicclass

HelloWorld {

publicstaticvoid

main(String[] args)

{ System.out.println(

"Hello, world!");

}

}

myprog.class

překladač

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

Hello,world!

JIT překladač

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

srovn n jit non jit
Srovnání JIT/non-JIT

C P U

C P U

C P U

C P U

publicclass

HelloWorld {

publicstaticvoid

main(String[] args)

{ System.out.println(

"Hello, world!");

}

}

myprog.class

překladač

JIT překladač

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

01010000 01110100 11010111 10010110 00100010 10110001

#include <iostream>

int main(int,char**)

{

std::cout <<

"Hello, world!\n";

}

operačnísystém

loader

myprog.exe

překladač

dynamick spojov n
Dynamické spojování…?

C P U

C P U

publicclass

HelloWorld {

publicstaticvoid

main(String[] args)

{mylib.doit();

}

}

myprog.class

}

}

překladač

mylib.class

překladač

JIT překladač

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

int main()

{

doit();

}

01010000 01110100 11010111 10010110 00100010 10110001

#include <iostream>

int main()

{

std::cout <<

"Hello, world!\n";

}

operačnísystém

loader

myprog.exe

překladač

mylib.dll

překladač

dynamick spojov n1
Dynamické spojování

C P U

C P U

import acme.mylib;

publicclass

HelloWorld {

publicstaticvoid

main(String[] args)

{mylib.doit();

}

}

myprog.class

publicclass

HelloWorld {

publicstaticvoid

main(String[] args)

{ System.out.println(

"Hello, world!");

}

}

překladač

mylib.class

překladač

JIT překladač

01010000 01110100 11010111 10010110 00100010 10110001

operačnísystém

#include "mylib.hpp"

int main()

{

doit();

}

// mylib.hpp

void doit();

01010000 01110100 11010111 10010110 00100010 10110001

#include <iostream>

int main()

{

std::cout <<

"Hello, world!\n";

}

operačnísystém

loader

myprog.exe

“překladač”

mylib.dll

“překladač”

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

// iostream

#include <fstream>

namespace std {

extern ofstream

cout, cerr;

};

// iostream

#include <fstream>

namespace std {

extern ofstream

cout, cerr;

};

iostream.obj

msvcrt.lib

Kompilátor

myprog.obj

Linker

myprog.exe

// myprog.cpp

#include <iostream>

int main()

{

std::cout <<

"Hello, world!\n";

}

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

Standardní

Standardní .obj

Standardní .lib

Uživatelské .hpp

Uživatelské .cpp

Kompilátor

Přeložené .obj

Linker

Spustitelný soubor .exe

spojov n modul
Spojování modulů

myprog.cpp

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

myprog.obj

0000: 01010000 ???????? 11010111

exportmain(int,argv**)

importB(int)

Kompilátor

bee.hpp

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

myprog.exe

0000: 01010000 00001100 110101111100: 10010110 00100010 10110001

Linker

bee.cpp

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

bee.obj

0000: 10010110 00100010 10110001

exportB(int)

Kompilátor

slide51
make

Standardní

Standardní .obj

Standardní .lib

Uživatelské .hpp

Uživatelské .cpp

Kompilátor

Přeložené .obj

Linker

Spustitelný soubor .exe

Make

makefile

integrovan prost ed
Integrované prostředí

Standardní

Standardní .obj

Standardní .lib

Uživatelské .hpp

Uživatelské .cpp

Kompilátor

Přeložené .obj

Linker

Spustitelný soubor .exe

Editor

Debugger

projekt

statick knihovny
Statické knihovny

Standardní

Standardní .obj

Standardní .lib

Uživatelské .hpp

Uživatelské .cpp

Kompilátor

Přeložené .obj

Linker

Spustitelný soubor .exe

Knihovní .hpp

Knihovna .lib

Knihovní .cpp

Kompilátor

Přeložené .obj

Librarian

dynamick knihovny microsoft
Dynamické knihovny (Microsoft)

Standardní

Standardní .obj

Standardní .lib

Uživatelské .hpp

Uživatelské .cpp

Kompilátor

Přeložené .obj

Linker

Spustitelný soubor .exe

Knihovní .hpp

Knihovna .lib

Knihovní .cpp

Knihovna.dll

Kompilátor

Přeložené .obj

Linker

dynamick knihovny gnu
Dynamické knihovny (GNU)

Standardní

Standardní .o

Standardní .a

Uživatelské .hpp

Uživatelské .cpp

Kompilátor

Přeložené .o

Linker

Spustitelný soubor

Knihovní .hpp

Knihovní .cpp

Knihovna.so

Kompilátor

Přeložené .o

Librarian

deklarace a definice1
Deklarace a definice
  • Deklarace
    • Zápis sdělující, že věc (typ/proměnná/funkce/...) existuje
      • Identifikátor
      • Základní vlastnosti věci
      • Umožňuje překladači přeložit kód, který na věc odkazuje
        • V některých případech je k tomu zapotřebí i definice
  • Definice
    • Zápis, který určuje všechny vlastnosti věci
      • Obsah třídy, inicializace proměnné, kód funkce
      • Umožňuje překladači vygenerovat kód a data, která věc reprezentují za běhu
    • Každá definice je i deklarace
  • Deklarace umožňují (některá) použití věci bez definice
      • Oddělený překlad modulů
      • Vyřešení cyklických závislostí
      • Zmenšení objemu překládaného zdrojového kódu
deklarace a definice2
Deklarace a definice
  • One-definition rule #1:
    • Jedna překladová jednotka...
        • (modul, tj. jedno .cpp včetně inkludovaných hpp)
    • ... smí obsahovat nejvýše jednu definici věci
  • One-definition rule #2:
    • Program...
        • (tj. .exe včetně připojených .dll)
    • ... smí obsahovat nejvýše jednu definici proměnné nebo non-inline funkce
      • Definice třídy, typu či inline funkce se v různých modulech opakovat smějí (typicky vložením téhož .hpp souboru)
        • Nejsou-li opakované definice totožné, nebo nesouhlasí-li definice s deklarací, program je nekorektní
      • Diagnostika na úrovni programu není normou požadována a překladače/linkery ji dělají jen v jednoduchých případech
um st n dat1
Umístění dat
  • Statická alokace = 1 instance na proces
    • Globální proměnná
    • Statická položka třídy
    • Statická lokální proměnná
  • [C++11]thread_local objekty = 1 instance na vlákno
  • Zásobníková alokace = 1 instance na každé vyvolání
    • Lokální proměnná
    • Parametr předávaný hodnotou
    • Návratová hodnota funkce
    • Pomocná proměnná při výpočtu výrazu
  • Dynamická alokace = řízeno programem
    • Dynamicky alokovaná data (new/delete)
inicializace
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
slo en typy 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
reference ukazatel iter tory
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::iterator

K::const_iterator

dynamick alokace
Dynamická alokace

my_class * p = 0;

void f1()

{

p = new my_class( 20, 30);

}

void f2()

{

delete p;

}

  • Dynamická alokace pole
    • std::vector je lepší

void f2( std::size_t n)

{

int * q = new int[ n];

q[ n-1] = p->m();

// ...

delete[] q;

}

  • Dynamickou alokaci je nutné použít, pokud
    • Rozsah života objektu se nekryje s vyvoláním funkce, nebo
    • Alokovaný objekt je třída s dědičností zařazená do datové struktury
  • Ostatní případy lze většinou nahradit použitím kontejnerů
    • Kontejnery skrývají dynamickou alokaci uvnitř
chytr ukazatele
Chytré ukazatele

void f1()

{

std::unique_ptr< my_class> p

= new my_class( 20, 30);

std::unique_ptr< my_class> q

= std::move( p); // nuluje p

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

// dealokuje původní objekt

} // dealokuje druhý objekt

void f2()

{

std::shared_ptr< my_class> p

= 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
reference a ukazatel
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 &
reference a ukazatel1
Reference a ukazatelé
  • Pravidla pro začátečníky
    • Kdy použít referenci: T &
      • Výstupní parametr
      • Návratová hodnota funkce zpřístupňující objekt
        • T & vector<T>::at(size_t i)
    • Kdy použít konstantní referenci: const T &
      • Obvykle pouze kvůli rychlosti
      • Parametr typu struktura/třída
      • Návratová hodnota funkce zpřístupňující objekt ke čtení
        • const T & vector<T>::at(size_t i) const
    • Kdy použít ukazatel (T *)
      • Je-li objekt dynamicky alokován
      • Je-li nutná schopnost přesměrování, null, nebo aritmetika
      • Nelze-li referenci správně namířit v okamžiku inicializace
    • Kdy použít konstantní ukazatel (const T *)
      • Sekundární odkaz na objekt, schopný pouze čtení
reference a ukazatel2
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 *”
reference2
Reference
      • Novinkysouvisející sexistencí 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; // funkcevrátí referenci na neexistující objekt
  • }
vracen odkazem
Vracení odkazem
  • Funkce jako add nemůže vracet referenci
    • add vrací hodnotu různou od všech svých parametrů
    • hodnotu parametrů nesmí měnit
    • reference nemá na co ukazovat
    • Špatné řešení č. 1: Lokální proměnná

Complex & add( const Complex & a, const Complex & b)

{

Complex r( a.Re + b.Re, a.Im + b.Im);

return r;

}

    • BĚHOVÁ CHYBA: r zaniká při návratu z funkce
vracen odkazem1
Vracení odkazem
  • Funkce jako add nemůže vracet referenci
    • add vrací hodnotu různou od všech svých parametrů
    • hodnotu parametrů nesmí měnit
    • reference nemá na co ukazovat
    • Špatné řešení č. 2: Dynamická alokace

Complex & add( const Complex & a, const Complex & b)

{

Complex * r= new Complex( a.Re + b.Re, a.Im + b.Im);

return * r;

}

    • PROBLÉM: kdo to odalokuje ?
vracen odkazem2
Vracení odkazem
  • Funkce jako add nemůže vracet referenci
    • add vrací hodnotu různou od všech svých parametrů
    • hodnotu parametrů nesmí měnit
    • reference nemá na co ukazovat
    • Špatné řešení č. 3: Globální proměnná

Complex g;

Complex & add( const Complex & a, const Complex & b)

{

g = Complex( a.Re + b.Re, a.Im + b.Im);

return g;

}

    • CHYBA: globální proměnná je sdílená

Complex a, b, c, d, e = add( add( a, b), add( c, d));

vracen odkazem3
Vracení odkazem
  • Funkce jako add musí vracet hodnotou
    • add vrací hodnotu různou od všech svých parametrů
    • hodnotu parametrů nesmí měnit
    • reference nemá na co ukazovat
    • Správné řešení

Complex add( const Complex & a, const Complex & b)

{

Complex r( a.Re + b.Re, a.Im + b.Im);

return r;

}

      • Zkrácený (ekvivalentní) zápis

return Complex( a.Re + b.Re, a.Im + b.Im);

vracen odkazem4
Vracení odkazem
  • Funkce jako add musí vracet hodnotou

Complex add( const Complex & a, const Complex & b)

{

Complex r( a.Re + b.Re, a.Im + b.Im);

return r;

}

    • Data se při vracení z funkce (několikrát) kopírují

z = add( x, y);

      • plnění proměnné r [constructor]
      • kopie ven z funkce [copy-constructor]
      • přiřazení [operator=]
      • [C++11] rvalue reference mohou některá kopírování usnadnit
    • Řešení bez kopírování existuje
      • za cenu dynamické alokace
      • u malých dat (Complex, string) se nevyplatí
vracen odkazem5
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
reference a ukazatel3
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

slide92

STL

Standard Template Library

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

#include <deque>

typedef std::deque< int> my_deque;

my_deque the_deque;

the_deque.push_back( 1);

the_deque.push_back( 2);

the_deque.push_back( 3);

int x = the_deque.front(); // 1

the_deque.pop_front();

my_deque::iterator ib = the_deque.begin();

my_deque::iterator ie = the_deque.end();

for ( my_deque::iterator it = ib;it != ie; ++it)

{

*it = *it + 3;

}

int y = the_deque.back(); // 6

the_deque.pop_back()

int z = the_deque.back(); // 5

stl kontejnery
STL – Kontejnery
  • Sekvenč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)
stl kontejnery1
STL – Kontejnery
  • Pomocné metody kontejneru
    • Test prázdnosti

bool empty() const

    • Počet prvků

size_t size() const

      • nepoužívat pro testy prázdnosti
stl kontejnery2
STL – Kontejnery
  • Metody kontejneru, vracející iterátory
    • Odkaz na začátek kontejneru - první platný prvek

iterator begin()

const_iterator begin() const

    • Odkaz za konec kontejneru - za poslední platný prvek

iterator end()

const_iterator end() const

    • iterator a const_iterator jsou typy definované uvnitř kontejneru, zvané iterátory
      • přístupné konstrukcemi jako vector< int>::iterator
      • vlastnosti iterátorů jsou mírně závislé na druhu kontejneru
    • Iterátor kontejneru obsahujícího typ T je třída s operátory definovanými tak, aby se chovala podobně jako "T *" "(ukazatel na typ T) resp. "const T *"
      • Vytváří se tak iluze, že kontejner je pole
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 = x

input

*I /* pouze pro čtení */

random_access, bidirectional, forward

*I /* čtení i zápis */

všechny kategorie

++I, I++

random_access, bidirectional, forward, input

I1 == I2, I1 != I2

random_access, bidirectional

--I, I--

random_access

I += n, I + n, n + I

I -= n, I - n, I1 - I2

I[ n]

I1 < I2, I1 > I2, I1 <= I2, I1 >= I2

Kategorie iterátorů
stl iter tory
STL – Iterátory
  • Operátory definované na iterátorech
    • přístup k prvku, na který iterátor ukazuje

T & iterator::operator *() const

const T & const_iterator::operator *() const

    • posouvání iterátoru směrem ke konci
      • jednosměrný iterátor

iterator & iterator::operator++()

    • posouvání iterátoru směrem k začátku
      • obousměrný iterátor

iterator & iterator::operator--()

    • libovolný posun
      • iterátor s přímým přístupem

iterator operator+( iterator, int)

iterator operator-( iterator, int)

stl kontejnery3
STL – Kontejnery
  • Metody kontejneru pro vkládání

iterator insert( iterator p, T x)

      • vsune (kopii) x před prvek, na který ukazuje iterátor p
        • vrací iterátor ukazující na vložený prvek

void insert( iterator p, int n, T x)

      • vsune n kopií x před prvek, na který ukazuje iterátor p

template< typename other_iterator>

void insert( iterator p, other_iterator b, other_iterator e)

      • před prvek, na který ukazuje iterátor p, vsune kopii posloupnosti prvků ohraničené iterátory b a e
        • Tato posloupnost nesmí být uvnitř téhož kontejneru
        • Tato posloupnost může být součástí jiného druhu kontejneru
      • je-li p == end(), vkládání připojuje na konec kontejneru
      • všechny dříve existující iterátory odkazující na tento kontejner jsou po použití insert neplatné, včetně p
        • výjimka: kontejnerylist a forward_list iterátory mířící na původní prvky nebo end() zachovávají
stl kontejnery4
STL – Kontejnery
  • Konstruktory kontejneru

K()

      • Vytvoří prázdný kontejner (array: kontejner dané velikosti)

K( int n, T x = T())

      • Vytvoří kontejner s n kopiemi hodnoty x
        • Má-li typ T konstruktor bez parametrů, není třeba udávat x

template< typename other_iterator>

K( other_iterator b, other_iterator e)

      • Vytvoří kontejner naplněný kopií posloupnosti prvků ohraničené iterátory b a e
        • Tato posloupnost může být součástí jiného druhu kontejneru
stl kontejnery5
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 varianty

iterator insert( iterator p, T && x)

      • Překladač ji použije, je-li parametrem pomocná proměnná...

k.insert( it, a + b); // operator nebo funkce vracejici hodnotou

k.insert( it, T( x, y, z)); // bezejmenny objekt

      • ... nebo pokud je použito std::move

k.insert( it, std::move( a));

        • Move-semantika: Proměnná a bude (může být) vyprázdněna
      • Move-semantika poskytuje úsporu času (a prostoru), pokud typ T
        • obsahuje dynamicky alokovaná data
        • je na move semantiku připraven (má move-konstruktory)

std::vector< std::string> k;

std::string a = "...";

k.push_back( a + ".kzr");

stl kontejnery6
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í emplacevarianty

iterator emplace( iterator p, T1 && x1, ..., Tn && xn);

void emplace_back( T1 && x1, ..., Tn && xn);

void emplace_front( T1 && x1, ..., Tn && xn);

      • Do kontejneru je přidán nový prvek inicializovaný konstruktorem s parametry x1, ..., xn

std::vector< std::vector< int> > k;

k.emplace_back(100, 0);

      • Šetří kopírování vkládaného prvku oproti původnímu zápisu

k.push_back( std::vector< int>( 100, 0));

      • Šetří i v případě nepřipraveného typu bez move-semantiky
        • V případě vector< int> by to kopírování ušetřila sama move-semantika
        • Poznámka: rvalue reference v hlavičce emplace funkcí dovolují i lvalue operandy pomocí skládání referencí a funkce std::forward
stl kontejnery7
STL – Kontejnery
  • Metody kontejneru pro odebírání

iterator erase( iterator p)

      • vyjme prvek, na který ukazuje iterátor p
        • p nesmí být rovno end()
        • vrací iterátor ukazující na prvek za vyjmutým prvkem (nebo end())

iterator erase( iterator p, iterator e)

      • vyjme všechny prvky mezi p a e, včetně p a vyjma e
        • 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: kontejnerylist a forward_list iterátory mířící na nesmazané prvky zachovávají

void clear()

{ erase( begin(), end()); }

stl kontejnery8
STL – Kontejnery
  • Odvozené funkce manipulace s konci kontejneru
    • Přidání jednotlivého prvku

void push_front( T x)

{ return insert( begin(), x); }

        • list, deque

void push_back( T x)

{ return insert( end(), x); }

        • list, deque, vector
    • Odebrání jednotlivého prvku

void pop_front()

{ return erase( begin()); }

        • list, deque

void pop_back()

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

        • list, deque, vector
stl kontejnery9
STL – Kontejnery
  • Odvozené funkce kontejneru pro přístup k prvkům
    • Prvky na koncích
        • list, deque, vector
        • podmínka: ! empty()

T &front()

const T & front() const

      • obě provádějí

{ return * begin(); }

T &back()

const T & back() const

      • obě provádějí

{ auto it = end(); --it; return *it; }

        • [C++11]autoumožňuje deklaraci proměnné bez uvedení typu
        • typ si odvodí překladač z inicializace, v tomto případě K::[const_]iterator
stl kontejnery10
STL – Kontejnery
  • Odvozené funkce kontejneru pro přístup k prvkům
    • Prvky uprostřed
      • deque, vector, string
      • podmínka: n < size()
        • at: porušení podmínky vyvolá výjimku
        • operator[]: porušení podmínky způsobí nedefinované chování

T & at( int n)

T & operator[]( int n)

const T & at( int n) const

const T & operator[]( int n) const

      • všechny provádějí

return * ( begin() + n);

stl kontejnery12
STL - Kontejnery
  • Asociativní kontejnery
    • Uspořádané (samovyvažující se stromy)
      • set<T> - množina
      • multiset<T> - množina s opakováním
      • map<K,T> - asociativní pole, tj. parciální zobrazení K -> T
      • multimap<K,T> - relace s rychlým vyhledáváním podle klíče K
    • [C++11] Hashované
      • [C++11] unordered_set<T> - množina
      • [C++11] unordered_multiset<T> - množina s opakováním
      • [C++11] unordered_map<K,T> - asociativní pole, tj. parciální zobrazení K -> T
      • [C++11] unordered_multimap<K,T> - relace s rychlým vyhledáváním podle klíče K
      • pair<A,B> - pomocná šablona uspořádané dvojice
        • s položkami first, second
stl kontejnery13
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í
stl kontejnery14
STL - Kontejnery
  • Uspořádané kontejnery vyžadují uspořádání na klíčích
      • Vystačí si s operací <
    • V nejjednodušším případě to funguje samo

std::map< std::string, int> mapa;

    • Pokud typ uspořádání nemá, lze jej definovat obecně

bool operator<( const Klic & a, const Klic & b) { return ...; }

std::map< Klic, int> mapa;

    • Pokud obecná definice uspořádání nevyhovuje, lze definovat uspořádání funktorempouze 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));

stl kontejnery15
STL - Kontejnery
  • Uspořádání na klíčích – implementace
      • Knihovny definují funktor std::less< K>

template< typename K>

class less { public:

bool operator()( const K & a, const K & b) const { return a < b; }

};

      • Šablona kontejnerumá typový parametr - typ funktoru

template< typename K, typename T, typename L = std::less< K> >

class map {public:

      • Konstruktor kontejnerudostává hodnotu funktoru

explicit map( const L & c = L())

: cmp_( c)

{ /*...*/ }

/*...*/

private:

      • Kontejnerdrží jednu instanci funktoru

L cmp_;

      • Metody kontejneru volají operátor ()na instanci funktoru

iterator find_( /*...*/) { /*...*/ if ( cmp_( x, y) ) /*...*/ }

};

stl kontejnery16
STL - Kontejnery
  • Uspořádání na klíčích – hashující kontejnery
    • Kontejner vyžaduje funktory pro hashování a pro porovnání

template< typename K>

class hash { public:

std::size_t operator()( const K & a) const { /*...*/ }

};

template< typename K>

class equal_to { public:

bool operator()( const K & a, const K & b) const { return a == b; }

};

      • Šablona kontejnerumá dva další parametry

template< typename K, typename T,

typename H = std::hash< K>, typename E = std::equal_to< K> >

class unordered_map;

      • Konstruktor kontejnerudostává hodnoty funktorů

explicit unordered_map( std::size_t initial_bucket_count = /*...*/,

const H & h = L(), const E & e = E());

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

iterator set::find( T x)

iteratormultiset::find( T x)

iteratormap::find( K x)

iteratormultimap::find( K x)

iteratorunordered_set::find( T x)

iteratorunordered_multiset::find( T x)

iteratorunordered_map::find( K x)

iteratorunordered_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())
stl kontejnery19
STL - Kontejnery
  • Uspořádané kontejnery - intervalové dotazy

iterator set::lower_bound( T x)

iteratormultiset::lower_bound( T x)

iteratormap::lower_bound( K x)

iteratormultimap::lower_bound( K x)

      • vrací první prvek jehož klíč není menší než x, případně end()

iterator set::upper_bound( T x)

iteratormultiset::upper_bound( T x)

iteratormap::upper_bound( K x)

iteratormultimap::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
stl kontejnery20
STL - Kontejnery
  • Asociativní kontejnery – vyhledávání s opakováním

pair<iterator,iterator> multiset::equal_range( T x)

pair<iterator,iterator> multimap::equal_range( K x)

pair<iterator,iterator> unordered_multiset::equal_range( T x)

pair<iterator,iterator> unordered_multimap::equal_range( K x)

      • vrací polouzavřený interval [first,second) obsahující prvky s daným klíčem
        • pokud prvky neexistují, vrací prázdný interval na místě, kde by byly
        • odpovídá std::make_pair( lower_bound( x), upper_bound( x))
      • existuje i pro kontejnery bez opakování

pair<iterator,iterator> set::equal_range( T x)

pair<iterator,iterator> map::equal_range( K x)

pair<iterator,iterator> unordered_set::equal_range( T x)

pair<iterator,iterator> unordered_map::equal_range( K x)

    • složitost operace
      • uspořadné kontejnery: O( log( size()))
      • hashující kontejnery: průměrně O(1), nejhůře O( size())
stl kontejnery21
STL - Kontejnery
  • Asociativní kontejnery - vkládání
    • set a map

pair< iterator, bool> set::insert( T x)

pair< iterator, bool> unordered_set::insert( T x)

pair< iterator, bool> map::insert( pair< const K, T> x)

pair< iterator, bool> unordered_map::insert( pair< const K, T> x)

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

iterator multiset::insert( T x)

iterator unordered_multiset::insert( T x)

iterator multimap::insert( pair< const K, T> x)

iterator unordered_multimap::insert( pair< const K, T> x)

      • kopie x se vloží do kontejneru
      • vrací iterátor ukazující na vložený prvek
    • [C++11] existuje též move-verze insert a emplace
    • složitost operace
      • uspořadné kontejnery: O( log( size()))
      • hashující kontejnery: průměrně O(1), nejhůře O( size())
stl kontejnery23
STL - Kontejnery
  • Asociativní kontejnery - odebírání
    • podle klíče

size_type[unordered_][multi]set::erase( T x)

size_type[unordered_][multi]map::erase( K x)

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

void erase( iterator p)

      • odebere prvek na který odkazuje iterátor p
        • p nesmí být rovno end()
      • operace má konstantní složitost
        • rozumí se amortizovaná/průměrná složitost
    • podle iterátorů - interval

void erase( iteratorp, 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é
stl kontejnery25
STL - Kontejnery
  • map/unordered_map – at/operator [ ]

T & [unordered_]map::at( K x)

      • Vrátí referenci na hodnotovou (second) složku prvku s klíčem ekvivalentním x
      • Pokud takový prvek neexistuje, vyvolá výjimku

T & [unordered_]map::operator[]( K x)

{ return (*((insert(make_pair( x, T()))).first)).second; }

      • 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
stl algoritmy
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
stl algoritmy1
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
stl algoritmy2
STL – Algoritmy
  • Použití funkce for_each

void my_function( double & x)

{

x += 1;

}

void increment( list< double> & c)

{

for_each( c.begin(), c.end(), my_function);

}

  • [C++11] Lambda
    • Nová syntaktická konstrukce generující funktor

void increment( list< double> & c)

{

for_each( c.begin(), c.end(), []( double & x){ x += 1;});

}

stl algoritmy3
STL – Algoritmy
  • Použití funkce for_each

class my_functor {

public:

double v;

void operator()( double & x) const { x += v; }

my_functor( double p) : v( p) {}

};

void add( list< double> & c, double value)

{

for_each( c.begin(), c.end(), my_functor( value));

}

  • [C++11] Lambda

void add( list< double> & c, double value)

{

for_each( c.begin(), c.end(), [=]( double & x){ x += value;});

}

stl algoritmy4
STL – Algoritmy
  • Použití funkce for_each

class my_functor {

public:

double s;

void operator()( const double & x) { s += x; }

my_functor() : s( 0.0) {}

};

double sum( const list< double> & c)

{

my_functor f;

f = for_each( c.begin(), c.end(), f);

return f.s;

}

  • [C++11] Lambda

double sum( const list< double> & c)

{ double s = 0.0;

for_each( c.begin(), c.end(), [&]( const double & x){ s += x;});

return s;

}

stl algoritmy5
STL – Algoritmy
  • Použití funkce for_each

class my_functor {

public:

double s;

void operator()( const double & x)

{ s += x; }

my_functor() : s( 0.0) {}

};

double sum( const list< double> & c)

{

my_functor f;

for_each( c.begin(), c.end(), f);

return f.s;

}

      • Pozor: f se předává hodnotou - tato implementace vždy vrací 0.0
stl algoritmy6
STL – Algoritmy
  • Použití funkce for_each

class my_functor {

public:

double s;

void operator()( const double & x)

{ s += x; }

my_functor() : s( 0.0) {}

};

double sum( const list< double> & c)

{

return for_each( c.begin(), c.end(), my_functor()).s;

}

lambda v razy
Lambda výrazy
  • Motivace

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

lambda v razy1
Lambda výrazy
  • Lambda výraz

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

    • Deklaruje třídu ve tvaru

class ftor {

public:

ftor(TList ... plist) : vlist( plist) ... { }

rettype operator()(params ) const { body }

private:

TList ... vlist;

};

      • vlist je určen proměnnými použitými v body
      • TList je určen jejich typy a upraven podle capture
      • operator() je const pokud není uvedeno mutable
    • Lambda výraz je nahrazen vytvořením objektu

ftor( vlist ...)

C++11

lambda v razy n vratov typ a typ funkce
Lambda výrazy – návratový typ a typ funkce
  • Návratový typ operátoru
      • Explicitně definovaný návratový typ

[]()->int { … }

      • Automaticky určen pro tělo lambda funkce ve tvaru

[]() { return V; }

      • Jinak void

C++11

lambda v razy capture
Lambda výrazy – capture
  • Capture

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

      • Způsob zpřístupnění vnějších entit
      • Určuje typy datových položek a konstruktoru funktoru
    • Explicitní capture
      • Programátor vyjmenuje všechny vnější entity v capture

[a,&b,c,&d]

        • entity označené &předány odkazem, ostatní hodnotou
    • Implicitní capture
      • Překladač sám určí vnější entity, capture určuje způsob předání

[=]

[=,&b,&d]

        • předání hodnotou, vyjmenované výjimky odkazem

[&]

[&,a,c]

        • předání odkazem, vyjmenované výjimky hodnotou

C++11

lambda v razy p kl ad
Lambda výrazy – příklad

int a = 1, b = 1, c = 1;

auto m1 = [a, &b, &c]() mutable {

auto m2 = [a, b, &c]() mutable {

std::cout << a << b << c;

a = 4; b = 4; c = 4;

};

a = 3; b = 3; c = 3;

m2();

};

a = 2; b = 2; c = 2;

m1();

std::cout << a << b << c;

  • Co to vypíše?

C++11

123234

stl algoritmy7
STL – Algoritmy
  • Šablona funkce find
      • <algorithm>

template<class InputIterator, class T>

InputIteratorfind(

InputIterator first,

InputIterator last,

const T & value)

{

for (; first != last; ++first)

if ( * first == value )

break;

return first;

}

stl algoritmy8
STL – Algoritmy
  • Šablona funkce find_if
      • <algorithm>

template<class InputIterator, class Predicate>

InputIteratorfind_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
stl algoritmy9
STL – Algoritmy
  • Př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
stl algoritmy10
STL – Algoritmy
  • Přehled algoritmů
    • Swap
      • swap - výměna obsahu dvou objektů
        • Pomocí parciální/explicitní specializace bývá implementována rychleji, než kopírování
    • Modifikace kontejnerů výměnou prvků
      • swap_ranges - výměna obsahu dvou sekvencí (volá swap)
      • iter_swap - výměna obsahu dvou jednoprvkových sekvencí
    • Modifikace kontejnerů permutací (voláním swap)
      • partition, stable_partition - přemístění prvků s danou vlastností dopředu
      • random_shuffle - náhodná permutace dle zadaného náhodného generátoru
      • reverse - otočení posloupnosti
      • rotate, rotate_copy - rotace prvků
stl algoritmy11
STL – Algoritmy
  • Přehled algoritmů
    • Modifikace kontejnerů přiřazením
      • copy, copy_backward - kopie první sekvence do druhé
      • transform - aplikace zadané unární/binární operace na každý prvek první/první a druhé sekvence a zapsání výsledku do druhé/třetí sekvence
      • replace, replace_if - nahrazení prvků s danou vlastností jinou hodnotou
      • replace_copy, replace_copy_if - kopie s nahrazením
      • fill, fill_n - naplnění sekvence danou hodnotou
      • generate, generate_n - naplnění sekvence z daného generátoru
    • Modifikace kontejnerů odebráním
      • remove, remove_if - smazání prvků s danou vlastností
      • unique, unique_copy - smazání opakujících se sousedních prvků
        • vhodné pro setříděné nebo asociativní kontejnery
    • Pozor: Tyto funkce neprodlužují ani nezkracují kontejner
stl algoritmy12
STL – Algoritmy
  • Přehled algoritmů
    • Pozor: Algoritmy neprodlužují ani nezkracují kontejner

vector< int> a, b;

a.push_back( 1); a.push_back( 2); a.push_back( 3);

copy( a.begin(), a.end(), b.begin()); // ilegální použití

    • Pro tyto účely existují "vkládající iterátory"
      • <iterator> obsahuje tyto funkce vracející iterátory
        • back_inserter( K) - iterátor vkládající na konec kontejneru K
        • front_inserter( K) - iterátor vkládající na začátek kontejneru K
        • inserter( K, I) - iterátor vkládající před iterátor I do kontejneru K
      • tyto iterátory jsou pouze výstupní
        • lze je použít jako cíl ve funkcích typu copy

copy( a.begin(), a.end(), back_inserter( b));

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

Konstruktory a destruktory

Constructors and Destructors

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

copy move

copy/move

Speciální metody tříd – C++11

copy move1
copy/move
  • Speciální metody tříd
    • Copy constructor

T( const T & x);

    • Move constructor

T(T && x);

    • Copy assignment operator

T & operator=( const T & x);

    • Move assignment operator

T & operator=( T && x);

C++11

copy move2
copy/move
  • Překladačem definované chování (default)
    • Copy constructor

T( const T & x)= default;

      • aplikuje copy constructor na složky
    • Move constructor

T(T && x) = default;

      • aplikuje move constructor na složky
    • Copy assignment operator

T & operator=( const T & x) = default;

      • aplikuje copy assignment operator na složky
    • Move assignment operator

T & operator=( T && x)= default;

      • aplikuje move assignment operator na složky
    • default umožňuje vynutit defaultní chování

C++11

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

C++11

copy move4
copy/move
  • Nejběžnější kombinace
    • Neškodná třída
      • Nedeklaruje žádnou copy/move metodu ani destruktor
      • Neobsahuje složky vyžadující zvláštní péči (ukazatele)
    • Složky vyžadující zvláštní péči
      • Překladačem generované chování (default) nevyhovuje
      • Bez podpory move (před C++11)

T( const T & x);

T & operator=( const T & x);

~T();

      • Plná podpora copy/move

T( const T & x);

T( T && x);

T & operator=( const T & x);

T & operator=( T && x);

~T();

C++11

copy move5
copy/move
  • Další kombinace
    • Nekopírovatelná třída
      • Např. dynamicky alokované živé objekty v simulacích

T( const T & x)= delete;

T & operator=( const T & x) = delete;

      • delete zakazuje generování překladačem
      • Destruktor může ale nemusí být nutný
    • Přesouvatelná nekopírovatelná třída
      • Např. unikátní vlastník jiného objektu (viz std::unique_ptr< U>)

T( T && x);

T & operator=( T && x);

~T();

      • Pravidla jazyka zakazují generování copy metod překladačem
      • Destruktor typicky bývá nutný

C++11

speci ln metody t d1
Speciální metody tříd
  • Konverzní konstruktory

class XXX {

XXX( YYY);

};

      • Zobecnění kopírovacího konstruktoru
      • Definuje uživatelskou konverzi typu YYY na XXX
      • Je-li tento speciální efekt nežádoucí, lze jej zrušit:

explicit XXX( YYY);

  • Konverzní operátory

class XXX {

operator YYY() const;

};

      • Definuje uživatelskou konverzi typu XXX na YYY
      • Vrací typ YYY hodnotou (tedy s použitím kopírovacího konstruktoru YYY, pokud je YYY třída)
      • Kompilátor vždy použije nejvýše jednu uživatelskou konverzi
konstruktory a spol

Konstruktory a spol.

Typické situace

konstruktory a spol1
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_;

};

konstruktory a spol2
Konstruktory a spol.
  • Všechny položky jsou neškodné
    • Položky mají svoje konstruktory
    • Třída nemusí mít žádný konstruktor

class T {

public:

// ...

const std::string & get_x() const { return x_; }

void set_x( const std::string & s) { x_ = s; }

private:

std::string x_;

};

konstruktory a spol3
Konstruktory a spol.
  • Všechny položky jsou neškodné
    • Položky mají svoje konstruktory
    • Konstruktor se hodí pro pohodlnou inicializaci
      • V takovém případě je (většinou) nutný i konstruktor bez parametrů
      • Konstruktory s jedním parametrem označeny explicit

class T {

public:

T() {}

explicit T( const std::string & s) : x_( s) {}

T( const std::string & s, const std::string & t)

: x_( s), y_( t)

{}

// ... metody ...

private:

std::string x_, y_;

};

konstruktory a spol4
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 jenutný 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_;

};

konstruktory a spol5
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_;

};

konstruktory a spol6
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

konstruktory a spol7
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

konstruktory a spol8
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

konstruktory a spol9
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

konstruktory a spol10
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

konstruktory a spol11
Konstruktory a spol.
  • Abstraktní třída
    • Se podporouklonová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

vznik a z nik objekt1
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; // konstruktorbezparametrů
  • 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
  • }
vznik a z nik objekt2
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 &)
  • }
vznik a z nik objekt3
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
vznik a z nik objekt4
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;
  • }
vznik a z nik objekt5
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;
vznik a z nik objekt6
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;
vznik a z nik objekt7
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
d di nost
Dědičnost

class Base { /* ... */ };

class Derived : public Base { /* ... */ }

  • Třída Derived je odvozena z třídy Base
    • Obsahuje všechny datové položky i metody třídy Base
    • Může k nim doplnit další
      • Není vhodné novými zakrývat staré, vyjma virtuálních
    • Může změnit chování metod, které jsou v Base deklarovány jako virtuální

class Base {

virtual void f() { /* ... */ }

};

class Derived : public Base {

virtual void f() { /* ... */ }

};

virtu ln funkce
Virtuální funkce

class Base {

virtual void f() { /* ... */ }

virtual void f( int) { /* ... */ }

virtual void g() = 0; // čistě virtuální funkce bez těla

virtual void h() const { /* ... */ }

void j() { /* ... */ }

};

  • Třída obsahující čistě virtuální funkci nemůže být samostatně instanciována.

class Derived : public Base {

virtual void f() { /* ... */ }

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
virtu ln funkce1
Virtuální funkce

class 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 objektu

b.f(); // volá Base::f ikdyž je virtuální

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

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

nespr vn u it d di nosti
Nesprávné užití dědičnosti
  • Nesprá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:

doubleabs( 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 &
nespr vn u it d di nosti1
Nesprávné užití dědičnosti
  • Nesprá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

doubleabs( Realp) { 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 &
nespr vn u it d di nosti2
Nesprávné užití dědičnosti
  • Nesprá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
nespr vn u it d di nosti3
Nesprávné užití dědičnosti
  • Nesprá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 &
nespr vn u it d di nosti4
Nesprávné užití dědičnosti
  • Nesprá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í se

Real & Real::operator=( const Real &);// nesouhlasí typ argumentu

ablony

Šablony

Templates

ablony t d definice
Šablony tříd - definice
  • Šablona je generická třída parametrizovaná libovolným počtem formálních parametrů těchto druhů:
    • celé číslo – uvnitř šablony se chová jako konstanta, použitelná jako meze polí
    • ukazatel libovolného typu
    • libovolný typ – deklarováno zápisem class T nebo typename T, identifikátor formálního parametru se chová jako identifikátor typu, použitelný uvnitř šablony v libovolné deklaraci
    • šablona třídy s definovanými formálními parametry
    • seznam typů ("variadic template")
  • Prefix definice šablony

template< formální-parametry>

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

C++11

ablony t d instanciace
Šablony tříd - instanciace
  • Instanciace šablony: Šablonu lze použít jako typ pouze s explicitním uvedením skutečných parametrů odpovídajících druhů:
    • celé číslo: celočíselný konstantní výraz
    • ukazatel: adresa globální nebo statické proměnné či funkce kompatibilního typu
    • libovolný typ – jméno typu či typová konstrukce (včetně jiné instanciované šablony)
    • šablona s odpovídajícími formálními parametry
  • Užití instanciované šablony:
    • Instanciované šablony jsou stejného typu, pokud jsou stejného jména a jejich skutečné parametry obsahují stejné hodnoty konstantních výrazů, adresy stejných proměnných či funkcí a stejné typy
ablony t d z visl typy
Šablony tříd – závislé typy
  • Šablony tříd (včetně těl metod) se při deklaraci kontrolují pouze syntakticky
      • Některé překladače nedělají ani to
    • Překladač potřebuje odlišit jména typů od ostatních jmen
      • U jmen ve tvaru A::B to překladač někdy nedokáže
      • Programátor musí pomoci klíčovým slovem typename

template< typename T> class X

{

typedef typename T::B U; // T::B je typ

typename U::D p; // T::B::D je typ

typename Y<T>::C q; // Y<T>::C je typ

void f() { T::D(); } // T::D není typ

}

      • typename je nutné uvést před jmény typu ve tvaru A::B, kde A je závislé jméno
      • Závislé jméno je jméno obsahující přímo či nepřímo parametr šablony
ablony t d this
Šablony tříd - this
  • Pokud je mezi předkem třídy závislé jméno
    • překladač pak neví, které identifikátory jsou zděděny
    • uživatel musí pomoci konstrukcí this->

template< typename T> class X

: public T

{

void f() { return this->a; }

}

ablony funkc
Š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říd

template< typename T, int k> // parametry šablony

int f( T * p, int q); // parametry funkce

template< typename T, typename U> // parametry šablony

int g( T * p, vector< U> q); // parametry funkce

  • Šablony funkcí lze volat dvěma způsoby
    • Explicitně

f< int, 729>( a, b)

    • Automaticky

g( a, b)

      • Překladač dopočte parametry šablony z typů parametrů funkce
      • Všechny formální argumenty šablony by měly být užity v typech formálních parametrů funkce
ablony funkc1
Š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> voidswap( T & a, T & b)

{ T tmp(a); a = b; b = tmp; };

      • K tomu řada chytřejších implementací swap pro některé třídy
ablony pokro il konstrukce jazyka
Šablony – pokročilé konstrukce jazyka
  • Parciální specializace
      • Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice

template< int n> class Array< n, bool>

{ /* specializace pro pole typu bool */ };

      • Krajním případem parciální specializace je explicitní specializace
  • Explicitní specializace

template<> class Array< 32, bool> { /* ... */ };

      • U šablon funkcí nahrazena obyčejnou funkcí
  • Explicitní instanciace
      • Překladač je možné donutit ke kompletní instanciaci šablony

template class Array< 128, char>;

explicitn specializace
Explicitní specializace
  • Příklad

template< int N>

struct Fib {

enum { value = Fib< N-1>::value + Fib< N-2>::value };

};

template<>struct Fib< 0> {

enum { value = 1 };

};

template<>struct Fib< 1> {

enum { value = 1 };

};

  • Kontrolní otázka:
      • Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::value
teoretick pohled na ablony
Teoretický pohled na šablony
  • Jiný příklad

template< int N>

struct Fib {

enum { value = Fib< N-1>::value + Fib< N-2>::value };

};

template<>struct Fib< 0> {

enum { value = 1 };

};

template<>struct Fib< 1> {

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
parci ln specializace
Parciální specializace
  • Parciální specializace
      • Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice
        • Parciálně specializovat lze šablony funkcí, celé šablony tříd i jednotlivě těla jejich metod
        • Obsah specializace šablony třídy (teoreticky) nemusí nijak souviset se základní definicí - může mít zcela jiné položky, předky apod.
        • Základní definice dokonce nemusí vůbec existovat (ale musí být deklarována)

template< class X, class Y> class C;// základní deklarace

template< class P, class Q> class C< P *, Q *> {// specializace

bool cmp( P *, Q *);

};

template< class Z> class C< Z, Z> : public Z {// jiná specializace

bool set( Z &);

};

template< class X, class Y> class C { // základní definice

Xadd( Y);

};

parci ln specializace1
Parciální specializace
  • Parciální specializace
      • Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice
        • Parciální specializace může mít stejný, menší i větší počet formálních parametrů než základní definice, jejich hodnoty se odvozují ze skutečných parametrů šablony (kterých je vždy tolik, kolik určuje základní definice)

template< class T, class U, int n> class C< T[n], U[n]>

{ /* specializace pro dvě pole stejné velikosti */ };

parci ln specializace2
Parciální specializace
  • Parciální specializace
      • Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice
        • Parciální specializace může mít stejný, menší i větší počet formálních parametrů než základní definice, jejich hodnoty se odvozují ze skutečných parametrů šablony (kterých je vždy tolik, kolik určuje základní definice)

template< class T, class U, int n> class C< T[n], U[n]>

{ /* specializace pro dvě pole stejné velikosti */ };

      • Krajním případem parciální specializace je explicitní specializace
  • Explicitní specializace

template<> class C< char, int[ 8]> { /* ... */ };

      • Explicitní specializace šablony není šablona
      • Podléhá trochu jiným (jednodušším) pravidlům
        • Překlad se neodkládá
        • Těla metod se nepíší do hlavičkových souborů
parci ln specializace3
Parciální specializace
  • Typická použití parciální a explicitní specializace
    • Výhodnější implementace ve speciálních případech
      • Šablona je používána přímo z nešablonovaného kódu
      • Programátor - uživatel šablony o specializaci nemusí vědět
      • Příklad: Implementace vector<char> může být jednodušší
parci ln specializace4
Parciální specializace
  • Typická použití parciální a explicitní specializace
    • Výhodnější implementace ve speciálních případech
      • Šablona je používána přímo z nešablonovaného kódu
      • Programátor - uživatel šablony o specializaci nemusí vědět
      • Příklad: Implementace vector<char> může být jednodušší
    • Mírná změna rozhraní ve speciálních případech
      • Š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
parci ln specializace5
Parciální specializace
  • Typická použití parciální a explicitní specializace
    • Výhodnější implementace ve speciálních případech
      • Šablona je používána přímo z nešablonovaného kódu
      • Programátor - uživatel šablony o specializaci nemusí vědět
      • Příklad: Implementace vector<char> může být jednodušší
    • Mírná změna rozhraní ve speciálních případech
      • Š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
parci ln specializace6
Parciální specializace
  • Modifikace chování jiné šablony
      • Specializovaná šablona je volána z jiného šablonovaného kódu
      • Autor volající šablony předpokládá, že ke specializaci dojde
        • Někdy dokonce ani nedefinuje základní obsah volané šablony
      • Autor specializace tak upravuje chování volající šablony
      • Příklad: šablona basic_string<T> volášablonu char_traits<T>, ve které je např. definována porovnávací funkce

template< class T> struct char_traits;

template< class T> class basic_string { /* ... */

int compare( const basic_string & b) const

{ /*...*/ char_traits< T>::compare( /* ... */) /*...*/ }

};

template<> struct char_traits< char> { /* ... */

static int compare(const char* s1, const char* s2, size_t n)

{ return memcmp( s1, s2, n); }

};

parci ln specializace7
Parciální specializace
  • Modifikace chování jiné šablony
      • Specializovaná šablona je volána z jiného šablonovaného kódu
      • Autor volající šablony předpokládá, že ke specializaci dojde
        • Někdy dokonce ani nedefinuje základní obsah volané šablony
      • Autor specializace tak upravuje chování volající šablony
  • Traits
    • Šablony, ze kterých nejsou vytvářeny objekty
    • Obsahují pouze:
      • Definice typů
      • Statické funkce
    • Určeny k doplnění informací o nějakém typu
      • Příklad: char_traits<T> doplňuje informace o typu T, např. porovnávací funkci
traits policies
Traits & policies
  • Traits
    • Šablony, ze kterých nejsou vytvářeny objekty
    • Obsahují pouze:
      • Definice typů
      • Statické funkce
    • Určeny k doplnění informací o nějakém typu
      • Příklad: char_traits<T> doplňuje informace o typu T, např. porovnávací funkci
  • Policy classes
    • Třídy, ze kterých obvykle nejsou vytvářeny objekty
    • Předávány jako parametr šablonám
      • Defaultní hodnotou parametru často bývá šablona traits
    • Určeny k definování určitého chování
      • Příklad: Alokační strategie
triky s ablonami
Triky s šablonami
  • Porovnání typů s booleovským výstupem

template< class A, class B>

struct Equal {

enum { value = false };

};

template< class A>

struct Equal< A, A> {

enum { value = true };

};

      • Equal< X, Y>::value je konstantní výraz
    • Použití

template< class T1>

class Test {

enum { T1_is_int = Equal< int, T1>::value};

enum { T1_is_long = Equal< long, T1>::value};

/* ... */

};

triky s ablonami1
Triky s šablonami
  • Porovnání typů s typovým výstupem

template< class A, class B, class C, class D>

struct IfEqual {

typedef D Result;

};

template< class A, class C, class D>

struct Equal< A, A, C, D> {

typedef C Result;

};

      • IfEqual< X, Y, U, V>::Result je typ
        • Význam: X == Y ? U : V
      • Použití

template< class T1>

class Test {

typedef Equal< T1, unsigned, unsigned long, long>::Result longT1;

/* ... */

};

triky s ablonami2
Triky s šablonami
  • Kompilač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 };

};

triky s ablonami3
Triky s šablonami
  • Kompilač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]
teoretick pohled na ablony1
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 };

};

teoretick pohled na ablony2
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];

teoretick pohled na ablony3
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
teoretick pohled na ablony4
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
ablony t d pravidla pou it
Š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 variantu

template< typename T> class X {

X< T>( const X< T> &);

};

ablony t d pravidla pou it1
Š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.
ablony t d pravidla pou it2
Šablony tříd – pravidla použití
    • Šablona třídy se překládá až v okamžiku instanciace, tj. použití s konkrétními parametry
    • Překladač instanciuje (tj. překládá) pouze ty metody, které jsou zapotřebí (tj. jsou volány nebo jsou virtuální)
      • Některá těla metod tedy nemusí být pro některé případy parametrů přeložitelná

template< typename Container> class Proxy {

public:

void pop_front() { c->pop_front(); } // jen pro list/deque

/* ... */

private:

Container * c;

};

  • Explicitní instanciace
      • Překladač je možné donutit ke kompletní instanciaci šablony

template class Array< 128, char>;

ablony t d triky
Š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

{ /* ... */

};

c ast

Cast

Různé druhy přetypování

p etypov n
Přetypování
  • Různé varianty syntaxe
    • C-style cast

(T)e

      • Převzato z C
p etypov n1
Přetypování
  • Různé varianty syntaxe
    • C-style cast

(T)e

      • Převzato z C
    • Function-style cast

T(e)

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

(T)e

      • Převzato z C
    • Function-style cast

T(e)

      • Ekvivalentní (T)e
        • T musí být syntakticky identifikátor nebo klíčové slovo označující typ
    • Type conversion operators
      • Pro odlišení účelu (síly a nebezpečnosti) přetypování:

const_cast<T>(e)

static_cast<T>(e)

reinterpret_cast<T>(e)

p etypov n3
Přetypování
  • Různé varianty syntaxe
    • C-style cast

(T)e

      • Převzato z C
    • Function-style cast

T(e)

      • Ekvivalentní (T)e
        • T musí být syntakticky identifikátor nebo klíčové slovo označující typ
    • Type conversion operators
      • Pro odlišení účelu (síly a nebezpečnosti) přetypování:

const_cast<T>(e)

static_cast<T>(e)

reinterpret_cast<T>(e)

      • Novinka - přetypování s běhovou kontrolou:

dynamic_cast<T>(e)

p etypov n4
Přetypování

const_cast<T>(e)

  • Odstranění konstantnosti
    • const U & => U &
    • const U * => U *
  • Obvykle používáno pro měnění pomocných položek v logicky konstantních objektech
    • Příklad: Čítač odkazů na logicky konstantní objekt

class Data {

public:

void register_pointer() const

{ const_cast< Data *>( this)->references++; }

private:

/* ... data ... */

int references;

};

    • Jiný příklad: datové struktury s amortizovaným vyhledáváním
p etypov n5
Přetypování

const_cast<T>(e)

  • Odstranění konstantnosti
    • const U & => U &
    • const U * => U *
  • U moderních překladačů lze nahradit specifikátorem mutable
    • Příklad: Čítač odkazů na logicky konstantní objekt

class Data {

public:

void register_pointer() const

{ references++; }

private:

/* ... data ... */

mutableint references;

};

p etypov n6
Přetypování

static_cast<T>(e)

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

static_cast<T>(e)

  • Umožňuje
    • Všechny implicitní konverze
      • Ekvivalentní použití pomocné proměnné tmp deklarované takto:

T tmp(e);

      • Používá se tehdy, pokud se vynucením jedné z možných implicitních konverzí odstraní nejednoznačnost nebo vynutí volání jiné varianty funkce

class A { /* ... */ }; class B { /* ... */ };

void f( A *); void f( B*);

class C : public A, public B { /* ... */

void g() { f( static_cast< A>( this)); }

};

p etypov n8
Přetypování

static_cast<T>(e)

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

}

p etypov n10
Přetypování

reinterpret_cast<T>(e)

  • Umožňuje
    • Konverze ukazatele na dostatečně velké celé číslo
    • Konverze celého čísla na ukazatel
    • Konverze mezi různými ukazateli na funkce
    • Konverze odkazu na odkaz na libovolný jiný typ
        • U* => V *
        • U & => U &
      • Neuvažuje příbuzenské vztahy tříd, neopravuje hodnoty ukazatelů
  • Většina použití je závislá na platformě
      • Příklad: Přístup k reálné proměnné po bajtech
      • Typické použití: Čtení a zápis binárních souborů

void put_double( std::ostream & o, const double & d)

{ o.write( reinterpret_cast< char *>( & d), sizeof( double)); }

        • Obsah souboru je nepřenositelný
p etypov n11
Přetypování

dynamic_cast<T>(e)

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

exception handling

Exception handling

Mechanismus výjimek

exception handling1
Srovnání: goto

Start: příkaz goto

Cíl: návěští

Určen při kompilaci

Skok může opustit blok

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

}

Exception handling
exception handling2
Srovnání: goto

Start: příkaz goto

Cíl: návěští

Určen při kompilaci

Skok může opustit blok

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

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

Srovnání 2: <csetjmp>

Pro pokročilé

Start: volání longjmp

Cíl: volání setjmp

Skok může opustit proceduru

Neřeší lokální proměnné

Nelze použít v C++

Předává hodnotu typu int

int f()

{

if ( something == wrong )

{

goto label;

}

else

{

MyClass my_variable;

if ( anything != good )

{

goto label;

}

/* ... */

}

return 0;

label:

std::cerr

<< "Error"

<< std::endl;

return -1;

}

Exception handling
exception handling3
Mechanismus výjimek

Start: příkaz throw

Cíl: try-catch blok

Určen za běhu

Skok může opustit proceduru

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

}

}

Exception handling
exception handling4
Mechanismus výjimek

Start: příkaz throw

Cíl: try-catch blok

Určen za běhu

Skok může opustit proceduru

Proměnné korektně 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 ) {

/*...*/

}

}

Exception handling
exception handling5
Mechanismus výjimek

Start: příkaz throw

Cíl: try-catch blok

Určen za běhu

Skok může opustit proceduru

Proměnné korektně 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 ) {

/*...*/

}

}

Exception handling
exception handling6
Mechanismus výjimek

Start: příkaz throw

Cíl: try-catch blok

Určen za běhu

Skok může opustit proceduru

Proměnné korektně 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 &) {

/*...*/

}

}

Exception handling
exception handling7
Mechanismus výjimek

Start: příkaz throw

Cíl: try-catch blok

Určen za běhu

Skok může opustit proceduru

Proměnné korektně 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 (...) {

/*...*/

}

}

Exception handling
exception handling8
Exception handling
  • Fáze zpracování výjimky
    • Vyhodnocení výrazu v příkaze throw
      • Hodnota je uložena "stranou"
    • Stack-unwinding
      • Postupně se opouštějí bloky a funkce, ve kterých bylo provádění vnořeno
      • Na zanikající lokální a pomocné proměnné jsou volány destruktory
      • Stack-unwinding končí dosažením try-bloku, za kterým je catch-blok odpovídající typu výrazu v příkaze throw
    • Provedení kódu v catch-bloku
      • Původní hodnota throw je stále uložena pro případné pokračování:
        • Příkaz throw bez výrazu pokračuje ve zpracování téže výjimky počínaje dalším catch-blokem - začíná znovu stack-unwinding
    • Zpracování definitivně končí opuštěním catch-bloku
      • Běžným způsobem nebo příkazy return, break, continue, goto
        • Nebo vyvoláním jiné výjimky
exception handling9
Exception handling
  • Použití mechanismu výjimek
    • Vyvolání a zpracování výjimky je relativně časově náročné
      • Používat pouze pro chybové nebo řídké stavy
        • Např. nedostatek paměti, ztráta spojení, chybný vstup, konec souboru
    • Připravenost na výjimky také něco (málo) stojí
      • Za normálního běhu je třeba zařídit, aby výjimka dokázala najít cíl a zrušit proměnné
        • Výjimky se týkají i procedur, ve kterých není ani throw, ani try-blok
      • Většina kompilátorů umí překládat ve dvou režimech "s" a "bez"
        • Celý spojovaný program musí být přeložen stejně
exception handling10
Exception handling
  • Standardní výjimky
    • <stdexcept>
    • Všechny standardní výjimky jsou potomky třídy exception
      • metoda what() vrací řetězec s chybovým hlášením
    • bad_alloc: vyvolává operátor new při nedostatku paměti
      • V režimu "bez výjimek" new vrací nulový ukazatel
    • bad_cast, bad_typeid: Chybné použití RTTI
    • Odvozené z třídy logic_error:
      • domain_error, invalid_argument, length_error, out_of_range
      • vyvolávány např. funkcí vector::operator[]
    • Odvozené z třídy runtime_error:
      • range_error, overflow_error, underflow_error
exception handling11
Exception handling
  • Standardní výjimky
    • <stdexcept>
    • Všechny standardní výjimky jsou potomky třídy exception
      • metoda what() vrací řetězec s chybovým hlášením
    • bad_alloc: vyvolává operátor new při nedostatku paměti
      • V režimu "bez výjimek" new vrací nulový ukazatel
    • bad_cast, bad_typeid: Chybné použití RTTI
    • Odvozené z třídy logic_error:
      • domain_error, invalid_argument, length_error, out_of_range
      • vyvolávány např. funkcí vector::operator[]
    • Odvozené z třídy runtime_error:
      • range_error, overflow_error, underflow_error
    • Aritmetické ani ukazatelové operátory na vestavěných typech NEHLÁSÍ běhové chyby prostřednictvím výjimek
      • např. dělení nulou nebo dereference nulového ukazatele
exception handling12
Typické použití

Deklarace výjimek

class AnyException{ /*...*/ };

class WrongException

: public AnyException { /*...*/ };

class BadException

: public AnyException { /*...*/ };

Vyvolání výjimek

void f()

{

if ( something == wrong )

throw WrongException();

if ( anything != good )

throw BadException();

}

Částečné ošetření

void g()

{ try {

f();

}

catch (...) {

std::cout << "Exception in g()";

throw;

}

}

Podrobné ošetření

int main( /*...*/)

{ try {

g();

h();

}

catch ( WrongException ) {

std::cout << "WrongException";

}

catch ( BadException ) {

std::cout << "BadException";

}

}

Exception handling
exception handling13
Použití se std::exception

Deklarace výjimek

class 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ýjimek

void f()

{

if ( something == wrong )

throw WrongException();

if ( anything != good )

throw BadException();

}

Částečné ošetření

void g()

{ try {

f();

}

catch (...) {

std::cout << "Exception in g()";

throw;

}

}

Podrobné ošetření

int main( /*...*/)

{ try {

g();

h();

}

catch ( const std::exception & e ) {

std::cout << e.what();

}

}

Exception handling
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

Exception-safe programming
exception safe programming1
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

Exception-safe programming
exception safe programming2
Exception-safe programming
  • Pravidla vynucená jazykem
    • Destruktor nesmí skončit vyvoláním výjimky
      • Výjimka může být vyvolána uvnitř, ale musí být zachycena nejpozději uvnitř destruktoru
    • Zdůvodnění:
      • V rámci ošetření výjimek (ve fázi stack-unwinding) se volají destruktory lokálních proměnných
      • Výjimku zde vyvolanou nelze z technických i logických důvodů ošetřit (ztratila by se původní výjimka)
      • Nastane-li taková výjimka, volá se funkce terminate() a program končí
exception safe programming3
Exception-safe programming
  • Pravidla vynucená jazykem
    • Destruktor nesmí skončit vyvoláním výjimky
      • Výjimka může být vyvolána uvnitř, ale musí být zachycena nejpozději uvnitř destruktoru
    • Toto pravidlo jazyka sice platí pouze pro destruktory lokálních proměnných
      • A z jiných důvodů též pro globální proměnné
    • Je však vhodné je dodržovat vždy
      • Bezpečnostní zdůvodnění: Destruktory lokálních proměnných často volají jiné destruktory
      • Logické zdůvodnění: Nesmrtelné objekty nechceme
exception safe programming4
Exception-safe programming
  • Pravidla vynucená jazykem
    • Destruktor nesmí skončit vyvoláním výjimky
    • Konstruktor globálního objektu nesmí skončit vyvoláním výjimky
      • Zdůvodnění: Není místo, kde ji zachytit
      • Stane-li se to, volá se terminate() a program končí
      • Jiné konstruktory ale výjimky volat mohou (a bývá to vhodné)
exception safe programming5
Exception-safe programming
  • Pravidla vynucená jazykem
    • Destruktor nesmí skončit vyvoláním výjimky
    • Konstruktor globálního objektu nesmí skončit vyvoláním výjimky
    • Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky
      • Zdůvodnění: Catch blok by nebylo možné vyvolat
      • Stane-li se to, volá se terminate() a program končí
      • Jiné copy-constructory ale výjimky volat mohou (a bývá to vhodné)
exception safe programming6
Exception-safe programming
  • Pravidla vynucená jazykem
    • Destruktor nesmí skončit vyvoláním výjimky
    • Konstruktor globálního objektu nesmí skončit vyvoláním výjimky
    • Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky
exception safe programming7
Exception-safe programming
  • Poznámka: Výjimky při zpracování výjimky
    • Výjimka při výpočtu výrazu v throw příkaze
      • Tento throw příkaz nebude vyvolán
    • Výjimka v destruktoru při stack-unwinding
      • Povolena, pokud neopustí destruktor
      • Po zachycení a normálním ukončení destruktoru se pokračuje v původní výjimce
    • Výjimka uvnitř catch-bloku
      • Pokud je zachycena uvnitř, ošetření původní výjimky může dále pokračovat (přikazem throw bez výrazu)
      • Pokud není zachycena, namísto původní výjimky se pokračuje ošetřováním nové
exception safe programming8
Exception-safe programming
  • Kompilátory samy ošetřují některé výjimky
    • Dynamická alokace polí
      • Dojde-li k výjimce v konstruktoru některého prvku, úspěšně zkonstruované prvky budou destruovány
        • Ve zpracování výjimky se poté pokračuje
exception safe programming9
Exception-safe programming
  • Kompilátory samy ošetřují některé výjimky
    • Dynamická alokace polí
      • Dojde-li k výjimce v konstruktoru některého prvku, úspěšně zkonstruované prvky budou destruovány
        • Ve zpracování výjimky se poté pokračuje
    • Výjimka v konstruktoru součásti (prvku nebo předka) třídy
      • Sousední, již zkonstruované součásti, budou destruovány
      • 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;
exception safe programming10
Exception-safe programming
  • Definice
    • (Weak) exception safety
      • Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v případě výjimky zanechá veškerá data v konzistentním stavu
      • Konzistentní stav znamená zejména:
        • Nedostupná data byla korektně destruována a odalokována
        • Ukazatele nemíří na odalokovaná data
        • Platí další invarianty dané logikou aplikace
exception safe programming11
Exception-safe programming
  • Definice
    • (Weak) exception safety
      • Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v případě výjimky zanechá veškerá data v konzistentním stavu
      • Konzistentní stav znamená zejména:
        • Nedostupná data byla korektně destruována a odalokována
        • Ukazatele nemíří na odalokovaná data
        • Platí další invarianty dané logikou aplikace
    • Strong exception safety
      • Funkce je silně bezpečná, pokud v případě, že skončí vyvoláním výjimky, zanechá data ve stejném stavu, ve kterém byla při jejím vyvolání
      • Nazýváno též "Commit-or-rollback semantics"
exception safe programming12
Exception-safe programming
  • Poznámky
    • (Weak) exception safety
      • Tohoto stupně bezpečnosti lze většinou dosáhnout
      • Stačí vhodně definovat nějaký konzistentní stav, kterého lze vždy dosáhnout, a ošetřit pomocí něj všechny výjimky
        • Konzistentním stavem může být třeba nulovost všech položek
        • Je nutné upravit všechny funkce tak, aby je tento konzistentní stav nepřekvapil (mohou na něj ale reagovat výjimkou)
    • Strong exception safety
      • Silné bezpečnosti nemusí jít vůbec dosáhnout, pokud je rozhraní funkce navrženo špatně
      • Obvykle jsou problémy s funkcemi s dvojím efektem
        • Příklad: funkce pop vracející odebranou hodnotu
exception safe programming13
Příklad: String č. 2

operator=

Nebezpečná implementace:

Pokud new char způsobí výjimku, operátor= zanechá v položce str_ původní ukazatel, který již míří na dealokovaný blok

class String {

public:

// ...

private:

char * str_;

};

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Exception-safe programming
exception safe programming14
Příklad: String č. 2

operator=

Nebezpečná implementace:

Pokud new char způsobí výjimku, operátor= zanechá v položce str_ původní ukazatel, který již míří na dealokovaný blok

K jiné výjimce zde dojít nemůže:

std::operator delete výjimky nikdy nevyvolává

char je vestavěný typ a nemá tedy konstruktory které by mohly výjimku vyvolávat

strlen a strcpy jsou C-funkce

Parametry a návratová hodnota se předávají odkazem

class String {

public:

// ...

private:

char * str_;

};

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Exception-safe programming
exception safe programming15
Příklad: String č. 2

operator=

Naivní pokus o opravu:

Pokud new char způsobí výjimku, ošetří se

Objekt se uvede do konzistentního stavu

Výjimka se propaguje dál - ven z funkce

Problém:

V catch bloku teoreticky může vzniknout nová výjimka

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

try {

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

catch ( ... )

{

str_ = new char[ 1];

* str_ = 0;

throw;

}

}

return * this;

}

Exception-safe programming
exception safe programming16
Příklad: String č. 2

operator=

Lepší pokus o opravu:

Pokud new char způsobí výjimku, ošetří se

Je nutné pozměnit invariant třídy String:

Položka str_ nyní smí obsahovat nulový ukazatel

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

try {

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

catch ( ... )

{

str_ = 0;

throw;

}

}

return * this;

}

Exception-safe programming
exception safe programming17
Příklad: String č. 2

operator=

Lepší pokus o opravu:

Pokud new char způsobí výjimku, ošetří se

Je nutné pozměnit invariant třídy String:

Položka str_ nyní smí obsahovat nulový ukazatel

Takový exemplář String je považován za konzistentní

Konzistentnost nemusí znamenat, že to je z uživatelského pohledu platná hodnota

Může být považována i za chybovou a každá operace s takovou hodnotou může vyvolávat výjimku

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

try {

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

catch ( ... )

{

str_ = 0;

throw;

}

}

return * this;

}

Exception-safe programming
exception safe programming18
Příklad: String č. 2

operator=

Ekvivalentní řešení:

Nulovat str_ po delete

Pokud new způsobí výjimku, v str_ zůstane nulový ukazatel

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Exception-safe programming
exception safe programming19
Příklad: String č. 2

operator=

Chyba: změnili jsme invariant

str_ nyní může být nulové

delete _str je v pořádku

operator delete je vždy proti nulovému ukazateli ošetřen (nedělá nic)

strlen a strcpy ale fungovat nebudou

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Exception-safe programming
exception safe programming20
Příklad: String č. 2

operator=

Opraveno

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

}

return * this;

}

Exception-safe programming
exception safe programming21
Příklad: String č. 2

operator=

Vylepšení:

operator= může vyvolávat výjimku, pokud se přiřazuje neplatná hodnota

Tato výjimka může být definována např. takto:

#include <exception>

class InvalidString

: public std::exception

{

virtual const char * what() const

{ return "Invalid string";

}

}

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

else

{

throw InvalidString();

}

}

return * this;

}

Exception-safe programming
exception safe programming22
Příklad: String č. 2

operator=

Toto řešení je slabě bezpečné

Silně bezpečnéale není:

Pokud dojde k výjimce, nezachovává se původní stav dat

To bude pro uživatele nepříjemné:

String x, y;

/* ... */

try { x = y + x;

}

catch (...) { /* ... */

}

Uživatel nedokáže rozlišit mezi výjimkami v operátorech + a =

Náš operator= ale v případě výjimky ztratí hodnotu x

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

else

{

throw InvalidString();

}

}

return * this;

}

Exception-safe programming
exception safe programming23
Příklad: String č. 2

operator=

Silně bezpečné řešení

Pokud dojde k výjimce v new, nestane se nic

Ani před throw nenastane žádná změna

String & String::operator=(

const String & b)

{

if ( this != & b )

{

if ( b.str_ )

{

char * aux = new char[

strlen( b.str_) + 1];

strcpy( aux, b.str_);

delete[] str_;

str_ = aux;

}

else

{

throw InvalidString();

}

}

return * this;

}

Exception-safe programming
exception safe programming24
Pří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;

}

Exception-safe programming
exception safe programming25
Pří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;

}

Exception-safe programming
exception safe programming26
Příklad: String č. 2

operator=

Silně bezpečné řešení

Pokud je copy-constructor silně bezpečný

Standardní řešení:

Copy-constructor naplní lokální proměnnou c kopií parametru b

Zde může dojít k výjimce

Metoda swap vyměňuje obsah this a proměnné c

Metoda swap je rychlá a nevyvolává výjimky

Před návratem z operatoru se volá destruktor c

Tím zaniká původní obsah this

void String::swap( String & x)

{

char * aux = str_;

str_ = x.str_;

x.str_ = aux;

}

String & String::operator=(

const String & b)

{

String c( b);

swap( c);

return * this;

}

Exception-safe programming
exception safe programming27
Příklad: String č. 2

operator=

Silně bezpečné řešení

Metodu swap je vhodné publikovat ve formě globální funkce

Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám

void String::swap( String & x)

{

char * aux = str_;

str_ = x.str_;

x.str_ = aux;

}

String & String::operator=(

const String & b)

{

String c( b);

swap( c);

return * this;

}

void swap( String & x, String & y)

{

x.swap( y);

}

Exception-safe programming
exception safe programming28
Příklad: String č. 2

operator=

Silně bezpečné řešení

Metodu swap je vhodné publikovat ve formě globální funkce

Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám

Sama metoda swap může využívat šablonu swap pro typ char *

#include <algorithm>

void String::swap( String & x)

{

swap( str_, x.str_);

}

String & String::operator=(

const String & b)

{

String c( b);

swap( c);

return * this;

}

void swap( String & x, String & y)

{

x.swap( y);

}

Exception-safe programming
exception safe programming29
Příklad: String č. 2

copy-constructor

Silně bezpečné řešení

Pokud tělo dorazí na konec, budou datové položky korektně vyplněny

Tělo může vyvolávat výjimky

V takovém případě není třeba datové položky vyplňovat

Objekt nebude považován za platný a nebude používán ani destruován

Obecně je však třeba ošetřit try-blokem situace, kdy je v objektu více dynamicky alokovaných ukazatelů

String( const String & b)

{

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

else

{

throw InvalidString();

}

}

Exception-safe programming
exception safe programming30
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

Slabě bezpečná implementace:

Při výjimce v konstruktoru proměnné s se nestane nic

operator delete nezpůsobuje výjimky

struct Box { String v; Box * next; };

class StringStack {

public:

// ...

private:

Box * top_;

};

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

Box * p = top_;

String s = p->v;

top_ = p->next;

delete p;

return s;

}

Exception-safe programming
exception safe programming31
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

Slabě bezpečná implementace

Není silně bezpečná:

Funkce vrací hodnotou

Pokud při vracení dojde k výjimce v copy-constructoru, zásobník již bude zkrácen

struct Box { String v; Box * next; };

class StringStack {

public:

// ...

private:

Box * top_;

};

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

Box * p = top_;

String s = p->v;

top_ = p->next;

delete p;

return s;

}

Exception-safe programming
exception safe programming32
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

Slabě bezpečná implementace

Není silně bezpečná:

Funkce vrací hodnotou

Pokud při vracení dojde k výjimce v copy-constructoru, zásobník již bude zkrácen

Tuto výjimku lze ošetřittry-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;

}

}

Exception-safe programming
exception safe programming33
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

Nefunkční implementace

Není silně bezpečná:

Funkce vrací hodnotou

Pokud při vracení dojde k výjimce v copy-constructoru, zásobník již bude zkrácen

Tuto výjimku lze ošetřittry-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;

}

}

Exception-safe programming
exception safe programming34
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

Silně bezpečná implementace

Jak zrušíme proměnnou p, když k výjimce nedojde?

std::auto_ptr< T>

"chytrý"ukazatel na T, který se chová jako "jediný vlastník objektu":

po zkopírování se vynuluje

při zániku volá delete

Pozor: auto_ptr má nestandardní copy-constructor a operator=

modifikují svůj parametr

pro auto_ptr nefungují kontejnery apod.

#include <memory>

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

std::auto_ptr< Box> p = top_;

top_ = p->next;

try {

return p->v;

}

catch ( ...)

{

top_ = p;

// toto přiřazení nuluje p

throw;

}

}

// při návratu se automaticky zruší * p

// pokud je p nenulové

Exception-safe programming
exception safe programming35
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

Silně bezpečná implementace

Uživatel ji nedokáže použít tak, aby to bylo silně bezpečné

Vracenou hodnotu je nutné okopírovat

Nedá se poznat, zda výjimku vyvolala metoda pop nebo operator=

V prvním případě je zásobník nedotčen, ale ve druhém je již zkrácen

StringStack stk;

String a;

/* ... */

try {

a = stk.pop();

}

catch (...)

{

/* ??? */

}

Exception-safe programming
exception safe programming36
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

Řešení A

Jako v STL

Rozdělit pop na dvě funkce

top vrací vrchol zásobníku

může jej vracet odkazem

nemodifikuje data

pop pouze zkracuje

je silně bezpečná

StringStack stk;

String a;

/* ... */

try {

a = stk.top();

}

catch (...)

{

/* chyba kopírování

nebo prázdný zásobník,

proměnná a nezměněna,

zásobník nedotčen */

}

try {

stk.pop();

}

catch (...)

{

/* chyba zkracování,

proměnná a změněna,

zásobník nedotčen */

}

Exception-safe programming
exception safe programming37
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

ŘešeníB

Namísto vracení hodnoty funkce pop vyplňuje parametr předávaný odkazem

tím se vyloučí nutnost kombinovat volání pop s dalším kopírováním

Pro uživatele jednodušší, implementace pop je však těžší

StringStack stk;

String a;

/* ... */

try {

stk.pop( a);

}

catch (...)

{

/* chyba zkracování nebo kopírování,

proměnná a nezměněna,

zásobník nedotčen */

}

Exception-safe programming
exception safe programming38
Příklad: StringStack::pop

Zásobník prvků typu String

Implementován seznamem

ŘešeníB

Lze implementovat nad řešením A

#include <memory>

class StringStack {

public:

/* A */

String & top();

void pop();

/* B */

void pop(String & out)

{

String & t = top();

swap( out, t);

try {

pop();

}

catch (...) {

swap( out, t);

throw;

}

}

};

Exception-safe programming
exception specifications
Exception specifications

U každé funkce (operátoru, metody) je možno určit seznam výjimek, kterými smí být ukončena

Na výjimky ošetřené uvnitř funkce se specifikace nevztahuje

Pokud není specifikace uvedena, povoleny jsou všechny výjimky

Specifikace respektuje dědičnost, to jest automaticky povoluje i všechny potomky uvedené třídy

void a()

{

/* tahle smí všechno */

}

void b() throw ()

{

/* tahle nesmí nic */

}

void c() throw ( std::bad_alloc)

{

/* tahle smí std::bad_alloc */

}

void d() throw ( std::exception, MyExc)

{

/* tahle smí potomky

std::exception a MyExc */

}

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

Kompilátor zajistí, že nepovolená výjimka neopustí funkci

Toto je běhová kontrola

Kompilátor smí vydávat nejvýše varování

Funkce smí volat jinou, která by mohla vyvolat nepovolenou výjimku (ale nemusí)

void f() throw ( std::exception)

{

}

void g() throw ()

{

f(); /* tohle se smí */

}

Exception specifications
exception specifications3
Exception specifications
  • Exception specifications
    • Kompilátor (a runtime) zajistí, že nepovolená výjimka neopustí funkci
      • Microsoft Visual C++ 7.0 to ovšem neimplementuje
    • Kompilátor to může využít
      • Speciálně při volání funkce s prázdným throw () se nemusí generovat ošetřující kód
      • Program se zmenší a možná i zrychlí
    • Užitek pro programátory:
      • Komentář
      • Ladicí prostředek
implicitn hodnoty parametr funkc
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);
p et ov n funkc1
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
p et ov n funkc2
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é
iostream
iostream
  • Problém: namespace

namespace prostor {

class Souradnice { public: int x, y; };

std::ostream & operator<<(

std::ostream & s, const Souradnice & a)

{

return s << '[' << a.x << ',' << a.y << ']';

}

};

prostor::Souradnice p;

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

// který není přímo vidět

iostream1
iostream
  • Problém: namespace

namespace prostor {

class Souradnice { public: int x, y; };

std::ostream & operator<<(

std::ostream & s, const Souradnice & a)

{

return s << '[' << a.x << ',' << a.y << ']';

}

};

prostor::Souradnice p;

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

// který není přímo vidět

std::cout << std::endl; // tentýž problém je ale už tady:

// tento operator<< je v namespace std

koenig lookup1
Koenig lookup

prostor::Souradnice p;

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

// který není přímo vidět

std::cout << std::endl; // tentýž problém je ale už tady:

// tento operator<< je v namespace std

  • Oba případy jsou překládány správně
  • Je k tomu nutná složitá definice vyhledávání identifikátoru
    • tzv. Koenigovo vyhledávání
    • používá se, je-li význam identifikátoru závislý na parametrech
      • volání funkce
      • použití operátoru
koenig lookup2
Koenig lookup
  • Koenigovo vyhledávání (zjednodušeno)
      • Argument-dependent name lookup (ISO C++)
    • Pro každý skutečný parametr se z jeho typu T určí množina asociovaných namespace
      • Je-li T číselný, tyto množiny jsou prázdné
      • Je-li T union nebo enum, jehoasociovaný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
koenig lookup3
Koenig lookup
  • Koenigovo vyhledávání (zjednodušeno)
      • Argument-dependent name lookup (ISO C++)
    • Pro každý skutečný parametr se z jeho typu T určí množina asociovaných namespace
    • Identifikátor funkce se pak vyhledává v těchto prostorech
      • Globální prostor a aktuální namespace
      • Všechny namespace přidané direktivami using
      • Sjednocení asociovaných namespace všech parametrů funkce
    • Všechny varianty funkce nalezené v těchto namespace jsou rovnocenné
      • Mezi nimi se vybírá podle počtu a typu parametrů
        • Pokud není jednoznačně určena nejlepší varianta, je to chyba
    • Volání v kontextu třídy: Je-li identifikátor nalezen uvnitř této třídy nebo některého předka (jako metoda), má přednost před výše uvedenými variantami (globálními funkcemi)
p et ov n oper tor

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

Operator overloading

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

+ - * / % << >> < > <= >= <<= >>= ^ & | && || == != += -= *= /= %= ^= &= |= ->*

    • lze pro operandy typu B a C předefinovat dvěma způsoby:
      • Globální funkcí

A operator xxx( B, C)

A operator xxx( B &, C &)

A operator xxx( const B &, const C &)

      • Metodou

A B::operator xxx( C)

A B::operator xxx( const C &)

A B::operator xxx( const C &) const

  • Binární operátor [ ]
    • lze předefinovat pouze metodou

A B::operator []( C)

A B::operator []( C &)

A B::operator []( const C &) const

p et ov n oper tor un rn oper tory
Přetěžování operátorů - Unární operátory
  • Unární operátor xxx z množiny

+ - * & ~ !

    • a prefixové operátory

++ --

    • lze pro operand typu B předefinovat dvěma způsoby:
      • Globální funkcí

A operator xxx( B)

A operator xxx( B &)

A operator xxx( const B &)

      • Metodou

A B::operator xxx()

A B::operator xxx() const

p et ov n oper tor un rn oper tory1
Přetěžování operátorů - Unární operátory
  • Postfixové operátory ++ a --
    • lze pro operand typu B předefinovat dvěma způsoby:
      • Globální funkcí

A operator xxx( B, int)

A operator xxx( B &, int)

A operator xxx( const B &, int)

      • Metodou

A B::operator xxx( int)

A B::operator xxx( int) const

p et ov n oper tor un rn oper tory2
Přetěžování operátorů - Unární operátory
  • Operátor ->
    • je považován za unární operátor a jeho návratovou hodnotou musí být buďto ukazatel na třídu s uvedenou položkou, nebo objekt či referenci na objekt, pro který je znovu definován operátor ->
p et ov n oper tor un rn oper tory3
Přetěžování operátorů - Unární operátory
  • Operátor volání funkce ()
    • smí být definován pouze jako metoda třídy a umožňuje používat objekty této třídy jako funkce.
    • Smí mít libovolný počet parametrů a pro výběr konkrétní varianty operátoru se použije podobný mechanismus, jako pro přetížené funkce.
complex

Complex

Komplexní číslo

complex1
Complex

class Complex {

public:

Complex() : re_( 0.0), im_( 0.0) {}

Complex( double re, double im = 0.0) : re_( re), im_( im) {}

doubleRe() const { return re_; }

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

pou en konstruktory
Poučení - konstruktory
  • Ve třídě Complex nejsou odkazy na data uložená jinde
  • Vyhovuje chování těchto kompilátorem vytvořených metod:

Complex( const Complex &);

    • Kopíruje datové položky

Complex & operator=( const Complex &);

    • Kopíruje datové položky

~Complex();

    • Nedělá nic
  • Tyto metody není třeba psát vlastní
pou en konstruktory1
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
pou en konstruktory2
Poučení - konstruktory
  • Speciální konstruktor

Complex( double re, double im = 0.0);

    • Lze zavolat s jedním parametrem
  • Slouží jako konverzní konstruktor
    • Implementuje konverzi double => Complex
  • Důsledky:
    • Bude fungovat přiřazení Complex=double
    • Není nutné psát sčítání pro Complex+double:

Complex operator+( const Complex & a, double b) const;

      • Dělává se to kvůli rychlosti
    • Kompilátor umí použít konverzi (jednu) a najít náhradní metodu:

Complex operator+(const Complex & a, const Complex & b) const;

    • Totéž funguje pro sčítání double+Complex
      • Pouze pokud je sčítání Complex+Complex globální funkce
      • U metod se levý operand nekonvertuje
pou en vracen hodnotou
Poučení - vracení hodnotou
  • Poučení: operator+ vždy vrací hodnotou
    • Vrací novou hodnotu, která jinde neexistuje

return Complex( ...);

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

return * this;

pou en bin rn oper tory
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;

}

pou en un rn oper tory
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
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);

Globální proměnná
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;

Statická položka třídy
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;

}

Statická lokální proměnná
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;

}

Lokální proměnná
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);

}

Pomocná proměnná
ide ln u it d di nost i a virtu ln ch funkc
Ideální užití dědičnosti a virtuálních funkcí
  • Abstraktní třída
    • Definuje rozhraní objektu jako množinu předepsaných virtuálních funkcí

class GraphicObject {

public:

virtual ~GraphicObject();// každá abstraktní třída má mít v.d.

virtual void paint() = 0; // čistě virtuální funkce

virtual void move( int dx, int dy) = 0; // čistě virtuální funkce

};

p klad d di nost a virtu ln funkce
Příklad: dědičnost a virtuální funkce
  • ISA hierarchie
  • Osoba
    • Student
    • Zaměstnanec
      • Učitel
        • Matikář
        • Fyzikář
        • Chemikář
      • Ředitel
      • Ostatní
        • Školník
        • Kuchař
    • Které abstraktní třídy potřebujeme?
      • Ty, jejichž specifické rozhraní někdo potřebuje
        • Třída „Ostatní“ není potřeba
p klad d di nost a virtu ln funkce1
Příklad: dědičnost a virtuální funkce
  • Virtuální funkce a schopnosti tříd
  • Osoba (dá se evakuovat)
    • Student (platí školné)
    • Zaměstnanec (přijímá výplatu)
      • Učitel (umí uspořádat třídní schůzku)
        • Matikář (umí učit Pythagorovu větu)
        • Fyzikář (umí učit Archimedův zákon)
        • Chemikář (umí předvést výbuch)
      • Ředitel (umí se podepsat)
      • Školník (umí odemykat)
      • Kuchař (umí počítat knedlíky)
p klad d di nost a virtu ln funkce2
Příklad: dědičnost a virtuální funkce
  • Násobná dědičnost
    • Absolvent učitelství na MFF umí
        • uspořádat třídní schůzku
        • učit Pythagorovu větu
        • učit Archimedův zákon
      • Implementuje rozhraní Učitel, Matikář, Fyzikář
        • Má být potomkem všech těchto tříd

class Matfyzak : virtual public Ucitel, virtual public Matikar, virtual public Fyzikar

{

virtual void tridni_schuzka() { ... }

virtual void pythagorova_veta() { ... }

virtual void archimeduv_zakon() { ... }

...

}

p klad d di nost a virtu ln funkce3
Příklad: dědičnost a virtuální funkce
  • Násobná dědičnost

class Matfyzak : virtual public Ucitel, virtual public Matikar, virtual public Fyzikar

{

virtual void tridni_schuzka() { ... }

virtual void pythagorova_veta() { ... }

virtual void archimeduv_zakon() { ... }

...

}

        • na třídu vedou odkazy z různých míst:

class Trida { ... Ucitel * tridni; ... }

class III : public Trida { ... Matikar * matikar; ... }

class IV : public Trida { ... Fyzikar * fyzikar; ... }

      • kdo z nich je vlastník ?
        • Nikdo.
      • Jednoznačným vlastníkem je seznam zaměstnanců.
p klad d di nost a virtu ln funkce4
Příklad: dědičnost a virtuální funkce

class Matfyzak :

public Zamestnanec,virtual public Ucitel, virtual public Matikar, virtual public Fyzikar

{

virtual void evakuace() { ... }

virtual void vyplata() { ... }

virtual void tridni_schuzka() { ... }

virtual void pythagorova_veta() { ... }

virtual void archimeduv_zakon() { ... }

}

class Trida { ... Ucitel * tridni; ... }

class III : public Trida { ... Matikar * matikar; ... }

class IV : public Trida { ... Fyzikar * fyzikar; ... }

class Skola {

vector< Zamestnanec *> zamestnanci;

vector< Trida *> tridy;

};

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

class Osoba {

public:

virtual ~Osoba() {}

virtual void evakuace() = 0;

protected:

Osoba() {}

private:

Osoba( const Osoba &);

Osoba & operator=( const Osoba &);

};

class Zamestnanec : public Osoba {

public:

virtual void vyplata() = 0;

};

p klad p esn ji abstraktn t dy pro rozhran
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;

};

p klad p esn ji konkr tn t da
Příklad přesněji: Konkrétní třída

class Matfyzak :

public Zamestnanec,virtual public Ucitel, virtual public Matikar, virtual public Fyzikar

{

public:

Matfyzak( const string & j ) : jmeno_( j) {}

private:

virtual void evakuace();

virtual void vyplata();

virtual void tridni_schuzka();

virtual void pythagorova_veta();

virtual void archimeduv_zakon();

string jmeno_;

};

void Matfyzak::evakuace() { ... }

...

p klad p esn ji kontejner odkaz
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;

};

p klad p esn ji kontejner odkaz na rozhran
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;

}

co um hardware
Co umí hardware
  • Datové typy podporované procesorem
    • Celá čísla o 8/16/32/64 bitech
    • Několik formátů čísel s pohyblivou čárkou
co um hardware1
Co umí hardware
  • Operace podporované procesorem
    • Celočíselná aritmetika a bitové operace
      • Operace určuje význam bitů
        • Bez znaménka = mod 2N
        • Se znaménkem = dvojkový doplněk
    • Aritmetika s pohyblivou čárkou (vč. konverzí)
    • Čtení z paměti, zápis do paměti
      • Přesouvá pouze podporované datové typy
      • Adresou je celé číslo o 32 resp. 64 bitech
        • Konstanta (součástí instrukce)
        • Registr + konstanta
        • Registr
      • Zarovnání: Adresa dělitelná velikostí přesouvaných dat (v bajtech)
        • Vždy výhodné, někdy nutné
      • Překlad adres: Zadaná adresa je virtuální
element rn typy 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
element rn operace v c
Elementární operace v C++
  • Celočíselná aritmetika a bitové operace
  • Ukazatelová aritmetika
  • Aritmetika v pohyblivé čárce
  • Booleovská algebra a podmíněné výrazy
  • Přístup do paměti
  • Formální operace
element rn operace v c1
Elementární operace v C++
  • Celočíselná aritmetika a bitové operace
        • + - * / % ~ << >> & ^ | < > <= >= == !=
        • Konverze int na long long apod.
      • Typy argumentů určují počet bitů a přítomnost znaménka
      • Typy argumentů určují typ výsledku (delší vyhrává, nejméně int)
  • Ukazatelová aritmetika
  • Aritmetika v pohyblivé čárce
  • Booleovská algebra a podmíněné výrazy
  • Přístup do paměti
  • Formální operace
element rn operace v c2
Elementární operace v C++
  • Celočíselná aritmetika a bitové operace
  • Ukazatelová aritmetika
      • Realizováno celočíselnou aritmetikou procesoru
      • + - < > <= >= == !=
      • Ukazatel +/- číslo
        • Posun ukazatele mezi prvky pole
        • Překladač doplní vynásobení čísla velikostí typu
      • Ukazatel – ukazatel
        • Vzdálenost mezi prvky pole
        • Překladač doplní vydělení výsledku velikostí typu
  • Aritmetika v pohyblivé čárce
  • Booleovská algebra a podmíněné výrazy
  • Přístup do paměti
  • Formální operace
element rn operace v c3
Elementární operace v C++
  • Celočíselná aritmetika a bitové operace
  • Ukazatelová aritmetika
  • Aritmetika v pohyblivé čárce
        • + - * / < > <= >= == !=
        • Konverze int na double apod.
      • Typy argumentů určují typ výsledku (delší vyhrává, nejméně double)
  • Booleovská algebra a podmíněné výrazy
  • Přístup do paměti
  • Formální operace
element rn operace v c4
Elementární operace v C++
  • Celočíselná aritmetika a bitové operace
  • Ukazatelová aritmetika
  • Aritmetika v pohyblivé čárce
  • Booleovská algebra a podmíněné výrazy
      • Realizováno podmíněnými skoky nebo bitovými operacemi
      • ! && || ? :
      • Zaručeno zkrácené vyhodnocování
  • Přístup do paměti
  • Formální operace
element rn operace v c5
Elementární operace v C++
  • Celočíselná aritmetika a bitové operace
  • Ukazatelová aritmetika
  • Aritmetika v pohyblivé čárce
  • Booleovská algebra a podmíněné výrazy
  • Přístup do paměti
      • Ke globálním proměnným (konstantní adresa)
      • K lokálním proměnným (vrchol zásobníku + konstanta)
      • Dereference ukazatele (*)
        • Odpovídá operaci čtení nebo zápisu (podle kontextu)
        • V některých kontextech pouze formální operace
      • K prvkům třídy (konstantní posunutí)
      • K prvkům pole (viz ukazatelová aritmetika)
  • Formální operace
element rn operace v c6
Elementární operace v C++
  • Celočíselná aritmetika a bitové operace
  • Ukazatelová aritmetika
  • Aritmetika v pohyblivé čárce
  • Booleovská algebra a podmíněné výrazy
  • Přístup do paměti
  • Formální operace
        • Reference tj. získání adresy (&)
        • Dereference (*) v kontextu předání odkazem
        • Konverze T * na U *
        • Konverze signed na unsigned apod
      • Většinou pouze změna v záznamech překladače
        • Některé konverze T * na U * mohou vyžadovat operaci přičtení/odečtení konstanty
start programu
Start programu
  • Start programu z pohledu OS
      • „Někdo“ (shell,...) požádá OS o spuštění programu
        • Jméno spustitelného souboru
        • Parametry (MS: vcelku, unix: po částech)
      • OS (loader)
        • vytvoří proces, vyhradí základní kvantum paměti
        • načte instrukce (a inicializační data) ze souboru do paměti
        • doplní referencované DLL, pospojuje odkazy a relokuje
        • oživí proces na definovaném místě
      • Proces běží a prostřednictvím systémových volání žádá o
        • interakci s okolím (soubory, uživatelská rozhraní, komunikace,...)
        • přidělení další paměti, vytvoření dalších vláken, ...
        • ukončení (s návratovým kódem)
      • Proces je zlikvidován (na vlastní žádost nebo po pádu), zabit, ...
        • nadřazenému procesu je předán návratový kód
        • význam návratového kódu je závislý na konvencích
start programu1
Start programu
  • Start programu z pohledu C++ knihoven
      • OS (loader)
        • oživí proces na definovaném místě v kódu standardní knihovny
      • Knihovní kód
        • požádá OS o první kvantum paměti pro dynamickou alokaci
        • inicializuje struktury dynamické alokace
        • naváže standardní vstupy a výstupy na OS
        • inicializuje všechny globální proměnné
        • zkonvertuje parametry programu
        • zavolá main
        • uklidí globální proměnné
        • zavře všechny otevřené soubory
        • požádá o ukončení s návratovým kódem vráceným z main
      • Proces je zlikvidován
funkce main
Funkce main

#include <vector>

#include <string>

#include <iostream>

intmain( 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
podm n n p eklad
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...

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

}

lad n
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>

intmain( int argv, char * * argc)

{

std::vector<std::string> arg( argv, argv + argc);

assert( arg.size() > 0 );

// ...

return 0;

}

preprocesor
Preprocesor
  • Preprocesor je dědictví jazyka C z roku 1970
      • #include
      • #define/#ifndef
      • #if...
      • assert
    • Umí ještě řadu dalších věcí – ty se ale dají v C++ udělat lépe
organizace pam ti procesu
Kódový segment

Datový segment

Heap

Zásobník (stack segment)

Segmenty (vyjma zásobníku) nemusejí být souvislé

Dynamicky-linkované knihovny sdílené mezi procesy

Postupná alokace heapu

Organizace paměti procesu

IP

R0

R1

...

SP

organizace pam ti procesu1
Kódový segment

Připraven kompilátorem – součást spustitelného souboru

Kód uživatelských i knihovních funkcí

Obvykle chráněn proti zápisu

Datový segment

Heap

Zásobník (stack segment)

Organizace paměti procesu

IP

R0

R1

...

SP

organizace pam ti procesu2
Kódový segment

Datový segment

Připraven kompilátorem – součást spustitelného souboru

Explicitně nebo implicitně (nulami) inicializované globální proměnné

Řetězcové konstanty

Data knihoven

Pomocná data generovaná kompilátorem

Heap

Zásobník (stack segment)

Organizace paměti procesu

IP

R0

R1

...

SP

organizace pam ti procesu3
Kódový segment

Datový segment

Heap

Vytvářen startovacím modulem knihoven

Neinicializovaná dynamicky alokovaná data

malloc/free

C++: new/delete

Obsazené bloky různé velikosti + seznam volných bloků

Knihovny mohou též požádat OS o zvětšení segmentu

Zásobník (stack segment)

Organizace paměti procesu

IP

R0

R1

...

SP

organizace pam ti procesu4
Kódový segment

Datový segment

Heap

Zásobník (stack segment)

Připraven op. systémem, knihovny mohou požádat OS o zvětšení

Explicitně inicializované nebo neinicializované lokální proměnné

Pomocné proměnné generované kompilátorem

Návratové adresy

Další pomocná data

Vícevláknové aplikace mají více zásobníků

Organizace paměti procesu

IP

R0

R1

...

SP

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

Organizace paměti vícevláknového procesu

thread 1

IP

R0

R1

...

SP

thread 2

IP

R0

R1

...

SP