Presentation is loading. Please wait.

Presentation is loading. Please wait.

Programming in C++ NPRG041 Programming in C++ - 2016/2017 David Bednárek.

Similar presentations


Presentation on theme: "Programming in C++ NPRG041 Programming in C++ - 2016/2017 David Bednárek."— Presentation transcript:

1 Programming in C++ NPRG041 Programming in C /2017 David Bednárek

2 Course credits Conditions
Exams abc-tests January to February - registration in SIS Passing practical programming tests in lab, approx. 3 hours, common sessions for all groups - registration in SIS 1st attempts - 2nd half of January 2nd attempts - 1st half of February 3rd attempts - April Creating an individual project Agreement on project assignment - until end of November Beta version until March 31, 2017 Final version including documentation until May 28, 2017 Reasonable participation in labs Homework assignments Conditions may be individually adjusted: contact your lab teacher during October Erasmus students may need dates and deadlines sooner NPRG041 Programming in C /2017 David Bednárek

3 NPRG041 Programming in C++ - 2016/2017 David Bednárek

4 3 billion devices run a bytecode interpreter (or JIT compiler), a run- time library, an operating system (if any), and device drivers… NPRG041 Programming in C /2017 David Bednárek

5 3 billion devices run a bytecode interpreter (or JIT compiler), a run- time library, an operating system (if any), and device drivers… …implemented mostly in C or C++ NPRG041 Programming in C /2017 David Bednárek

6 The C++ programming language Object-Oriented Programing
History of C++ Unix 1973 BCPL (Cambridge 1966) B (Bell Labs. 1969) C (Bell Labs. 1971) inspired almost superset superset significant change K&R C (Kernigan & Ritchie 1978) C with classes (Stroustrup 1979) classes classes Objective-C (Cox & Love 1981) MacOS 1984 The C++ programming language (Stroustrup 1985) Object-Oriented Programing (Cox 1986) ANSI C (ANSI X3J ) Linux 1991 Java (Sun 1995) Windows NT 1993 C++98 (ISO/IEC ) templates OS-X 2000 C99 (ISO/IEC ) C# (Microsoft 2002) C++03 (ISO/IEC ) Objective-C 2.0 (Apple 2006) C++/CLI (Microsoft 2005) C++TR1 (ISO/IEC ) C++17 Objective-C++ (Apple 2010) C11 (ISO/IEC ) parallelism C++11 (ISO/IEC ) C++14 (2014)

7 Books C++11 Scott Meyers: Effective Modern C++ (C++11/C++14)
O'Reilly 2014 Methodology of programming Scott Meyers: Overview of the New C++ (C++11) Collected slides Motivation behind C++11 explained Bjarne Stroustrup: The C++ Programming Language - 4th Edition Addison-Wesley 2013 Complete C++11 textbook Stanley B. Lippman, Josée Lajoie, Barbara E. Moo: C++ Primer (5th Edition) Addison-Wesley 2012

8 Older books For beginners – before C++11 = outdated, avoid!
Bruce Eckel: Thinking in C++ (2000) Andrew Koenig, Barbara E. Moo: Accelerated C++ (2000) Stanley B. Lippman: Essential C++ (2000) Intermediate – before C++11 = outdated but may be worth reading 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)

9 Hello, World! NPRG041 Programming in C /2017 David Bednárek

10 Hello, World! Program entry point main function arguments
#include <iostream> int main( int argc, char * * argv) { std::cout << "Hello, world!" << std::endl; return 0; } Program entry point Heritage of the C language No classes or namespaces Global function "main" main function arguments Command-line arguments Split to pieces Archaic data types Pointer to pointer to char Logically: array of strings std - standard library namespace cout - standard output global variable << - stream output overloaded operator endl - line delimiter global function (trick!)

11 Hello, World! More than one module
Module interface described in a file .hpp - "header" file The defining and all the using modules shall "include" the file Text-based inclusion // 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; }

12 Hello, World! // world.hpp #ifndef WORLD_HPP_ #define WORLD_HPP_
#include <vector> #include <string> using t_arg = std::vector< std::string>; 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; }

13 Compilation and linking
NPRG041 Programming in C /2017 David Bednárek

14 Single-module programs - static linking
// iostream #include <fstream> namespace std { extern ofstream cout, cerr; }; // iostream #include <fstream> namespace std { extern ofstream cout, cerr; }; iostream.obj msvcrt.lib // myprog.cpp #include <iostream> int main() { std::cout << "Hello, world!\n"; } Compiler myprog.obj Linker myprog.exe

15 Multiple-module programs
User include files .hpp Library include files Compiler User modules .cpp Compiled .obj Linker Runnable .exe Library modules .obj Library .lib

16 Module interfaces and linking
myprog.cpp #include "bee.hpp" int main(int,char**) { return B( 7); } myprog.obj 0000: ???????? export main(int,argv**) import B(int) Compiler bee.hpp #ifndef bee_hpp #define bee_hpp int B( int q); #endif myprog.exe 0000: : Linker bee.cpp #include "bee.hpp" int B( int q) { return q+1; } bee.obj 0000: export B(int) Compiler

17 make User include files .hpp Library include files Compiler
User modules .cpp Compiled .obj Linker Runnable .exe Library modules .obj Library .lib Make makefile

18 Integrated environment
User include files .hpp Library include files Compiler User modules .cpp Compiled .obj Linker Runnable .exe Library modules .obj Library .lib Editor Debugger project file

19 Static libraries Library as distributed (binary)
User include files .hpp Std. library include files Compiler User modules .cpp Compiled .obj Linker Runnable .exe Std. library modules .obj Std. library .lib Library as distributed (binary) Library as distributed (source) Library .hpp Library .lib Library .cpp Compiler Compiled .obj Librarian

20 Dynamic libraries (Microsoft)
User include files .hpp Std. library include files Compiler User modules .cpp Compiled .obj Linker Runnable .exe Std. library modules .obj Std. library .lib Library as distributed (binary) Library as distributed (source) Library .hpp Library .dll Stub library .lib Library .cpp Compiler Compiled .obj Librarian

21 Dynamic libraries (Linux)
User include files .hpp Std. library include files Compiler User modules .cpp Compiled .o Linker Runnable Std. library modules .o Std. library .a Library as distributed (binary) Library as distributed (source) Library .hpp Library .so Library .cpp Compiler Compiled .o Librarian

22 .cpp/.hpp - best practices
.hpp – "header files" Protect against repeated inclusion #ifndef myfile_hpp_ #define myfile_hpp_ /* … */ #endif Use include directive with double-quotes #include "myfile.hpp" Angle-bracket version is dedicated to standard libraries #include <iostream> Use #include only in the beginning of files (after ifndef+define) Make header files independent: it must include everything what it needs .cpp - "modules" Incorporated to the program using a project/makefile Never include using #include NPRG041 Programming in C /2017 David Bednárek

23 .cpp/.hpp - best practices
.hpp – "header files" Declaration/definitions of types and classes Implementation of small functions Outside classes, functions must be marked "inline" inline int max( int a, int b) { return a > b ? a : b; } Headers of large functions int big_function( int a, int b); Extern declarations of global variables extern int x; Consider using singletons instead of global variables Any generic code (class/function templates) The compiler cannot use the generic code when hidden in a .cpp .cpp - "modules" Implementation of large functions Including "main" Definitions of global variables and static class data members May contain initialization int x = 729; NPRG041 Programming in C /2017 David Bednárek

24 All identifiers must be declared prior to first use
Dependences in code All identifiers must be declared prior to first use Compilers read the code in one pass Exception: Member-function bodies are analyzed at the end of the class A member function body may use other members declared later Generic code involves similar but more elaborate rules Cyclic dependences must be broken using declaration + definition class one; // declaration class two { std::shared_ptr< one> p_; }; class one : public two // definition {}; Declared class is of limited use before definition Cannot be used as base class, data-member type, in new, sizeof etc. NPRG041 Programming in C /2017 David Bednárek

25 Declarations and definitions

26 Declarations and definitions
A construct to declare the existence (of a class/variable/function/...) Identifier Some basic properties Ensures that (some) references to the identifier may be compiled Some references may require definition Definition A construct to completely define (a class/variable/function/...) Class contents, variable initialization, function implementation Ensures that the compiler may generate runtime representation Every definition is a declaration Declarations allow (limited) use of identifiers without definition Independent compilation of modules Solving cyclic dependences Minimizing the amount of code that requires (re-)compilation

27 Declarations and definitions
One-definition rule #1: One translation unit... (module, i.e. one .cpp file and the .hpp files included from it) ... may contain at most one definition of any item One-definition rule #2: Program... (i.e. the .exe file including the linked .dll files) ... may contain at most one definition of a variable or a non-inline function Definitions of classes, types or inline functions may be contained more than once (due to inclusion of the same .hpp file in different modules) If these definitions are not identical, undefined behavior will occur Beware of version mismatch between headers and libraries Diagnostics is usually poor (by linker)

28 Error: Duplicate class definition "C"
ODR #1 violation t.hpp class C { /* DEFINITION */ }; a.hpp #include "t.hpp" void af(C p); b.hpp #include "t.hpp" void bf(C q); Compiler Error: Duplicate class definition "C" m.cpp #include "a.hpp" #include "b.hpp" void z(C r) { af(r); bf(r); }

29 ODR #1 protection Compiler t.hpp #ifndef t_hpp_ #define t_hpp_
class C { /* DEFINITION */ }; #endif a.hpp #ifndef a_hpp_ #define a_hpp_ #include "t.hpp" void af(C p); #endif b.hpp #ifndef b_hpp_ #define b_hpp_ #include "t.hpp" void bf(C q); #endif Compiler OK m.cpp #include "a.hpp" #include "b.hpp" void z(C r) { af(r); bf(r); }

