Programování v C++ David Bednárek www.ksi.mff.cuni.cz/~bednarek
Pravidla studia NPRG041 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. 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 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 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é Zkouška Pokud letos složíte zkoušku se známkou výborně nebo velmi dobře a nedostanete zápočet, bude vám příští rok uznána Zápočet Tento mechanismus je implementován zkoušejícími, nikoliv studijním oddělěním 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 C++
Historie C++ BCPL (Cambridge 1966) B (Bell Labs. 1969) C (Bell Labs. 1971) K&R C (Kernigan & Ritchie 1978) inspirace nadmnožina C with classes (Stroustrup 1979) téměř nadmnožina významná změna The C++ programming language (Stroustrup 1985) C++98 (ISO/IEC 14882 1998) šablony 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 BCPL (Cambridge 1966) B (Bell Labs. 1969) C (Bell Labs. 1971) K&R C (Kernigan & Ritchie 1978) inspirace nadmnožina C with classes (Stroustrup 1979) téměř nadmnožina významná změna ANSI C (ANSI X3J11 1989) The C++ programming language (Stroustrup 1985) C99 (ISO/IEC 9899 1999) C++98 (ISO/IEC 14882 1998) šablony C++03 (ISO/IEC 14882 2003) C++TR1 (ISO/IEC 19768 2007) C11 (ISO/IEC 9899 2011) paralelismus C++11 (ISO/IEC 14882 2011) C++14 (2014+)
Historie C++ - Objective-C BCPL (Cambridge 1966) B (Bell Labs. 1969) C (Bell Labs. 1971) inspirace nadmnožina téměř nadmnožina významná změna Objective-C (Cox & Love 1981) Object-Oriented Programing (Cox 1986) K&R C (Kernigan & Ritchie 1978) ANSI C (ANSI X3J11 1989) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) C99 (ISO/IEC 9899 1999) C++98 (ISO/IEC 14882 1998) šablony Objective-C 2.0 (Apple 2006) C++03 (ISO/IEC 14882 2003) C++TR1 (ISO/IEC 19768 2007) Objective-C++ (Apple 2010) C11 (ISO/IEC 9899 2011) paralelismus C++11 (ISO/IEC 14882 2011) C++14 (2014+)
Historie C++ - významné příbuzné jazyky BCPL (Cambridge 1966) B (Bell Labs. 1969) C (Bell Labs. 1971) inspirace nadmnožina téměř nadmnožina významná změna Objective-C (Cox & Love 1981) Object-Oriented Programing (Cox 1986) K&R C (Kernigan & Ritchie 1978) ANSI C (ANSI X3J11 1989) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) Java (Sun 1995) Objective-C 2.0 (Apple 2006) Objective-C++ (Apple 2010) C99 (ISO/IEC 9899 1999) C11 (ISO/IEC 9899 2011) C# (Microsoft 2002) C++/CLI (Microsoft 2005) paralelismus C++98 (ISO/IEC 14882 1998) C++03 (ISO/IEC 14882 2003) C++TR1 (ISO/IEC 19768 2007) C++11 (ISO/IEC 14882 2011) šablony C++14 (2014+)
Historie C++ - použití C v jádrech OS BCPL (Cambridge 1966) B (Bell Labs. 1969) C (Bell Labs. 1971) Unix 1973 inspirace nadmnožina téměř nadmnožina významná změna Objective-C (Cox & Love 1981) Object-Oriented Programing (Cox 1986) OS-X 2000 Objective-C 2.0 (Apple 2006) Objective-C++ (Apple 2010) Windows NT 1993 K&R C (Kernigan & Ritchie 1978) MacOS 1984 ANSI C (ANSI X3J11 1989) C99 (ISO/IEC 9899 1999) C11 (ISO/IEC 9899 2011) C with classes (Stroustrup 1979) The C++ programming language (Stroustrup 1985) Linux 1991 C# (Microsoft 2002) C++/CLI (Microsoft 2005) paralelismus C++98 (ISO/IEC 14882 1998) C++03 (ISO/IEC 14882 2003) C++TR1 (ISO/IEC 19768 2007) C++11 (ISO/IEC 14882 2011) Java (Sun 1995) šablony C++14 (2014+)
Literatura
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)
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)
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)
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
Je lepší C++ nebo Java/C#?
Je lepší C++ nebo Java/C#? Špatná otázka
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 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 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 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č (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! #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, World! Dělení do modulů Rozhraní modulů je nutno opsat do zvláštního souboru.hpp - hlavičkový soubor Definující i používající modul tento soubor inkluduje textová direktiva #include // world.hpp #ifndef WORLD_HPP_ #define WORLD_HPP_ void world(); #endif // main.cpp #include "world.hpp" int main( int argc, char * * argv) world(); return 0; // world.cpp #include "world.hpp" #include <iostream> void world() std::cout << "Hello, world!" << std::endl;
Hello, World! // world.hpp #ifndef WORLD_HPP_ #define WORLD_HPP_ #include <vector> #include <string> typedef std::vector< std::string> t_arg; void world( const t_arg & arg); #endif // main.cpp #include "world.hpp" int main( int argc, char * * argv) world( t_arg( argv + 1, argv + argc)); return 0; // world.cpp #include "world.hpp" #include <iostream> void world( const t_arg & arg) if ( arg.empty() ) std::cout << "Hello, world!" << std::endl;
Architektura Překladače / interpretry
CPU C P U
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 C P U 01010000 01110100 11010111 10010110 00100010 10110001
1940... programování ve strojovém kódu 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 01010000 01110100 11010111 10010110 00100010 10110001
1940... programování ve strojovém kódu 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 01010000 01110100 11010111 10010110 00100010 10110001 X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X
1950... assembler 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 01010000 01110100 11010111 10010110 00100010 10110001 X X X X X X X X X XX X XX XX XX X XX X X X X X X X XXXXXXXXXXXXXXXXXXXXXXXXXXXX X X X X X X X XX X X X XX X XXX X X X X X X X 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 C P U assembler
1950... operační systém 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader 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 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 C P U assembler
1950... operační systém 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader 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 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 C P U assembler myprog.exe
1950... 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 C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader 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 READ INPUT TAPE 5, 501, IA, IB, IC 501 FORMAT (3I5) IF (IA) 777, 777, 701 701 IF (IB) 777, 777, 702 702 IF (IC) 777, 777, 703 703 IF (IA+IB-IC) 777,777,704 704 IF (IA+IC-IB) 777,777,705 705 IF (IB+IC-IA) 777,777,799 777 STOP 1 799 S = FLOATF (IA + IB + IC) / 2.0 AREA = SQRT( S * (S - FLOATF(IA)) * (S - FLOATF(IB)) * + (S - FLOATF(IC))) WRITE OUTPUT TAPE 6, 601, IA, IB, IC, AREA STOP END C P U překladač Fortran myprog.exe
1970... překladač C 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader Hello, world! #include <stdio.h> int main(int,char**) printf( "Hello, world!\n"); C P U překladač C myprog.exe
1980... překladač C++ 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader Hello, world! #include <iostream> int main(int,char**) std::cout << "Hello, world!\n"; C P U překladač C++ myprog.exe
1960... interpret(er) 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 10 INPUT "What is your name: ", U$ 20 PRINT "Hello "; U$ 30 INPUT "How do you want: ", 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 C P U interpret operační systém 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
Interpretace s mezikódem 10 INPUT "What is your name: ", U$ 20 PRINT "Hello "; U$ 30 INPUT "How do you want: ", N 40 S$ = "" 50 FOR I = 1 TO N 60 S$ = S$ + "*" 70 NEXT I 80 PRINT S$ 90 INPUT "Do you want? ", A$ 100 IF LEN(A$) = 0 THEN 90 110 A$ = LEFT$(A$, 1) 120 IF A$ = "Y" THEN 30 130 PRINT "Goodbye ";U$ 140 END 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U překladač 04FBC41E 77AB2000 1AE04E33 operační systém interpret 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
interpretovaný mezikód (bytecode) public class HelloWorld public static void main(string[] args) System.out.println( "Hello, world!"); C P U překladač myprog.class 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U 04FBC41E 77AB2000 1AE04E33 interpret operační systém Hello, world!
JIT překladač public class HelloWorld public static void main(string[] args) System.out.println( "Hello, world!"); C P U překladač myprog.class 13 0 1 0 10 0 8 0 9 0 13 0 8 0 16 0 7 0 2 0 0 0 0 12 0 15 0 6 0 2 0 7 0 9 0 6 0 2 0 8 0 8 0 10 0 4 0 0 2 0 7 0 7 0 1 0 2 0 0 5 0 1 0 8 0 0 8 0 7 0 10 0 1 0 0 0 2 0 8 0 11 0 4 0 9 0 13 0 0 2 0 0 0 0 6 0 7 0 0 0 0 8 0 5 0 0 3 0 17 0 0 0 5 0 20 10 0 C P U JIT překladač 01010000 01110100 11010111 10010110 00100010 10110001 operační systém Hello, world!
Srovnání JIT/non-JIT public class HelloWorld public static void main(string[] args) System.out.println( "Hello, world!"); C P U překladač myprog.class C P U JIT překladač 01010000 01110100 11010111 10010110 00100010 10110001 operační systém #include <iostream> int main(int,char**) std::cout << "Hello, world!\n"; C P U překladač myprog.exe C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader
Srovnání JIT/non-JIT JIT (Java, C#, C++/CLI) non-jit (C++) Distribuuje se bytecode (.class,.exe) Distribuce závislá na jazyku a překladači Distribuuje se (někdy) jako binární instrukce (.exe) Distribuce závislá na procesoru a OS Překladač zná přesně cílovou architekturu, může pozorovat chování programu Překladač má dost času na překlad
Dynamické spojování? public class HelloWorld public static void main(string[] args) mylib.doit(); překladač překladač myprog.class mylib.class C P U JIT překladač 01010000 01110100 11010111 10010110 00100010 10110001 operační systém #include <iostream> int main() int main() doit(); std::cout << "Hello, world!\n"; překladač myprog.exe C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader překladač mylib.dll
Dynamické spojování import acme.mylib; public publiclass class HelloWorld HelloWorld public public static static void void main(string[] main(string[] args) args) mylib.doit(); System.out.println( "Hello, world!"); překladač překladač myprog.class mylib.class C P U JIT překladač 01010000 01110100 11010111 10010110 00100010 10110001 operační systém #include "mylib.hpp" #include <iostream> int main() int main() doit(); std::cout << "Hello, world!\n"; // mylib.hpp void doit(); překladač myprog.exe C P U 01010000 01110100 11010111 10010110 00100010 10110001 operační systém loader překladač mylib.dll
Překlad jednoduchého programu - statické spojování // iostream // iostream #include <fstream> #include <fstream> namespace std namespace std extern ofstream extern ofstream cout, cerr; cout, cerr; ; ; iostream.obj msvcrt.lib // myprog.cpp Kompilátor myprog.obj Linker myprog.exe #include <iostream> int main() std::cout << "Hello, world!\n";
Oddělený překlad a spojování modulů Standardní Standardní.obj Uživatelské.hpp Standardní.lib Uživatelské.cpp Kompilátor Přeložené.obj Linker Spustitelný soubor.exe
Spojování modulů myprog.cpp #include "bee.hpp" int main(int,char**) return B( 7); Kompilátor myprog.obj 0000: 01010000???????? 11010111 export main(int,argv**) import B(int) bee.hpp #ifndef bee_hpp #define bee_hpp int B( int q); #endif Linker myprog.exe 0000: 01010000 00001100 11010111 1100: 10010110 00100010 10110001 bee.cpp #include "bee.hpp" int B( int q) return q+1; Kompilátor bee.obj 0000: 10010110 00100010 10110001 export B(int)
make Standardní Standardní.obj Uživatelské.hpp Standardní.lib Uživatelské.cpp Kompilátor Přeložené.obj Linker Spustitelný soubor.exe Make makefile
Integrované prostředí Standardní Standardní.obj Uživatelské.hpp Standardní.lib Uživatelské.cpp Kompilátor Přeložené.obj Linker Spustitelný soubor.exe Editor Debugger projekt
Statické knihovny Standardní Standardní.obj Uživatelské.hpp Standardní.lib 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) Standardní Standardní.obj Uživatelské.hpp Standardní.lib Uživatelské.cpp Kompilátor Přeložené.obj Linker Spustitelný soubor.exe Knihovní.hpp Knihovna.lib Knihovní.cpp Kompilátor Přeložené.obj Linker Knihovna.dll
Dynamické knihovny (GNU) Standardní Standardní.o Uživatelské.hpp Standardní.a Uživatelské.cpp Kompilátor Přeložené.o Linker Spustitelný soubor Knihovní.hpp Knihovní.cpp Kompilátor Přeložené.o Librarian Knihovna.so
Deklarace a definice
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 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
Deklarace a definice tříd a typů Deklarace Definice Třída class A; class A... ; Struktura struct A; struct A... ; Unie (v C++ prakticky nepoužitelné) Pojmenovaný typ union A; union A... ; typedef A A2; typedef A * AP; typedef std::shared_ptr< A> AS; typedef A AA[ 10]; typedef A AF(); typedef AF * AFP1; typedef A (* AFP2)(); typedef std::vector< A> AV; typedef AV::iterator AVI;
Deklarace a definice proměnných Globální proměnná Statická položka třídy Statická konstantní položka třídy Statická lokální proměnná Nestatická položka třídy Nestatická lokální proměnná Deklarace Definice extern int x, y, z; int x; int y = 729; int z( 729); class A static int x, y, z; ; int A::x; int A::y = 729; int A::z( 729); class A static const int x = 729; ; void f() static int x; static int y = 7, z( 7); class A int x, y; ; void f() int x; int y = 7, z( 7); ;
Deklarace a definice funkcí non-inline Deklarace (.hpp nebo.cpp) Definice (.cpp) Globální funkce int f( int, int); int f( int p, int q) return p + q; Statická metoda class A static int f( int p); ; Nestatická metoda class A int f( int p); ; Virtuální metoda class A virtual int f( int p); ; int A::f( int p) return p + 1; int A::f( int p) return p + 1; int A::f( int) return 0; inline Deklarace (.hpp nebo.cpp) Definice (.hpp nebo.cpp) Globální inline funkce Nestatická inline metoda (a) Nestatická inline metoda (b) class A int f( int p); ; inline int f( int p, int q) return p + q; inline int A::f( int p) return p + 1; class A int f( int p) return p+1; ;
Umístění dat
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 Čí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
Okamžik inicializace a destrukce Inicializace Destrukce Globální proměnná Před vstupem do main Po výstupu z main Statická položka třídy Před vstupem do main Po výstupu z main Statická lokální proměnná Lokální proměnná Při prvním průchodu řízení deklarací V okamžiku průchodu řízení deklarací Po výstupu z main Při výstupu z bloku Parametr předávaný hodnotou Návratová hodnota funkce Pomocná proměnná při výpočtu výrazu Před voláním funkce V příkazu return Když je vypočtena její hodnota Před návratem z funkce Po návratu z funkce Na konci příkazu (v deklaraci: na konci bloku) Dynamicky alokovaná data Při volání new Při volání delete
Nejdůležitější datové typy
Vybrané číselné typy bool char std::wchar_t int unsigned long long unsigned long long std::size_t double long double std::complex<double> false, true znak základní sady (např. ASCII, 8 bit) znak rozšířené sady (např. Unicode, 16/32 bit) celé číslo se znaménkem (obvykle 32 bit) celé číslo bez znaménka (obvykle 32 bit) extra velké celé číslo se znaménkem (64 bit) extra velké celé číslo bez znaménka (64 bit) dostatečně velké číslo pro velikost čehokoliv (32/64 bit) reálné číslo (Intel: 64 bit) přesnější reálné číslo (Intel: 80 bit) komplexní číslo dané přesnosti
Další důležité typy std::string std::wstring std::istream std::wistream std::ostream std::wostream struct T std::pair<t1,t2> std::vector<t> std::list<t> std::map<k,t> std::multimap<k,t> řetězec (nad char) řetězec (nad std::wchar_t) vstupní proud (nad char) vstupní proud (nad std::wchar_t) výstupní proud (nad char) výstupní proud (nad std::wchar_t) struktura uspořádaná dvojice s prvky typu T1 a T2 pole prvků typu T seznam prvků typu T asociativní pole prvků typu T indexované typem K asociativní pole s opakováním
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
Ukazatel vs. hodnota
Reference, ukazatelé, iterátory T & const T & T * const T * Reference Konstrukce jazyka C++ Použití syntakticky shodné s hodnotou (r.a) 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ů 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
Referenční semantika C#/Java vs. C++ Referenční typy (C#,Java) Ukazatele na objekty (C++) class T public int a; class test static void f( T z) z.a = 3; static void g() T x = new T(); //vznik x.a = 1; T y = x; //druhý odkaz y.a = 2; // x.a == 2 f( x); // x.a == 3 class T public: int a; ; void f( T * z) z->a = 3; void g() T * x = new T; //vznik x->a = 1; T * y = x; //druhý odkaz y->a = 2; // x->a == 2 f( x); // x->a == 3 //zrušení zařídí garbage collector delete x; //zánik je nutno vyvolat ručně
Referenční semantika C#/Java vs. C++ Referenční typy (C#,Java) Chytré ukazatele na objekty (C++) class T public int a; class test static void f( T z) z.a = 3; static void g() T x = new T(); //vznik x.a = 1; T y = x; //druhý odkaz y.a = 2; // x.a == 2 f( x); // x.a == 3 class T public: int a; ; void f( T * z) z->a = 3; void g() std::shared_ptr< T> x = new T; //vznik x->a = 1; std::shared_ptr< T> y = x; //druhý odkaz y->a = 2; // x->a == 2 f( x); // x->a == 3 //zrušení zařídí garbage collector //zrušení při zániku posledního odkazu
Hodnotová semantika C#/Java vs. C++ Hodnotové typy (C#) Objekty (C++) struct T int a; class test static void f( T z) z.a = 3; static void g() T x; //vznik x.a = 1; T y = x; //kopie y.a = 2; // x.a == 1 f( x); // x.a == 1 class T public: int a; ; void f( T z) z.a = 3; void g() T x; //vznik x.a = 1; T y = x; //kopie y.a = 2; // x.a == 1 f( x); // x.a == 1 //zrušení v okamžiku zániku proměnné //zrušení v okamžiku zániku proměnné
Hodnotová semantika C#/Java vs. C++ Předání odkazem (C#) (hodnotové typy) struct T int a; class test static void f( ref T z) z.a = 3; Předání odkazem (C++) class T public: int a; ; void f( T & z) z.a = 3; static void g() T x; //vznik x.a = 1; f( ref x); // x.a == 3 void g() T x; x.a = 1; f( x); // x.a == 3
Referenční semantika C#/Java vs. C++ Předání odkazem (C#) Referenční typy class T public int a; class test static void f( ref T z) z = new T(); //vznik static void g() T x = new T(); //vznik f( ref x); // x je nyní jiný objekt //zrušení zařídí garbage collector Předání odkazem (C++) Reference na ukazatel Užíváno řídce nebezpečí chyb class T public: int a; ; void f( T * & z) delete z; //zánik je nutno vyvolat ručně z = new T; //vznik void g() T * x = new T; //vznik f( x); // *x je nyní jiný objekt delete x; //zánik je nutno vyvolat ručně
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(); //... 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ř delete[] q;
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)); 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 // dealokuje oba objekty
Reference
Reference Hodnotové typy (C++,C#,...) Referenční typy (C#,Java) Lokálně existující objekty (C++) Reference na objekty (C++) Ukazatele na objekty (C++) f(int y); class T... class T... class T... class T... void g() int x; //vznik void g() T x = new T; //vznik void g() T x; //vznik void g() T x; //vznik void g() T * x = new T; //vznik int y = z; //kopie //zrušení při //výstupu z bloku T y = x; //druhý odkaz y.f(); //zrušení zařídí //garbage //collector T y = x; //kopie y.f(); //zánik při //výstupu z bloku T & y = x; //odkaz y.f(); //zánik při //výstupu z bloku T * y = x; //druhý odkaz y->f(); delete y; //zánik x->f(); //chyba
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 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 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 *
Reference Novinky související s existencí reference Inicializaci reference nelze nahradit přiřazením Třídy obsahující referenci musí mít konstruktor Nelze rozlišit skutečné parametry předávané hodnotou a odkazem Návratová hodnota funkce může být l-hodnota a.at( i) = x; Zvýšené nebezpečí nekorektních konstrukcí int & f() int x; return x; // funkce vrátí referenci na neexistující objekt
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í 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í 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í 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í 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í odkazem Řešení bez kopírování class ComplexBody public: ComplexBody( double r, double i) : re( r), im( i) double re, im; ; Complex add( const Complex & a, const Complex & b) return Complex( a.re() + b.re(), a.im() + b.im()); return r; 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; ; Ekvivalent garbage-collection ComplexBody je sdíleno několika instancemi Complex a zaniká s posledním z nich Garbage-collection metodou markand-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 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
STL Standard Template Library
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 #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 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 Kontejnery Pomocné metody kontejneru Test prázdnosti bool empty() const Počet prvků size_t size() const nepoužívat pro testy prázdnosti
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 list random_access 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
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 Kontejnery Metody kontejneru pro vkládání iterator insert( iterator p, T x) vsune (kopii) x před prvek, na který ukazuje iterátor p vrací iterátor ukazující na vložený prvek void insert( iterator p, int n, T x) vsune n kopií x před prvek, na který ukazuje iterátor p template< typename other_iterator> void insert( iterator p, other_iterator b, other_iterator e) před prvek, na který ukazuje iterátor p, vsune kopii posloupnosti prvků ohraničené iterátory b a e Tato posloupnost nesmí být uvnitř téhož kontejneru Tato posloupnost může být součástí jiného druhu kontejneru je-li p == end(), vkládání připojuje na konec kontejneru všechny dříve existující iterátory odkazující na tento kontejner jsou po použití insert neplatné, včetně p výjimka: kontejnery list a forward_list iterátory mířící na původní prvky nebo end() zachovávají
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 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 Kontejnery Původní vkládací metody kopírují vkládané prvky Zbytečná práce, nemožnost použití některých typů (unique_ptr) [C++11] emplace Metody insert a push_back/front mají emplace varianty iterator emplace( iterator p, T1 && x1,..., Tn && xn); void emplace_back( T1 && x1,..., Tn && xn); void emplace_front( T1 && x1,..., Tn && xn); Do kontejneru je přidán nový prvek inicializovaný konstruktorem s parametry x1,..., xn std::vector< std::vector< int> > k; k.emplace_back( 100, 0); Šetří kopírování vkládaného prvku oproti původnímu zápisu k.push_back( std::vector< int>( 100, 0)); Šetří i v případě nepřipraveného typu bez move-semantiky V případě vector< int> by to kopírování ušetřila sama move-semantika Poznámka: rvalue reference v hlavičce emplace funkcí dovolují i lvalue operandy pomocí skládání referencí a funkce std::forward
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: kontejnery list a forward_list iterátory mířící na nesmazané prvky zachovávají void clear() erase( begin(), end());
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 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] auto umožňuje deklaraci proměnné bez uvedení typu typ si odvodí překladač z inicializace, v tomto případě K::[const_]iterator
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 - Kontejnery složitost operace na kontejneru s n prvky list deque vector basic_string přídání / odebrání jednoho prvku na začátku push_front pop_front konstantní konstantní funkce neexistuje funkce neexistuje přídání / odebrání jednoho prvku na i-té pozici insert erase konstantní min( i, n - i) n - i n - i přídání / odebrání m prvků na i-té pozici insert erase m přesuny mezi seznamy (splice) jsou konstantní m +min( i, n - i) m + n - i m + n - i přídání / odebrání jednoho prvku na konci push_back pop_back konstantní konstantní konstantní funkce neexistuje nalezení i-tého prvku begin() + i funkce neexistuje konstantní konstantní konstantní paměťová náročnost kontejneru s prvky velikosti s (s + K) * n K řádově 16 B q * s * n q kolem 1.2 q * s * n q kolem 1.2 q * s * n q kolem 1.2
Asociativní kontejnery
STL - Kontejnery Asociativní kontejnery Uspořádané (samovyvažující se stromy) set<t> - množina multiset<t> - množina s opakováním map<k,t> - asociativní pole, tj. parciální zobrazení K -> T multimap<k,t> - relace s rychlým vyhledáváním podle klíče K [C++11] Hashované [C++11] unordered_set<t> - množina [C++11] unordered_multiset<t> - množina s opakováním [C++11] unordered_map<k,t> - asociativní pole, tj. parciální zobrazení K -> T [C++11] unordered_multimap<k,t> - relace s rychlým vyhledáváním podle klíče K pair<a,b> - pomocná šablona uspořádané dvojice s položkami first, second
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 - Kontejnery Uspořádané kontejnery vyžadují uspořádání na klíčích Vystačí si s operací < V nejjednodušším případě to funguje samo std::map< std::string, int> mapa; Pokud typ uspořádání nemá, lze jej definovat obecně bool operator<( const Klic & a, const Klic & b) return...; std::map< Klic, int> mapa; Pokud obecná definice uspořádání nevyhovuje, lze definovat uspořádání funktorem pouze pro daný typ kontejneru struct Usporadani bool operator()( const Klic & a, const Klic & b) const return...; ; std::map< Klic, int, Usporadani> mapa; Pokud různé instance kontejneru mají mít různé uspořádání, lze do funktoru doplnit datové položky struct Usporadani Usporadani( bool a); /*...*/ bool ascending; ; std::map< Klic, int, Usporadani> mapa( Usporadani( true));
STL - Kontejnery Uspořádání na klíčích implementace Knihovny definují funktor std::less< K> template< typename K> class less public: bool operator()( const K & a, const K & b) const return a < b; ; Šablona kontejneru má typový parametr - typ funktoru template< typename K, typename T, typename L = std::less< K> > class map public: Konstruktor kontejneru dostává hodnotu funktoru explicit map( const L & c = L()) : cmp_( c) /*...*/ /*...*/ private: ; L cmp_; Kontejner drží jednu instanci funktoru Metody kontejneru volají operátor () na instanci funktoru iterator find_( /*...*/) /*...*/ if ( cmp_( x, y) ) /*...*/
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 kontejneru má dva další parametry template< typename K, typename T, typename H = std::hash< K>, typename E = std::equal_to< K> > class unordered_map; Konstruktor kontejneru dostává hodnoty funktorů explicit unordered_map( std::size_t initial_bucket_count = /*...*/, const H & h = L(), const E & e = E());
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 - Kontejnery Asociativní kontejnery vyhledávání iterator set::find( T x) iterator multiset::find( T x) iterator map::find( K x) iterator multimap::find( K x) iterator unordered_set::find( T x) iterator unordered_multiset::find( T x) iterator unordered_map::find( K x) iterator unordered_multimap::find( K x) pokud v kontejneru existuje prvek s klíčem ekvivalentním x: vrací iterátor ukazující na první takový prvek multiset, multimap: další takové prvky jsou dostupné operací ++ jinak vrací end() složitost operace uspořadné kontejnery: O( log( size())) hashující kontejnery: průměrně O(1), nejhůře O( size())