30 Error: Duplicate symbol "F()"
ODR #2 violation a.cpp #include "f.hpp" int main(int,char**) { return F(); } a.obj F(): main: export F(), main Compiler f.hpp #ifndef f_hpp #define f_hpp int F() { /* CODE */ } #endif Linker Error: Duplicate symbol "F()" b.cpp #include "f.hpp" b.obj F(): export F() Compiler

31 ODR #2 protection Compiler Linker Compiler a.cpp
#include "f.hpp" int main(int,char**) { return F(); } a.obj F(): main: export F(), main Compiler f.hpp #ifndef f_hpp #define f_hpp inline int F() { /* CODE */ } #endif p.exe F(): main: export main Linker b.cpp #include "f.hpp" b.obj F(): export F() Compiler

32 Placement of declarations
Every name must be declared before its first use In every translation unit which uses it “Before” refers to the text produced by inclusion and conditional compilation directives Special handling of member function bodies Compilation of the body of a member function... if the body is present inside its class definition ... is delayed to the end of its class definition thus, declarations of all class members are visible to the body The placement of declaration defines the scope of the name Declaration always uses an unqualified name Exception: Friend functions Friend function declaration inside a class may declare the name outside the class (if not already defined)

33 Placement of declarations
class C { public: D f1(); // error: D not declared yet int f2() { D x; return x.f3(); } // OK, compilation delayed class D { int f3(); }; friend C f4(); // declares global f4 and makes it friend private: int m_; C::D C::f1() { return D{}; } // qualified name C::D required outside C int C::D::f3() { return 0; } // this could be static member function void C::f5() {} // error: cannot declare outside the required scope C f4() { C x; x.m_ = 1; return x; } // friends may access private members

34 Placement of definitions
Type alias (typedef/using), enumeration type, constant Must be defined before first use (as seen after preprocessing) Class/struct Class/struct C must be defined before its first non-trivial use: (member) variable definition of type C, inheriting from class C creation/copying/moving/destruction of an object of type C access to any member of C Trivial use is satisfied with a declaration constructing complex types from C declaring functions accepting/returning C manipulating with pointers/references to C Inline function must be defined anywhere in each translation unit which contains a call the definition is typically placed in a .hpp file Non-inline function, global/static variable must be defined exactly once in the program (if used) the definition is placed in a .cpp file

35 Class and type definitions
Declaration Definition Class class A; class A { ... }; Structure (almost equivalent to class) struct A; struct A { Union (unusable in C++) union A; union A { Type alias (old style) 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; Type alias (C++11 style) using A2 = A; using AFP2 = A (*)();

36 Function declarations and definitions
non-inline Declaration (.hpp or .cpp) Definition (.cpp) Global function int f( int, int); int f( int p, int q) { return p + q;} Static member function class A { static int f( int p); }; int A::f( int p) { return p + 1; } Nonstatic member function int f( int p); Virtual member function virtual int f( int p); int A::f( int) { return 0; inline Definition (.hpp or .cpp) Global inline function inline int f( int p, int q) { return p + q; Nonstatic inline member fnc (a) inline int A::f( int p) Nonstatic inline member fnc (b) int f( int p) { return p+1;}

37 Variable declarations and definitions
Global variable extern int x, y, z; int x; int y = 729; int z(729); int u{729}; Static member variable class A { static int x, y, z; }; int A::x; int A::y = 729; int A::z( 729); int A::z{ 729}; Constant member static const int x = 729; Static local variable void f() { static int x; static int y = 7, z( 7); static int u{ 7}; } Nonstatic member variable int x, y; Nonstatic local variable int x; int y = 7, z( 7); int u{ 7}; C++11 C++11 C++11 C++11

38 Storage classes Where data reside... Static storage
Global, static member, static local variables, string constants One instance per process Allocated by compiler/linker/loader (listed in .obj/.dll/.exe) Thread-local storage Variables marked "thread_local" One instance per thread Automatic storage (stack or register) Local variables, parameters, anonymous objects, temporaries One instance per function invocation (execution of defining statement) Placement by compiler, space allocated by compiler-generated instructions Dynamic allocation new/delete operators The programmer is responsible for deallocation, no garbage collection Allocation by library routines Significantly slower than other storage classes C++11 NPRG041 Programming in C /2017 David Bednárek

39 Storage classes Where data reside... Static storage
T x; // global variable Thread-local storage thread_local T x; // global variable Automatic storage (stack or register) void f() { T x; // local variable } Dynamic allocation This example uses raw pointers – deprecated in C++11 T * p = new T; // ... delete p; NPRG041 Programming in C /2017 David Bednárek

40 Dynamic allocation Use smart pointers instead of raw (T *) pointers
#include <memory> one owner (pointer cannot be copied) no runtime cost (compared to T *) void f() { std::unique_ptr< T> p = std::make_unique< T>(); std::unique_ptr< T> q = std::move( p); // pointer moved to q, p becomes nullptr } shared ownership runtime cost of reference counting std::shared_ptr< T> p = std::make_shared< T>(); // invokes new std::shared_ptr< T> q = p; // pointer copied; object shared between q and p Memory is deallocated when the last owner disappears Destructor of (or assignment to) the smart pointer invokes delete when required Reference counting cannot deallocate cyclic structures NPRG041 Programming in C /2017 David Bednárek

41 Using auto Compiler can infer the type of a variable from its initialization std::unique_ptr< T> void f() { auto p = std::make_unique< T>(); auto q = std::move( p); // pointer moved to q, p becomes nullptr } std::shared_ptr< T> auto p = std::make_shared< T>(); // invokes new auto q = p; // pointer copied to q Beware of conversions k.size() returns std::size_t which may be larger than int (the type of 0) void f( std::vector< T> & k) { for ( auto i = 0; i < k.size(); ++ i) /* … k[ i] … */ NPRG041 Programming in C /2017 David Bednárek

42 Using pointers in modern C++
Owner of object std::unique_ptr< T>, std::shared_ptr< T> Use only if objects must be allocated one-by-one Possible reasons: Inheritance, irregular life range, graph-like structure, singleton For holding multiple objects of the same type, use std::vector< T> std::weak_ptr< T> To enable circular references with std::shared_ptr< T>, used rarely Modifying observer T * In modern C++, native pointers shall not represent ownership Save T * in another object which needs to modify the T object Beware of lifetime: The observer must stop observing before the owner dies If you are not able to prevent premature owner death, you need shared ownership Read-only observer const T * Save const T * in another object which needs to read the T object Besides pointers, C++ has references Used (by convention) for temporary access during a function call etc. NPRG041 Programming in C /2017 David Bednárek

43 auto owner = std::make_unique< T>(); // std::unique_ptr< T> auto modifying_observer = owner.get(); // T * auto modifying_observer2 = &*owner; // same effect as .get() const T * read_only_observer = owner.get(); // implicit conversion of T * to const T * auto read_only_observer2 = (const T *)owner.get(); // explicit conversion const T * read_only_observer3 = modifying_observer; // implicit conversion NPRG041 Programming in C /2017 David Bednárek

44 Dynamic allocation is slow
compared to static/automatic storage the reason is cache behavior, not only the allocation itself Use dynamic allocation only when necessary variable-sized or large arrays polymorphic containers (containing various objects using inheritance) object lifetimes not corresponding to function invocations Avoid data structures with individually allocated items linked lists, binary trees, ... std::list, std::map, ... prefer contiguous structures (hash tables, B-trees, etc.) avoiding is difficult - do it only if speed is important This is how C++ programs may be made faster than C#/java C#/java requires dynamic allocation of every class instance NPRG041 Programming in C /2017 David Bednárek

45 Arrays and tuples Homogeneous (arrays) Polymorphic (tuples) Fixed size
// modern: container-style static const std::size_t n = 3; std::array< T, n> a; a[ 0] = /*...*/; a[ 1].f(); // structure/class struct S { T1 x; T2 y; T3 z; }; S a; a.x = /*...*/; a.y.f(); // native arrays (avoid!) T a[ n]; // for generic access std::tuple< T1, T2, T3> a; std::get< 0>( a) = /*...*/; std::get< 1>( a).f(); Variable size std::size_t n = /*...*/; std::vector< T> a(n); std::vector< std::unique_ptr< Tbase>> a; a.push_back( std::make_unique< T1>()); a.push_back( std::make_unique< T2>()); a.push_back( std::make_unique< T3>()); a[ 1]->f(); NPRG041 Programování v C /2017 David Bednárek

46 Array and tuple layouts
std::array< T, 3> or T[ 3] struct { T1 x; T2 y; T3 z;} or std::tuple< T1, T2, T3> T T T T1 T2 T3 std::vector< T> std::vector< std::unique_ptr<Tbase>> T T T T1 T2 T3 NPRG041 Programování v C /2017 David Bednárek

47 copy/move

48 A copy operation on containers and similar types
std::vector< char> x { 'a', 'b', 'c' }; x a b c std::vector< char> y = x; y a b c A copy operation on containers and similar types Requires allocation and copying of dynamically-allocated data It is slow and may throw exceptions NPRG041 Programming in C /2017 David Bednárek

49 After moving, the source is empty
Move std::vector< char> x { 'a', 'b', 'c' }; x a b c std::vector< char> y = std::move(x); y After moving, the source is empty Exact meaning depends on the type A move operation usually does no allocation It is fast and does not throw exceptions NPRG041 Programming in C /2017 David Bednárek

50 Move operation is invoked instead of copy, if
the source is explicitly marked with std::move() the source is an r-value temporary object, which cannot be accessed repeatedly return values from functions which return by value explicitly created temporary objects results of casts etc. NPRG041 Programming in C /2017 David Bednárek

51 Pointer/reference conventions

52 C++ allows several ways of passing links to objects
Pointer/references C++ allows several ways of passing links to objects smart pointers C-like pointers references Technically, all the forms allow almost everything At least using dirty tricks to bypass language rules By convention, the use of a specific form signalizes some intent Conventions (and language rules) limits the way how the object is used Conventions help to avoid "what-if" questions What if someone destroys the object I am dealing with? What if someone modifies the contents of the object unexpectedly? ...

53 Forms of pointers in C++
References T &, const T &, T && Built in C++ Must be initialized to point to an object, cannot be redirected later Syntactically identical to values when used (r.a) Raw pointers T *, const T * Built in C/C++ Requires special operators to access the referenced value (*p, p->a) Pointer arithmetics allows to access adjacent values residing in arrays Manual allocation/deallocation Smart pointers std::shared_ptr< T>, std::unique_ptr< T> Class templates in standard C++ library Operators to access the referenced value same as with raw pointers (*p, p->a) Represents ownership - automatic deallocation on destruction of the last reference Iterators K::iterator, K::const_iterator Classes associated to every kind of container (K) in standard C++ library Returned by container functions as pointers to container elements Pointer arithmetics allows to access adjacent values in the container

54 References in C++ References may be used only in some contexts
Formal arguments of functions (almost always safe and useful) Like passing by reference in other languages (but more complex) Return values of functions (dangerous but sometimes necessary) Local variables (sometimes useful, particularly with auto &&) Static variables, data members of classes (limited usability, use a pointer or std::ref instead) References must be initialized and cannot be redirected later All uses of references work as if they were the referenced object References have three flavors Modifiable L-value reference T & The actual argument (init value) must be an L-value, i.e. a repeatedly accessible object Const (L-value) reference const T & The actual argument may be anything of type T R-value reference T && The actual argument must be an R-value, i.e. a temporary object or marked with std::move()

55 References with function templates and auto
Universal reference As template function argument template< typename T> void f( T && p) As auto variable auto && x = /*...*/; May be bound to both R-values and L-values Beware, there are reference combining rules U a; auto && x = a; // decltype(x) == U & f( a); // T == U &

56 Passing a pointer/reference in C++ - conventions
What the recipient may do? For how long? What the others will do meanwhile? std::unique_ptr<T> Modify the contents and destroy the object As required Nothing std::shared_ptr<T> Modify the contents Read/modify the contents T * Until notified to stop/by agreement const T * Read the contents T & During a call/statement Nothing (usually) T && Steal the contents Forever const T &

57 Passing arguments in C++
Guidelines for formal argument types If the function needs to modify the object use modifiable reference: T & otherwise, if copying of T is really cheap pass by value: T otherwise, if the type does not support copying pass by R-value reference: T && otherwise, if you want it to be really fast implement two versions of the function, with const T & and T && otherwise, if the function stores a copy of the object somewhere use std::move() when storing the object otherwise use const reference: const T & These guidelines do not apply to return values and other contexts

58 Returning values from functions
Function return type guidelines If the function provides access to an element of a data structure (e.g. operator[]) and if you want to allow modification to the element use modifiable L-value reference: T & otherwise use constant reference: const T & The returned object must survive at least a moment after exiting the function In all other cases pass by value: T If the return statement contains a (local) variable whose copying is not really cheap, use std::move() here If a function computes or somehow constructs the returned value, it cannot return by reference the computed value is stored only in local objects which are destroyed before the function is exited the only accessible object which survives is the temporary created by the calling function to hold the returned value (the return statement initializes this temporary)

59 Typical function headers
Providing writable access to an element of a data structure T & get_element( std::vector< T> & v, std::size_t i) { return v[ i]; } Read-only access when the data structure is read-only const T & get_element( const std::vector< T> & v, std::size_t i) Returning a newly constructed value std::string concat( const std::string & a, const std::string & b) { auto tmp = a; tmp.append( b); return std::move( tmp); } With an anonymous temporary Complex concat( const Complex & a, const Complex & b) { return Complex{ a.re + b.re, a.im + b.im}; }

60 Typical function headers
To provide usable access, two functions are required Different headers, usually (syntactically) the same body Global functions: T & get_element( std::vector< T> & v, std::size_t i) { return v[ i]; } const T & get_element( const std::vector< T> & v, std::size_t i) Member functions: class my_hidden_vector { public: T & get_element( std::size_t i) { return v_[ i]; } const T & get_element(std::size_t i) const private: std::vector< T> & v_; };

61 Typical function headers
For read-only arguments passed by reference, const is necessary Global function: std::string concat( const std::string & a, const std::string & b) { auto tmp = a; tmp.append( b); return std::move( tmp); } Member functions: class my_hidden_string { public: my_hidden_string concat(const my_hidden_string & b) const; }; Otherwise, the argument could not be bound to an R-value std::string concat2(std::string & a, std::string & b); // WRONG u = concat2( concat2( x, y), z); // ERROR

62 Pointers and references - examples
NPRG041 Programming in C /2017 David Bednárek

63 Transferring unique ownership
channel ch; void send_hello() { std::unique_ptr< packet> p = std::make_unique< packet>(); p->set_contents( "Hello, world!"); ch.send( std::move( p)); // p is nullptr now } void dump_channel() while ( ! ch.empty() ) std::unique_ptr< packet> m = ch.receive(); std::cout << m->get_contents(); // the packet is deallocated here class packet { /*...*/ }; class channel { public: void send( std::unique_ptr< packet> q); bool empty() const; std::unique_ptr< packet> receive(); private: /*...*/ }; NPRG041 Programming in C /2017 David Bednárek

64 Transferring unique ownership
channel ch; void send_hello() { std::unique_ptr< packet> p = std::make_unique< packet>(); p->set_contents( "Hello, world!"); ch.send( std::move( p)); // p is nullptr now } void dump_channel() while ( ! ch.empty() ) std::unique_ptr< packet> m = ch.receive(); std::cout << m->get_contents(); // the packet is deallocated here class packet { /*...*/ }; class channel { public: void send( std::unique_ptr< packet> q) q_.push_back( std::move( q)); } std::unique_ptr< packet> receive() std::unique_ptr< packet> r = std::move( q_.front()); // remove the nullptr from the queue q_.pop_front(); return r; private: std::deque< std::unique_ptr< packet>> q_; }; NPRG041 Programming in C /2017 David Bednárek

65 Shared ownership class sender { public: sender( std::shared_ptr< channel> ch) : ch_( ch) {} void send_hello() { /*...*/ ch_->send( /*...*/); } private: std::shared_ptr< channel> ch_; }; class recipient { recipient( std::shared_ptr< channel> ch) void dump_channel() { /*...*/ = ch_->receive(); /*...*/ } } class channel { /*...*/ }; std::unique_ptr< sender> s; std::unique_ptr< recipient> r; void init() { std::shared_ptr< channel> ch = std::make_shared< channel>(); s.reset( new sender( ch)); r.reset( new recipient( ch)); } void kill_sender() { s.reset(); } void kill_recipient() { r.reset(); } The server and the recipient may be destroyed in any order The last one will destroy the channel NPRG041 Programming in C /2017 David Bednárek

66 Accessing without ownership transfer
class sender { public: sender( channel * ch) : ch_( ch) {} void send_hello() { /*...*/ ch_->send( /*...*/); } private: channel * ch_; }; class recipient { recipient( channel * ch) void dump_channel() { /*...*/ = ch_->receive(); /*...*/ } } class channel { /*...*/ }; std::unique_ptr< channel> ch; std::unique_ptr< sender> s; std::unique_ptr< recipient> r; void init() { ch = std::make_unique< channel>(); s = std::make_unique< sender>( ch.get()); r = std::make_unique< recipient( ch.get()); } void shutdown() { s.reset(); r.reset(); ch.reset(); The server and the recipient must be destroyed before the destruction of the channel NPRG041 Programming in C /2017 David Bednárek

67 Holding pointers to locally allocated objects
class sender { public: sender( channel * ch) : ch_( ch) {} void send_hello() { /*...*/ ch_->send( /*...*/); } private: channel * ch_; }; class recipient { recipient( channel * ch) void dump_channel() { /*...*/ = ch_->receive(); /*...*/ } } void do_it( sender &, receiver &); void do_it_all() { channel ch; sender s( & ch); recipient r( & ch); do_it( s, r); } The need to use "&" in constructor parameters warns of long life of the reference "&" - converts reference to pointer "*" - converts pointer to reference Local variables are automatically destructed in the reverse order of construction NPRG041 Programming in C /2017 David Bednárek

68 Class holding a reference
class sender { public: sender( channel & ch) : ch_( ch) {} void send_hello() { /*...*/ ch_.send( /*...*/); } private: channel & ch_; }; class recipient { recipient( channel & ch) void dump_channel() { /*...*/ = ch_.receive(); /*...*/ } } void do_it( sender &, receiver &); void do_it_all() { channel ch; sender s( ch); recipient r( ch); do_it( s, r); } s and r will hold the reference to ch for their lifetime There is no warning of that! If references are held by locally allocated objects, everything is OK Destruction occurs in reverse order NPRG041 Programming in C /2017 David Bednárek

69 ERROR: Passing a reference to local object out of its scope
class sender { public: sender( channel & ch) : ch_( ch) {} void send_hello() { /*...*/ ch_.send( /*...*/); } private: channel & ch_; }; class recipient { recipient( channel & ch) void dump_channel() { /*...*/ = ch_.receive(); /*...*/ } } std::unique_ptr< sender> s; std::unique_ptr< recipient> r; void init() { channel ch; s = std::make_unique< sender>( ch); r = std::make_unique< recipient>( ch); } ch will die sooner than s and r s and r will access invalid object Fatal crash sooner or later Nothing warns of this behavior Prefer pointers in this case NPRG041 Programming in C /2017 David Bednárek

70 ERROR: Killing an object in use
class sender { public: sender( channel & ch) : ch_( ch) {} void send_hello() { /*...*/ ch_.send( /*...*/); } private: channel & ch_; }; class recipient { recipient( channel & ch) void dump_channel() { /*...*/ = ch_.receive(); /*...*/ } } std::unique_ptr< channel> ch; void do_it() { ch = std::make_unique< channel>(); sender s( * ch); recipient r( * ch); do_it( s, r); ch = std::make_unique< channel>); } ch is destructed before s and r Fatal crash sooner or later Rare programming practice NPRG041 Programming in C /2017 David Bednárek

71 Allowing access temporarily
channel ch; void send_hello() { std::unique_ptr< packet> p = std::make_unique< packet>; p->set_contents( "Hello, world!"); ch.send( std::move( p)); // p is nullptr now } void dump_channel() while ( ! ch.empty() ) std::unique_ptr< packet> m = ch.receive(); std::cout << m->get_contents(); // the packet is deallocated here class packet { void set_contents( const std::string & s); const std::string & get_contents() const; /*...*/ }; get_contents returns a reference to data stored inside the packet const prohibits modification How long the reference is valid? Probably until modification/destruction of the packet It will last at least during the statement containing the call Provided there is no other action on the packet in the same statement set_contents receives a reference to data stored elsewhere the reference is valid throughout the call NPRG041 Programming in C /2017 David Bednárek

72 A setter without conversions – various implementations
Before C++11 Simple in C++11 Full in C++11 class packet { public: void set_contents(const std::string & s) { data_ = s; } private: std::string data_; }; If the actual is an L-value, there is no better solution std::string my_string = /*...*/; p->set_contents( my_string); copy from my_string to data_ this operation may recycle the space held previously by data_ If the actual is an R-value, there is unnecessary copying p->set_contents( "Hello, world!"); p->set_contents( my_string + "!"); p->set_contents( std::move(my_string)); copy to data_ class packet { public: void set_contents(std::string s) { data_ = std::move( s); } private: std::string data_; }; If the actual is an L-value std::string my_string = /*...*/; p->set_contents( my_string); copy from my_string to s move from s to data recycling not possible If the actual is an R-value p->set_contents( "Hello, world!"); p->set_contents( my_string + "!"); p->set_contents( std::move(my_string)); move to s move from s to data_ class packet { public: void set_contents(const std::string & s) { data_ = s; } void set_contents(std::string && s) { data_ = std::move( s); } private: std::string data_; }; If the actual is an L-value std::string my_string = /*...*/; p->set_contents( my_string); copy from my_string to data_ recycling possible If the actual is an R-value p->set_contents( "Hello, world!"); p->set_contents( my_string + "!"); p->set_contents( std::move(my_string)); move to data_ includes deallocation of the space held previously NPRG041 Programming in C /2017 David Bednárek

73 A setter without conversions – various implementations
Full in C++11 Generic in C++11 class packet { public: void set_contents(const std::string & s) { data_ = s; } void set_contents(std::string && s) { data_ = std::move( s); } private: std::string data_; }; If the actual is an L-value std::string my_string = /*...*/; p->set_contents( my_string); copy from my_string to data_ recycling possible If the actual is an R-value p->set_contents( "Hello, world!"); p->set_contents( my_string + "!"); p->set_contents( std::move(my_string)); move to data_ includes deallocation of the space held previously class packet { public: template< typename X> void set_contents(X && s) { data_ = std::forward< X>( s); } private: std::string data_; }; If the actual is of type std::string, the behavior is identical to the two-function implementation std::forward is a conditional variant of std::move for universal references If the actual is a different type (e.g. char[14]) p->set_contents( "Hello, world!"); the conversion is done inside the function there may be a specialized operator= for this type recycling possible otherwise, the sequence is conversion, including allocation move to data_ NPRG041 Programming in C /2017 David Bednárek

74 Returning by reference
Functions which compute their return values must NOT return by reference the computed value usually differs from values of arguments the value of arguments must not be changed there is nothing that the reference might point to Invalid idea #1: Local variable Complex & add( const Complex & a, const Complex & b) { Complex r( a.Re + b.Re, a.Im + b.Im); return r; } RUNTIME ERROR: r disappears during exit from the function before the calling statement can read it

75 Returning by reference
Functions which compute their return values must NOT return by reference the computed value usually differs from values of arguments the value of arguments must not be changed there is nothing that the reference might point to Invalid idea #2: Dynamic allocation Complex & add( const Complex & a, const Complex & b) { Complex * r = new Complex( a.Re + b.Re, a.Im + b.Im); return * r; } PROBLEM: who will deallocate the object?

76 Returning by reference
Functions which compute their return values must NOT return by reference the computed value usually differs from values of arguments the value of arguments must not be changed there is nothing that the reference might point to Invalid idea #3: Global variable Complex g; Complex & add( const Complex & a, const Complex & b) { g = Complex( a.Re + b.Re, a.Im + b.Im); return g; } PROBLEM: the variable is shared Complex a, b, c, d, e = add( add( a, b), add( c, d));

77 Returning by reference
Functions which compute their return values must return by value the computed value usually differs from values of arguments the value of arguments must not be changed there is nothing that a reference might point to (The only) correct function interface: Complex add( const Complex & a, const Complex & b) { Complex r( a.Re + b.Re, a.Im + b.Im); return r; } This body may be shortened to (equivalent by definition): return Complex( a.Re + b.Re, a.Im + b.Im);

78 Returning by reference
Functions which enable access to existing objects may return by reference the object must survive the return from the function Example: template< typename T, std::size_t N> class array { public: T & at( std::size_t i) { return a_[ i]; } private: T a_[ N]; }; Returning by reference may allow modification of the returned object array< int, 5> x; x.at( 1) = 2;

79 Returning by reference
Functions which enable access to existing objects may return by reference Often there are two versions of such function template< typename T, std::size_t N> class array { public: Allowing modification of elements of a modifiable container T & at( std::size_t i) { return a_[ i]; } Read-only access to elements of a read-only container const T & at( std::size_t i) const private: T a_[ N]; }; void f( array< int, 5> & p, const array< int, 5> & q) { p.at( 1) = p.at( 2); // non-const version in BOTH cases int x = q.at( 3); // const version }

80 Returning by reference
Functions which enable access to existing objects may return by reference The object must survive the return from the function template< typename T> class vector { public: back returns the last element which will remain on the stack it may allow modification of the element T & back(); const T & back() const; this pop_back removes the last element from the stack and returns its value it must return by value - slow (and exception-unsafe) T pop_back(); therefore, in standard library, the pop_back function returns nothing void pop_back(); // ... };

81 Class NPRG041 Programming in C /2017 David Bednárek

82 Class in C++ is an extremely powerful construct
class X { /*...*/ }; Class in C++ is an extremely powerful construct Other languages often have several less powerful constructs (class+interface) Requires caution and conventions Three degrees of usage Non-instantiated class - a pack of declarations (used in generic programming) Class with data members Class with inheritance and virtual functions (object-oriented programming) class = struct struct members are by default public by convention used for simple or non-instantiated classes class members are by default private by convention used for large classes and OOP

83 Three degrees of classes
Non-instantiated class Class with data members Classes with inheritance class X { public: typedef int t; static const int c = 1; static int f( int p) { return p + 1; } }; class Y { public: Y() : m_( 0) {} int get_m() const { return m_; } void set_m( int m) { m_ = m; } private: int m_; }; class U { public: void f() { f_(); } private: virtual void f_() = 0; }; class V : public U { V() : m_( 0) {} int m_; virtual void f_() { ++ m_; } NPRG041 Programming in C /2017 David Bednárek

84 Type and static members of classes
class X { public: class N { /*...*/ }; typedef unsigned long t; using t2 = unsigned long; static const t c = 1; static t f( t p) { return p + v_; } private: static t v_; // declaration of X::v_ }; X::t X::v_ = X::c; // definition of X::v_ void f2() { X::t a = 1; a = X::f( a); } Type and static members... Nested class definitions typedef/using definitions static member constants static member functions static member variables ... are not bound to any class instance (object) Equivalent to global types/variables/functions But referenced using qualified names (prefix X::) Encapsulation in a class avoids name clashes But namespaces do it better Some members may be private Class may be passed to a template NPRG041 Programming in C /2017 David Bednárek

85 Uninstantiated classes vs. namespaces
Class definitions are intended for objects Static members must be explicitly marked Class members may be public/protected/private class X { public: class N { /*...*/ }; typedef unsigned long t; static const t c = 1; static t f( N p) { return p.m + v; } private: static t v; // declaration of X::v }; Class must be defined in one piece Except of definitions placed outside X::t X::v = X::c; // definition of X::v Access to members requires qualified names void f2() { X::N a; auto b = X::f( a); } A class may become a template argument This is the (only) reason for uninstantiated classes using my_class = some_generic_class< X>; Namespace members are always static No objects can be made from namespaces Functions/variables are not automatically inline/extern namespace X { class N { /*...*/ }; typedef unsigned long t; const t c = 1; extern t v; // declaration of X::v }; Namespace may be reopened and member declarations added Namespace may be split into several header files inline t f( N p) { return p.m + v; } Definitions of previously declared namespace members may be outside X::t X::v = X::c; // definition of X::v Namespace members can be made directly visible "using", "using namespace" Functions in namespaces are visible by argument-dependent lookup void f2() { X::N a; auto b = f( a); // ADL: calls X::f because the class type of a is a member of X using X::t; t b = 2; using namespace X; b = c; } NPRG041 Programming in C /2017 David Bednárek

86 Class with data members
class Y { public: Y() : m_( 0) {} int get_m() const { return m_; } void set_m( int m) { m_ = m; } private: int m_; }; Class (i.e. type) may be instantiated (into objects) Using a variable of class type Y v1; This is NOT a reference! Dynamically allocated Held by a (smart) pointer auto p = std::make_unique< Y>(); auto q = std::make_shared< Y>(); Element of a larger type typedef std::array< Y, 5> A; class C1 { public: Y v; }; class C2 : public Y {}; Embedded into the larger type NO explicit instantiation by new! NPRG041 Programming in C /2017 David Bednárek

87 Class with data members
class Y { public: Y() : m_( 0) {} int get_m() const { return m_; } void set_m( int m) { m_ = m; } private: int m_; }; Class (i.e. type) may be instantiated (into objects) Y v1; auto p = std::make_unique< Y>(); Non-static data members constitute the object Non-static member functions are invoked on the object Object must be specified when referring to non-static members v1.get_m() p->set_m(0) References from outside may be prohibited by "private"/"protected" v1.m_ // error Only "const" methods may be called on const objects const Y * pp = p.get(); // secondary pointer pp->set_m(0) // error NPRG041 Programming in C /2017 David Bednárek

88 Inheritance and virtual functions
NPRG041 Programming in C /2017 David Bednárek

89 Inheritance Derived class is a descendant of Base class
class Base { /* ... */ }; class Derived : public Base { /* ... */ } Derived class is a descendant of Base class Contains all types, data elements and functions of Base Because of this, a pointer/reference to Derived may be silently converted to a pointer/reference to Base The opposite conversion is available as explicit cast New types/data/functions may be added Hiding old names by new names is not wise, except for virtual functions Functions declared as virtual in Base may change their behavior by reimplementation in Derived class Base { virtual void f() { /* ... */ } }; class Derived : public Base {

90 Virtual functions class Base { virtual void f() { /* ... */ } }; class Derived : public Base { Virtual function call works only in the presence of pointers or references std::unique_ptr<Base> p = std::make_unique< Derived>(); // automatic conversion p->f(); // calls Derived::f although p is pointer to Base Without pointers/references, having functions virtual has no sense Derived d; d.f(); // calls Derived::f even for non-virtual f Base b = d; // slicing = copying a part of an object b.f(); // calls Base::f even for virtual f Slicing is specific to C++

91 Inheritance Inheritance mechanisms in C++ are very strong
Often misused Inheritance shall be used only in these cases ISA hiearachy Eagle IS A Bird Square-Rectangle-Polygon-Drawable-Object Interface-implementation Readable-InputFile Writable-OutputFile (Readable+Writable)-IOFile

92 Inheritance ISA hierarchy Interface-implementation Often combined
C++: Single non-virtual public inheritance class Derived : public Base Abstract classes may contain data (although usually do not) Interface-implementation C++: Multiple virtual public inheritance class Derived : virtual public Base1, virtual public Base2 virtual inheritance merges copies of a base class multiply included via diamond patterns Abstract classes usually contain no data Interfaces are (typically) not used to own (destroy) the object Often combined class Derived : public Base, virtual public Interface1, virtual public Interface2

93 Misuse of inheritance Misuse of inheritance - #1
class Real { public: double Re; }; class Complex : public Real { public: double Im; }; Leads to slicing: double abs( const Real & p) { return p.Re > 0 ? p.Re : - p.Re; } Complex x; double a = abs( x); // it CAN be compiled - but it should not Reference to the derived class may be assigned to a reference to the base class Complex => Complex & => Real & => const Real &

94 Misuse of inheritance Misuse of inheritance - #2
class Complex { public: double Re, Im; }; class Real : public Complex { public: Real( double r); }; Mistake: Objects in C++ are not mathematical objects void set_to_i( Complex & p) { p.Re = 0; p.Im = 1; } Real x; set_to_i( x); // it CAN be compiled - but it should not Real => Real & => Complex &

95 Classes in inheritance
Abstract class Definition in C++: A class that contains some pure virtual functions virtual void f() = 0; Such class are incomplete and cannot be instantiated alone General definition: A class that will not be instantiated alone (even if it could) Defines the interface which will be implemented by the derived classes Concrete class A class that will be instantiated as an object Implements the interface required by its base class

96 Inheritance and destructors
class Base { public: virtual ~Base() {} }; class Derived : public Base { virtual ~Derived() { /* ... */ } std::unique_ptr< Base> p = std::make_unique< Derived>(); p.reset(); If an object is destroyed using delete applied to a pointer to its base class, the destructor of the base class must be virtual Rule of thumb: Every abstract class must have a virtual destructor There is no additional cost (there are other virtual functions) It will be probably needed

97 Constructors and destructors

98 Constructors and destructors
Constructor of class T is a method named T Return type not specified More than one constructor may exist with different arguments Never virtual A constructor is called whenever an object of the type T is created Constructor parameters specified in the moment of creation Some constructors have special meaning Some constructors may be generated by the compiler Constructors cannot be called directly Destructor of class T is a method named ~T No arguments, no return value May be virtual The destructor is called whenever an object of the type T is destroyed The destructors may be generated by the compiler Explicit call must use special syntax

99 Special member functions
Default constructor T(); For object without explicit initialization Generated by compiler if required and if the class has no constructor at all: Data members of non-class types are not initialized Data members of class types and base classes are initialized by calling their default constructors Generation may fail due to non-existence or inaccessibility of element constructors Destructor ~T(); Generated by compiler if required and not defined Calls destructors of data members and base classes If a class derived from T has to be destroyed using T *, the destructor of T must be virtual All abstract classes shall have a virtual destructor virtual ~T();

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

101 copy/move Special member functions Copy constructor Move 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);

102 copy/move Compiler-generated implementation Copy constructor
T( const T & x) = default; applies copy constructor to every element Move constructor T( T && x) = default; applies move constructor to every element Copy assignment operator T & operator=( const T & x) = default; applies copy assignment to every element Move assignment operator T & operator=( T && x) = default; applies move assignment to every element elements are data members and base classes for elements of non-class types, move is equivalent to copy the default keyword allows to enforce generation by the compiler

103 copy/move If needed, compiler will generate the methods automatically under these conditions: Copy constructor/assignment operator if there is no definition for the method and no move method is defined this is backward-compatibility rule; future development of the language will probably make the condition more stringent (no copy/move/destructor at all) Move constructor/assignment operator if no copy/move method is defined and no destructor is defined the default keyword overrides the conditions

104 copy/move Most-frequent cases A harmless class
No copy/move method, no destructor No dangerous data members (raw pointers) A class containing dangerous members Compiler-generated behavior (default) would not work properly No move support (before C++11, still functional but not optimal) T( const T & x); T & operator=( const T & x); ~T(); Full copy/move support T( T && x); T & operator=( T && x);

105 Handling data members in constructors and destructors
copy/move Handling data members in constructors and destructors Numeric types Explicit initialization recommended, no destruction required Compiler-generated copy/move works properly Structs/classes If they have no copy/move methods, they behave as if their members were present directly If they have copy/move methods, they usually do not require special handling Special handling required if the outer class semantics differ from the inner class (e.g., using smart pointers to implement containers) Containers and strings Behave as if their members were present directly Containers are initialized as empty - no need to initialize even containers of numeric types NPRG041 Programming in C /2017 David Bednárek

106 Data members - links without ownership
copy/move Data members - links without ownership References (U&) for class members, use of pointers U* is preferred over U& Explicit initialization required, destruction not required Copy/move constructors work smoothly Copy/move operator= is impossible if assignment is needed, use std::ref_wrapper< T> instead of T & Raw pointers (U*) without ownership semantics Proper deallocation is ensured by someone else Copy/move work smoothly NPRG041 Programming in C /2017 David Bednárek

107 Data members - smart pointers
copy/move Data members - smart pointers std::unique_ptr<U> Explicit initialization not required (nullptr by default) Explicit destruction not required (smart pointers deallocate automatically) Copying is impossible If copying is required, it must be implemented by duplicating the linked object Move methods work smoothly std::shared_ptr<U> Copying works as sharing If sharing semantics is not desired, other methods must be adjusted - all modifying operations must ensure a private copy of the linked object NPRG041 Programming in C /2017 David Bednárek

108 Class patterns POD: Plain-Old-Data Public data members
The user is responsible for initialization class T { public: std::string x_; }; struct often used instead of class struct T {

109 Class patterns All data-members harmless
Every data member have its own constructor The class does not require any constructor class T { public: // ... const std::string & get_x() const { return x_; } void set_x( const std::string & s) { x_ = s; } private: std::string x_; };

110 Class patterns All data-members harmless
Every data member have its own constructor Constructor enables friendly initialization Due to language rules, the parameter-less constructor is often needed too 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) {} // ... other methods ... private: std::string x_, y_; };

111 Class patterns Some slightly dangerous elements
Some elements lack suitable default constructors Numeric types (including bool, char), observer pointers (T *, const T *) A constructor is required to properly initialize these elements Consequently, default (parameterless) constructor is (typically) also required One-parameter constructors marked 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) {} // ... other methods ... private: int x_, y_; };

112 copy/move Less frequent cases A non-copyable and non-movable class
E.g., dynamically allocated "live" objects in simulations T( const T & x) = delete; T & operator=( const T & x) = delete; The delete keyword prohibits automatic default for copy methods Language rules prohibit automatic default for move methods A destructor may be required A movable non-copyable class E.g., an owner of another object (like std::unique_ptr< U>) T( T && x); T & operator=( T && x); ~T(); Language rules prohibit automatic default for copy methods A destructor is typically required

113 Class patterns Classes containing unique_ptr Uncopiable class
But moveable class T { public: T() : p_( std::make_unique< Data>()) {} private: std::unique_ptr< Data> p_; };

114 Class patterns Classes containing unique_ptr Copying enabled class T {
public: T() : p_(std::make_unique< Data>()) {} T( const T & x) : p_(std::make_unique< Data>( * x.p_)) {} T( T && x) = default; T & operator=( const T & x) { return operator=( T( x));} T & operator=( T && x) = default; private: std::unique_ptr< Data> p_; };

115 Class patterns Abstract class Copying/moving prohibited class T {
protected: T() {} T( const T & x) = delete; T & operator=( const T & x) = delete; public: virtual ~T() {} // required for proper deletion of objects };

116 Class patterns Abstract class Cloning support class T { protected:
T( const T & x) = default; // descendants will need it to implement clone T & operator=( const T & x) = delete; public: virtual ~T() {} virtual std::unique_ptr< T> clone() const = 0; };

117 Data members - links with ownership
copy/move Data members - links with ownership Raw pointers (U*) with unique ownership Our class must deallocate the remote object properly Explicit initialization required (allocate or set to zero) Destruction is required (deallocate if not zero) Copy methods must allocate new space a copy data Move methods must clear links in the source object In addition, copy/move operator= must clean the previous contents Raw pointer (U*) with shared ownership Our class must count references and deallocate if needed Destruction is required (decrement counter, deallocate if needed) Copy methods must increment counter NPRG041 Programming in C /2017 David Bednárek

118 Class patterns Some very dangerous elements [avoid whenever possible]
Raw pointers with (exclusive/shared) ownership semantics copy/move constructor/operator= and destructor required Some additional constructor (e.g. default) is also required 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_; };

119 Conversions

120 Special member functions
Conversion constructors class T { T( U x); }; Generalized copy constructor Defines conversion from U to T If conversion effect is not desired, all one-argument constructors must be "explicit": explicit T( U v); Conversion operators operator U() const; Defines conversion from T to U Returns U by value (using copy-constructor of U, if U is a class) Compiler will never use more than one user-defined conversion in a chain

121 Type cast Various syntax styles C-style cast Function-style cast
(T)e Inherited from C Function-style cast T(e) Equivalent to (T)e T must be single type identifier or single keyword Type conversion operators Differentiated by intent (strength and associated danger) of cast: const_cast<T>(e) static_cast<T>(e) reinterpret_cast<T>(e) New - run-time assisted cast: dynamic_cast<T>(e)

122 Dynamic cast Most frequent use
dynamic_cast<T>(e) Most frequent use Converting a pointer to a base class to a pointer to a derived class class Base { public: virtual ~Base(); /* base class must have at least one virtual function */ }; 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 ) { /* ... */ }

123 Class patterns Typické situace

124 Frequently used data types

125 Selected number types bool false, true char character (ASCII, 8 bit)
std::wchar_t character (Unicode, 16/32 bit) int signed integer (~32 bit) unsigned unsigned integer (~32 bit) long long extra large signed integer (~64 bit) unsigned long long extra large unsigned integer (~64 bit) std::size_t unsigned integer large enough for array sizes (32/64 bit) double "double precision" floating-point number (Intel: 64 bit) long double extended precision floating-point number (Intel: 80 bit) std::complex<double> complex number of double precision

126 Important non-number types
std::string string (containing char) std::wstring string (containing std::wchar_t) std::istream input stream (containing char) std::wistream input stream (containing std::wchar_t) std::ostream output stream (containing char) std::wostream output stream (containing std::wchar_t) struct T { … } structure (almost equivalent to class) std::pair<T1,T2> pair of T1 and T2 std::tuple<T1,...> k-tuple of various types std::array<T,n> fixed-size array of T std::vector<T> variable-size array of T std::list<T> doubly linked list of T std::map<K,T> ordered associative container of T indexed by K std::multimap<K,T> ordered associative container with multiplicity of keys std::unordered_map<K,T> hash table of T indexed by K std::unordered_multimap<K,T> hash table with multiplicity of keys

127 Pointer vs. value

128 C#/Java vs. C++ Reference types (C#,Java) Raw pointers (C++)
class T { public int a; } class test { static void f( T z) { z.a = 3; static void g() T x = new T(); // allocation x.a = 1; T y = x; // second reference y.a = 2; // x.a == 2 f( x); // x.a == 3 // garbage collector will later // reclaim the memory when needed class T { public: int a; }; void f( T * z) { z->a = 3; } void g() T * x = new T; // allocation x->a = 1; T * y = x; // second pointer y->a = 2; // x->a == 2 f( x); // x->a == 3 delete x; // manual deallocation

129 C#/Java vs. C++ Reference types (C#,Java) Smart pointers (C++)
class T { public int a; } class test { static void f( T z) { z.a = 3; static void g() T x = new T(); // allocation x.a = 1; T y = x; // second reference y.a = 2; // x.a == 2 f( x); // x.a == 3 // garbage collector will later // reclaim the memory when needed class T { public: int a; }; void f( T * z) { z->a = 3; } void g() std::shared_ptr< T> x = std::make_shared< T>(); // allocation x->a = 1; std::shared_ptr< T> y = x; // second pointer y->a = 2; // x->a == 2 f( x); // x->a == 3 // automatic deallocation // when pointers are destructed

130 C#/Java vs. C++ Reference types (C#,Java) References (C++)
class T { public int a; } class test { static void f( T z) { z.a = 3; static void g() T x = new T(); // allocation x.a = 1; T y = x; // second reference y.a = 2; // x.a == 2 f( x); // x.a == 3 // garbage collector will later // reclaim the memory when needed class T { public: int a; }; void f( T & z) { z.a = 3; } void g() T x; // automatic storage (stack) x.a = 1; T & y = x; // a reference to the stack object y.a = 2; // x.a == 2 f( x); // x.a == 3 // x is destructed on exit

131 C#/Java vs. C++ Value types (C#) Values (C++)
struct T { int a; } class test { static void f( T z) { z.a = 3; static void g() T x; // creation x.a = 1; T y = x; // a copy y.a = 2; // x.a == 1 f( x); // destruction on exit class T { public: int a; }; void f( T z) { z.a = 3; } void g() T x; // creation x.a = 1; T y = x; // a copy y.a = 2; // x.a == 1 f( x); // destruction on exit

132 C#/Java vs. C++ Passing value types by reference (C#)
Passing by lvalue reference (C++) struct T { int a; } class test { static void f( ref T z) { z.a = 3; static void g() T x; // creation x.a = 1; f( ref x); // x.a == 3 class T { public: int a; }; void f( T & z) { z.a = 3; } void g() T x; x.a = 1; f( x); // x.a == 3

133 C#/Java vs. C++ Passing smart pointers by reference (C++)
Passing reference types by reference (C#) Passing smart pointers by reference (C++) class T { public int a; } class test { static void f( ref T z) { z = new T(); // allocation of another object static void g() T x = new T(); // allocation f( ref x); // x is now a different object // deallocation later by GC class T { public: int a; }; void f( std::unique_ptr<T> & z) { z = new T; // allocation of another object // deallocation of the old object } void g() std::unique_ptr< T> x = new T; // allocation f( x); // *x is now a different object // deallocation by destruction of x

134 Standard Template Library
STL Standard Template Library

135 STL Containers Generic data structures
Based on arrays, linked lists, trees, or hash-tables Store objects of given type (template parameter) The container takes care of allocation/deallocation of the stored objects All objects must be of the same type (defined by the template parameter) Containers can not directly store polymorphic objects with inheritance New objects are inserted by copying/moving/constructing in place Containers can not hold objects created outside them Inserting/removing objects: Member functions of the container Reading/modifying objects: Iterators

136 STL – Example #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

137 Sequential containers
STL Sequential containers New objects are inserted in specified location array< T, N> - fixed-size array (no insertion/removal) vector< T> - array, fast insertion/removal at the back end stack< T> - insertion/removal only at the top (back end) priority_queue< T> - priority queue (heap implemented in vector) basic_string< T> - vektor s terminátorem string = basic_string< char> wstring = basic_string< wchar_t> deque< T> - fast insertion/removal at both ends queue< T> - FIFO (insert to back, remove from front) forward_list< T> - linked list list< T> - doubly-linked list

138 Associative containers
STL Associative containers New objects are inserted at a position defined by their properties sets: type T must define ordering relation or hash function maps: stored objects are of type pair< const K, T> type K must define ordering or hash multi-: multiple objects with the same (equivalent) key value may be inserted Ordered (implemented usually by red-black trees) set<T> multiset<T> map<K,T> multimap<K,T> Hashed unordered_set<T> unordered_multiset<T> unordered_map<K,T> unordered_multimap<K,T>

139 STL - Ordered Containers
Ordered containers require ordering relation on the key type Only < is used (no need to define >, <=, >=, ==, !=) In simplest cases, the type has a built-in ordering std::map< std::string, my_value> my_map; If not built-in, ordering may be defined using a global function bool operator<( const my_key & a, const my_key & b) { return /*...*/; } std::map< my_key, my_value> mapa; If global definition is not appropriate, ordering may be defined using a functor struct my_functor { bool operator()( const my_key & a, const my_key & b) const { return /*...*/; } }; std::map< my_key, my_value, my_functor> my_map; If the ordering has run-time parameters, the functor will carry them struct my_functor { my_functor( bool a); /*...*/ bool ascending; }; std::map< my_key, my_value, my_functor> my_map( my_functor( true));

140 STL - Unordered containers
Hashed containers require two functors: hash function and equality comparison struct my_hash { std::size_t operator()( const my_key & a) const { /*...*/ } }; struct my_equal { public: bool operator()( const my_key & a, const my_key & b) const { /*return a == b;*/ } std::unordered_map< my_key, my_value, my_hash, my_equal> my_map; If not explicitly defined by container template parameters, hashed containers try to use generic functors defined in the library std::hash< K> std::equal_to< K> Defined for numeric types, strings, and some other library types std::unordered_map< std::string, my_value> my_map;

141 STL – Iterators Each container defines two member types: iterator and const_iterator using my_container = std::map< my_key, my_value>; using my_iterator = my_container::iterator; using my_const_iterator = my_container::const_iterator; Iterators act like pointers to objects inside the container objects are accessed using operators *, -> const_iterator does not allow modification of the objects An iterator may point to an object inside the container to an imaginary position behind the last object: end()

142 STL – Iterators void example( my_container & c1, const my_container & c2) { Every container defines functions to access both ends of the container begin(), cbegin() - the first object (same as end() if the container is empty) end(), cend() - the imaginary position behind the last object auto i1 = begin( c1); // also c1.begin() auto i2 = cbegin( c1); // also c1.cbegin(), begin( c1), c1.begin() auto i3 = cbegin( c2); // also c2.cbegin(), begin( c2), c2.begin() Associative containers allow searching find( k) - first object equal (i.e. not less and not greater) to k, end() if not found lower_bound( k) - first object not less than k , end() if not found upper_bound( k) - first object greater than k , end() if not found my_key k = /*...*/; auto i4 = c1.find( k); // my_container::iterator auto i5 = c2.find( k); // my_container::const_iterator Iterators may be shifted to neighbors in the container all iterators allow shifting to the right and equality comparison for ( auto i6 = c1.begin(); i6 != c1.end(); ++ i6 ) { /*...*/ } bidirectional iterators (all containers except forward_list) allow shifting to the left -- i1; random access iterators (vector, string, deque) allow addition/subtraction of integers, difference and comparison auto delta = i4 - c1.begin(); // number of objects left to i4; my_container::difference_type === std::ptrdiff_t auto i7 = c1.end() - delta; // the same distance from the opposite end; my_container::iterator if ( i4 < i7 ) auto v = i4[ delta].second; // same as (*(i4 + delta)).second, (i4 + delta)->second }

143 STL – Iterators Caution:
Shifting an iterator before begin() or after end() is illegal for (auto it = c1.end(); it >= c1.begin(); -- it) // ERROR: underruns begin() Comparing iterators associated to different (instances of) containers is illegal if ( c1.begin() < c2.begin() ) // ILLEGAL Insertion/removal of objects in vector/basic_string/deque invalidate all associated iterators The only valid iterator is the one returned from insert/erase std::vector< std::string> c( 10, "dummy"); auto it = c.begin() + 5; // the sixth dummy std::cout << * it; auto it2 = c.insert( std::begin(), "first"); std::cout << * it; // CRASH it2 += 6; // the sixth dummy c.push_back( "last"); std::cout << * it2; // CRASH

144 STL – Insertion/deletion
Containers may be filled immediately upon construction using n copies of the same object std::vector< std::string> c1( 10, "dummy"); or by copying from another container std::vector< std::string> c2( c1.begin() + 2, c1.end() - 2); Expanding containers - insertion insert - copy or move an object into container emplace - construct a new object (with given parameters) inside container Sequential containers position specified explicitly by an iterator new object(s) will be inserted before this position c1.insert( c1.begin(), "front"); c1.insert( c1.begin() + 5, "middle"); c1.insert( c1.end(), "back"); // same as c1.push_back( "back");

145 STL – insertion/deletion
insert by copy slow if copy is expensive std::vector< std::vector< int>> c3; not applicable if copy is prohibited std::vector< std::unique_ptr< T>> c4; insert by move explicitly using std::move auto p = std::make_unique< T>(/*…*/); c4.push_back( std::move( p)); implicitly when argument is rvalue (temporal object) c3.insert( begin( c3), std::vector< int>( 100, 0)); emplace constructs a new element from given arguments c3.emplace( begin( c3), 100, 0);

146 STL – insertion/deletion
Shrinking containers - erase/pop single object my_iterator it = /*...*/; c1.erase( it); c2.erase( c2.end() - 1); // same as c2.pop_back(); range of objects my_iterator it1 = /*...*/, it2 = /*...*/; c1.erase( it1, it2); c2.erase( c2.begin(), c2.end()); // same as c2.clear(); by key (associative containers only) my_key k = /*...*/; c3.erase( k);

147 Algorithms

148 Set of generic functions working on containers
Algorithms Set of generic functions working on containers cca 90 functions, trivial or sophisticated (sort, make_heap, set_intersection, ...) #include <algorithm> Containers are accessed indirectly - using iterators Typically a pair of iterator specifies a range inside a container Algorithms may be run on complete containers or parts Anything that looks like an iterator may be used Some algorithms are read-only The result is often an iterator E.g., searching in non-associative containers Most algorithms modify the contents of a container Copying, moving (using std::move), or swapping (pomocí std::swap) elements Applying user-defined action on elements (defined by functors) Iterators does not allow insertion/deletion of container elements The space for "new" elements must be created before calling an algorithm Removal of unnecessary elements must be done after returning from an algorithm

149 Algorithms Iterators does not allow insertion/deletion of container elements The space for "new" elements must be created before calling an algorithm my_container c2( c1.size(), 0); std::copy( c1.begin(), c1.end(), c2.begin()); Note: This example does not require algorithms: my_container c2( c1.begin(), c1.end()); Removal of unnecessary elements must be done after returning from an algorithm auto my_predicate = /*...*/; // some condition my_container c2( c1.size(), 0); // max size my_iterator it2 = std::copy_if( c1.begin(), c1.end(), c2.begin(), my_predicate); c2.erase( it2, c2.end()); // shrink to really required size my_iterator it1 = std::remove_if( c1.begin(), c1.end(), my_predicate); c1.erase( it1, c1.end()); // really remove unnecessary elements

150 STL – Algorithms Fake iterators
Algorithms may accept anything that works like an iterator The required functionality is specified by iterator category Input, Output, Forward, Bidirectional, RandomAccess Every iterator must specify its category and some other properties std::iterator_traits Some algorithms change their implementation based on the category (std::distance) Inserters my_container c2; // empty auto my_inserter = std::back_inserter( c2); std::copy_if( c1.begin(), c1.end(), my_inserter, my_predicate); Text input/output auto my_inserter2 = std::ostream_iterator< int>( std::cout, " "); std::copy( c1.begin(), c1.end(), my_inserter2);

151 Functors NPRG041 Programming in C /2017 David Bednárek

152 STL – Functors Example - for_each
template<class InputIterator, class Function> Function for_each( InputIterator first, InputIterator last, Function f) { for (; first != last; ++first) f( * first); return f; } f may be anything that has the function call operator f(x) a global function (pointer to function), or a functor, i.e. a class containing operator() The function f (its operator()) is called for each element in the given range The element is accessed using the * operator which typically return a reference The function f can modify the elements of the container

153 STL – Algorithms A simple application of for_each [C++11] Lambda
void my_function( double & x) { x += 1; } void increment( std::list< double> & c) std::for_each( c.begin(), c.end(), my_function); [C++11] Lambda New syntax construct - generates a functor for_each( c.begin(), c.end(), []( double & x){ x += 1;});

154 STL – Algorithms Passing parameters requires a functor
class my_functor { public: double v; void operator()( double & x) const { x += v; } my_functor( double p) : v( p) {} }; void add( std::list< double> & c, double value) { std::for_each( c.begin(), c.end(), my_functor( value)); } Equivalent implementation using lambda std::for_each( c.begin(), c.end(), [value]( double & x){ x += value;});

155 STL – Algoritmy A functor may modify its contents
class my_functor { public: double s; void operator()( const double & x) { s += x; } my_functor() : s( 0.0) {} }; double sum( const std::list< double> & c) { my_functor f = std::for_each( c.begin(), c.end(), my_functor()); return f.s; } Using lambda (the generated functor contains a reference to s) { double s = 0.0; for_each( c.begin(), c.end(), [& s]( const double & x){ s += x;}); return s;

156 Lambda

157 C++11 [ capture ]( params ) mutable -> rettype { body }
Lambda expressions Lambda expression [ capture ]( params ) mutable -> rettype { body } Declares a class class ftor { public: ftor( TList ... plist) : vlist( plist) ... { } rettype operator()( params ) const { body } private: TList ... vlist; }; vlist determined by local variables used in the body TList determined by their types and adjusted by capture operator() is const if mutable not present The lambda expression corresponds to creation of an anonymous object ftor( vlist ...) C++11

158 Lambda expressions – return types
Return type of the operator() Explicitly defined []() -> int { /*…*/ } Automatically derived if body contains just one return statement []() { return V; } void otherwise C++11

159 Lambda expressions – capture
Defines which external variables are accessible and how local variables in the enclosing function this, if used in a member function Determines the data members of the functor Explicit capture The external variables explicitly listed in capture [a,&b,c,&d,this] variables marked & passed by reference, the others by value Implicit capture The required external variables determined automatically by the compiler, capture defines the mode of passing [=] [=,&b,&d] passed by value, the listed exceptions by reference [&] [&,a,c] passed by reference, the listed exceptions by value C++11

160 Templates Templates

161 Templates Template Class templates Function templates
a generic piece of code parameterized by types and integer constants Class templates Global classes Classes nested in other classes, including class templates template< typename T, std::size_t N> class array { /*...*/ }; Function templates Global functions Member functions, including constructors template< typename T> inline T max( T x, T y) { /*...*/ } Type templates [C++11] using array3 = std::array< T, 3>;

162 Templates Template instantiation
Using the template with particular type and constant parameters Class and type templates: parameters specified explicitly std::array< int, 10> x; Function templates: parameters specified explicitly or implicitly Implicitly - derived by compiler from the types of value arguments int a, b, c; a = max( b, c); // calls max< int> Explicitly a = max< double>( b, 3,14); Mixed: Some (initial) arguments explicitly, the rest implicitly array< int, 5> v; x = get< 3>( v); // calls get< 3, array< int, 5>>

163 Multiple templates with the same name
Class and type templates: one "master" template template< typename T> class vector {/*...*/}; any number of specializations which override the master template partial specialization template< typename T, std::size_t n> class unique_ptr< T [n]> {/*...*/}; explicit specialization template<> class vector< bool> {/*...*/}; Function templates: any number of templates with the same name shared with non-templated functions

164 Writing templates Compiler needs hints from the programmer
Dependent names have unknown meaning/contents template< typename T> class X { type names must be explicitly designated using U = typename T::B; typename U::D p; // U is also a dependent name using Q = typename Y<T>::C; void f() { T::D(); } // T::D is not a type explicit template instantiations must be explicitly designated bool g() { return 0 < T::template h<int>(); } } members inherited from dependent classes must be explicitly designated template< typename T> class X : public T const int K = T::B + 1; // B is not directly visible although inherited void f() { return this->a; }

165 Variadic templates

166 C++11 Variadic templates template< typename ... TList>
Template heading Allows variable number of type arguments template< typename ... TList> class C { /* ... */ }; typename ... declares named template parameter pack may be combined with regular type/constant arguments template< typename T1, int c2, typename ... TList> class D { /* ... */ }; also in partial template specializations template< typename T1, typename ... TList> class C< T1, TList ...> { /* ... */ }; C++11

167 C++11 Variadic templates template parameter pack - a list of types
template< typename ... TList> template parameter pack - a list of types may be referenced inside the template: always using the suffix ... as type arguments to another template: X< TList ...> Y< int, TList ..., double> in argument list of a function declaration: void f( TList ... plist); double g( int a, double c, TList ... b); this creates a named function parameter pack in several less frequent cases, including base-class list: class E : public TList ... number of elements in the parameter pack: sizeof...(TList) C++11

168 C++11 Variadic templates template< typename ... TList>
void f( TList ... plist); named function parameter pack may be referenced inside the function: always using the suffix ... as parameters in a function call or object creation: g( plist ...) new T( a, plist ..., 7) T v( b, plist ..., 8); constructor initialization section (when variadic base-class list is used) E( TList ... plist) : TList( plist) ... { } other infrequent cases C++11

169 C++11 Variadic templates template< typename ... TList>
void f( TList ... plist); parameter packs may be wrapped into a type construction/expression the suffix ... works as compile-time "for_each" parameter pack name denotes the place where every member will be placed more than one pack name may be used inside the same ... (same length required) the result is a list of types (in a template instantiation or a function parameter pack declaration) X< std::pair< int, TList *> ...> class E : public U< TList> ... void f( const TList & ... plist); a list of expressions (in a function call or object initialization) g( make_pair( 1, & plist) ...); h( static_cast< TList *>( plist) ...); // two pack names in one ... m( sizeof( TList) ...); // different from sizeof...( TList) other infrequent cases C++11

170 the std::tuple template
template <class ... Types> class tuple { public: tuple( const Types & ...); /* black magic */ }; template < size_t I, class T> class tuple_element { using type = /* black magic */; template < size_t I, class ... Types> typename tuple_element< I, tuple< Types ...> >::type & get( tuple< Types ...> & t); example typedef tuple< int, double, int> my_tuple; typedef typename tuple_element< 1, my_tuple>::type alias_to_double; my_tuple t1( 1, 2.3, 4); double v = get< 1>( t1); C++11: <utility>

171 Exception handling Mechanismus výjimek

172 Exception handling Exceptions are "jumps" Start: throw statement
Destination: try-catch block Determined in run-time The jump may exit a procedure Local variables will be properly destructed by destructors Besides jumping, a value is passed The type of the value determines the destination Typically, special-purpose classes Catch-block matching can understand inheritance class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException void f() { if ( something == wrong ) throw WrongException( something); if ( anything != good ) throw BadException( anything); } void g() try { f(); catch ( const AnyException & e1 ) { /*...*/

173 Exception handling Exceptions are "jumps" Start: throw statement
Destination: try-catch block Determined in run-time The jump may exit a procedure Local variables will be properly destructed by destructors Besides jumping, a value is passed The type of the value determines the destination Typically, special-purpose classes Catch-block matching can understand inheritance The value may be ignored class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException void f() { if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException(); } void g() try { f(); catch ( const AnyException &) { /*...*/

174 Exception handling Exceptions are "jumps" Start: throw statement
Destination: try-catch block Determined in run-time The jump may exit a procedure Local variables will be properly destructed by destructors Besides jumping, a value is passed The type of the value determines the destination Typically, special-purpose classes Catch-block matching can understand inheritance The value may be ignored There is an universal catch block class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException void f() { if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException(); } void g() try { f(); catch (...) { /*...*/

175 Exception handling Exception handling
Evaluating the expression in the throw statement The value is stored "somewhere" Stack-unwinding Blocks and functions are being exited Local and temporary variables are destructed by calling destructors (user code!) Stack-unwinding stops in the try-block whose catch-block matches the throw expression type catch-block execution The throw value is still stored may be accessed via the catch-block argument (typically, by reference) "throw;" statement, if present, continues stack-unwinding Exception handling ends when the accepting catch-block is exited normally Also using return, break, continue, goto Or by invoking another exception

176 C++11 Exception handling Materialized exceptions
std::exception_ptr is a smart- pointer to an exception object Uses reference-counting to deallocate std::current_exception() Returns (the pointer to) the exception being currently handled The exception handling may then be ended by exiting the catch-block std::rethrow_exception( p) (Re-)Executes the stored exception like a throw statement This mechanism allows: Propagating the exception to a different thread Signalling exceptions in the promise/future mechanism std::exception_ptr p; void g() { try { f(); } catch (...) { p = std::current_exception(); void h() std::rethrow_exception( p); C++11

177 Exception handling Out-of-memory, network errors, end-of-file, ...
Throwing and handling exceptions is slower than normal execution Compilers favor normal execution at the expense of exception-handling complexity Use exceptions only for rare events Out-of-memory, network errors, end-of-file, ... Mark procedures which cannot throw by noexcept void f() noexcept { /*...*/ } it may make code calling them easier (for you and for the compiler) noexcept may be conditional template< typename T> void g( T & y) noexcept( std::is_nothrow_copy_constructible< T>::value) { T x = y;

178 Exception handling Standard exceptions <stdexcept>
All standard exceptions are derived from class exception the member function what() returns the error message bad_alloc: not-enough memory bad_cast: dynamic_cast on references Derived from logic_error: domain_error, invalid_argument, length_error, out_of_range e.g., thrown by vector::at Derived from runtime_error: range_error, overflow_error, underflow_error Hard errors (invalid memory access, division by zero, ...) are NOT signalized as exceptions These errors might occur almost anywhere The need to correctly recover via exception handling would prohibit many code optimizations

179 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

180 Exception-safe programming
Bezpečné programování s výjimkami

181 Exception-safe programming
Using throw a catch is simple Producing code that works correctly in the presence of exceptions is hard Exception-safety Exception-safe programming void f() { int * a = new int[ 100]; int * b = new int[ 200]; g( a, b); delete[] b; delete[] a; } If new int[ 200] throws, the int[100] block becomes inaccessible If g() throws, two blocks become inaccessible

182 Exception-safe programming
Using throw a catch is simple Producing code that works correctly in the presence of exceptions is hard 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;

183 Exception-safe programming
Language-enforced rules Destructors may not end by throwing an exception Constructors of static variables may not end by throwing an exception Copying of exception objects may not throw Compilers sometimes generate implicit try blocks When constructing a compound object, a part constructor may throw Array allocation Class constructors The implicit try block destructs previously constructed parts and rethrows

184 Exception-safe programming
Theory (Weak) exception safety Exceptions does not cause inconsistent state No memory leaks No invalid pointers Application invariants hold ...? Strong exception safety Exiting function by throwing means no change in (observable) state Observable state = public interface behavior Also called "Commit-or-rollback semantics"


Download ppt "Programming in C++ NPRG041 Programming in C++ - 2016/2017 David Bednárek."

Similar presentations


Ads by Google