https://baltig.infn.it/giaco/cpp-geant4-2019.git
http://cern.ch/go/z6PN
(Some) C++ fundamentals
Francesco Giacomini
INFN
VIII International Geant4 SchoolBelgrade, 17–22 November 2019
1
2
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
3
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
4
What is C++
C++ is a complex and large programming language (and library)• strongly and statically typed
• general-purpose• multi-paradigm• good from low-level programming to high-level abstractions• efficient (“you don’t pay for what you don’t use”)• standard
4
What is C++
C++ is a complex and large programming language (and library)• strongly and statically typed• general-purpose
• multi-paradigm• good from low-level programming to high-level abstractions• efficient (“you don’t pay for what you don’t use”)• standard
4
What is C++
C++ is a complex and large programming language (and library)• strongly and statically typed• general-purpose• multi-paradigm
• good from low-level programming to high-level abstractions• efficient (“you don’t pay for what you don’t use”)• standard
4
What is C++
C++ is a complex and large programming language (and library)• strongly and statically typed• general-purpose• multi-paradigm• good from low-level programming to high-level abstractions
• efficient (“you don’t pay for what you don’t use”)• standard
4
What is C++
C++ is a complex and large programming language (and library)• strongly and statically typed• general-purpose• multi-paradigm• good from low-level programming to high-level abstractions• efficient (“you don’t pay for what you don’t use”)
• standard
4
What is C++
C++ is a complex and large programming language (and library)• strongly and statically typed• general-purpose• multi-paradigm• good from low-level programming to high-level abstractions• efficient (“you don’t pay for what you don’t use”)• standard
5
Learn more
• Start from◦ https://isocpp.org/◦ https://en.cppreference.com/◦ https://github.com/isocpp/CppCoreGuidelines
• Main C++ conferences◦ https://github.com/cppcon,
https://youtube.com/cppcon◦ https://github.com/boostcon,
https://youtube.com/boostcon• Standard Committee papers
◦ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/
6
C++ timeline
“Modern C++ feels like a new language”
6
C++ timeline
“Modern C++ feels like a new language”
7
Standards
• Working drafts, almost the same as the final publisheddocument
C++03 https://wg21.link/n1905C++11 https://wg21.link/n3242C++14 https://wg21.link/n4296C++17 https://wg21.link/n4659C++2a https://wg21.link/n4835 (current draft)
• For the LATEXsources seehttps://github.com/cplusplus/draft
8
Compilers
• The School VM is a CentOS 7, which by default provides gcc4.8.5◦ It supports C++11 (-std=c++11) and some C++14
(-std=c++1y)• To play with more recent features
◦ Install the latest Developer Toolset software collection− https://www.softwarecollections.org/en/scls/rhscl/
devtoolset-8/− gcc 8.3 and clang 7.0, support C++17 and beyond
◦ Use online services, e.g.− https://godbolt.org/− https://coliru.stacked-crooked.com/
9
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
10
Objects
• The constructs in a C++ program create, destroy, refer to,access, and manipulate objects
• An object is a region of storage (i.e. memory)
◦ has a storage duration/lifetime◦ has a type◦ can have a name
10
Objects
• The constructs in a C++ program create, destroy, refer to,access, and manipulate objects• An object is a region of storage (i.e. memory)
◦ has a storage duration/lifetime◦ has a type◦ can have a name
10
Objects
• The constructs in a C++ program create, destroy, refer to,access, and manipulate objects• An object is a region of storage (i.e. memory)
◦ has a storage duration/lifetime
◦ has a type◦ can have a name
10
Objects
• The constructs in a C++ program create, destroy, refer to,access, and manipulate objects• An object is a region of storage (i.e. memory)
◦ has a storage duration/lifetime◦ has a type
◦ can have a name
10
Objects
• The constructs in a C++ program create, destroy, refer to,access, and manipulate objects• An object is a region of storage (i.e. memory)
◦ has a storage duration/lifetime◦ has a type◦ can have a name
11
Types
• A type gives meaning to a piece of storage
◦ What’s the meaning of a piece of storage that contains01100011011010010110000101101111?
◦ Read as a sequence of alphabetic characters, it’s the letters c, i,a, o
◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanisms
to build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?
◦ Read as a sequence of alphabetic characters, it’s the letters c, i,a, o
◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanisms
to build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o
◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanisms
to build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o◦ Read as an “integer” number, it’s 1868654947
◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanisms
to build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanisms
to build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values
• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanisms
to build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type
• C++ defines a few fundamental types and provides mechanismsto build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanismsto build compound and user-defined types on top of them
◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanismsto build compound and user-defined types on top of them◦ int, unsigned, double, char, bool, void, . . .
◦ reference, pointer, class, array, function, . . .
11
Types
• A type gives meaning to a piece of storage◦ What’s the meaning of a piece of storage that contains
01100011011010010110000101101111?◦ Read as a sequence of alphabetic characters, it’s the letters c, i,
a, o◦ Read as an “integer” number, it’s 1868654947◦ Read as a “real” number, it’s about 6.97615× 1028
• A type identifies a set of values and the operations that can beapplied to those values• A type is also associated with a machine representation for thevalues belonging to the type• C++ defines a few fundamental types and provides mechanismsto build compound and user-defined types on top of them◦ int, unsigned, double, char, bool, void, . . .◦ reference, pointer, class, array, function, . . .
12
Variables
• A variable is a name for an object• A name is an identifier : a sequence of letters (including _) anddigits, starting with a letter
int i; // declaration; the value is undeterminedi = 4321; // assignmentint j = 1234; // declaration and initializationi = j; // assignment of j's value to i
Memory
i
1234
j
At the end i and j have the same value but remain distinct objects
12
Variables
• A variable is a name for an object• A name is an identifier : a sequence of letters (including _) anddigits, starting with a letter
int i; // declaration
; the value is undeterminedi = 4321; // assignmentint j = 1234; // declaration and initializationi = j; // assignment of j's value to i
Memory
i
1234
j
At the end i and j have the same value but remain distinct objects
12
Variables
• A variable is a name for an object• A name is an identifier : a sequence of letters (including _) anddigits, starting with a letter
int i; // declaration; the value is undetermined
i = 4321; // assignmentint j = 1234; // declaration and initializationi = j; // assignment of j's value to i
Memory
?
i
1234
j
At the end i and j have the same value but remain distinct objects
12
Variables
• A variable is a name for an object• A name is an identifier : a sequence of letters (including _) anddigits, starting with a letter
int i; // declaration; the value is undeterminedi = 4321; // assignment
int j = 1234; // declaration and initializationi = j; // assignment of j's value to i
Memory
4321
i
1234
j
At the end i and j have the same value but remain distinct objects
12
Variables
• A variable is a name for an object• A name is an identifier : a sequence of letters (including _) anddigits, starting with a letter
int i; // declaration; the value is undeterminedi = 4321; // assignmentint j = 1234; // declaration and initialization
i = j; // assignment of j's value to i
Memory
4321
i
1234
j
At the end i and j have the same value but remain distinct objects
12
Variables
• A variable is a name for an object• A name is an identifier : a sequence of letters (including _) anddigits, starting with a letter
int i; // declaration; the value is undeterminedi = 4321; // assignmentint j = 1234; // declaration and initializationi = j; // assignment of j's value to i
Memory
1234
i
1234
j
At the end i and j have the same value but remain distinct objects
12
Variables
• A variable is a name for an object• A name is an identifier : a sequence of letters (including _) anddigits, starting with a letter
int i; // declaration; the value is undeterminedi = 4321; // assignmentint j = 1234; // declaration and initializationi = j; // assignment of j's value to i
Memory
1234
i
1234
j
At the end i and j have the same value but remain distinct objects
13
auto-matic type deduction
Let the compiler deduce the type of a variable from the initializer
auto i = 0; // intauto u = 0U; // unsigned intauto p = &i; // int*auto d = 1.; // doubleauto c = 'a'; // charauto s = "a"; // char const*
auto t = std::string{"a"}; // std::stringstd::vector<std::string> v;auto it = std::begin(v); // std::vector<std::string>::iteratorusing namespace std::chrono_literals;auto u = 1234us; // std::chrono::microsecondsauto e; // error
13
auto-matic type deduction
Let the compiler deduce the type of a variable from the initializer
auto i = 0; // intauto u = 0U; // unsigned intauto p = &i; // int*auto d = 1.; // doubleauto c = 'a'; // charauto s = "a"; // char const*auto t = std::string{"a"}; // std::stringstd::vector<std::string> v;auto it = std::begin(v); // std::vector<std::string>::iterator
using namespace std::chrono_literals;auto u = 1234us; // std::chrono::microsecondsauto e; // error
13
auto-matic type deduction
Let the compiler deduce the type of a variable from the initializer
auto i = 0; // intauto u = 0U; // unsigned intauto p = &i; // int*auto d = 1.; // doubleauto c = 'a'; // charauto s = "a"; // char const*auto t = std::string{"a"}; // std::stringstd::vector<std::string> v;auto it = std::begin(v); // std::vector<std::string>::iteratorusing namespace std::chrono_literals;auto u = 1234us; // std::chrono::microseconds
auto e; // error
13
auto-matic type deduction
Let the compiler deduce the type of a variable from the initializer
auto i = 0; // intauto u = 0U; // unsigned intauto p = &i; // int*auto d = 1.; // doubleauto c = 'a'; // charauto s = "a"; // char const*auto t = std::string{"a"}; // std::stringstd::vector<std::string> v;auto it = std::begin(v); // std::vector<std::string>::iteratorusing namespace std::chrono_literals;auto u = 1234us; // std::chrono::microsecondsauto e; // error
14
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
15
Pointers
Where in memory does a given object reside?
0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xab00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?
0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xab00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xab00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xab00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00
int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xab00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declarator
std::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself
and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xab00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**
p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xcd00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;
int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xcd00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xcd00p
0xbad0
1234
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;
*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xcd00p
0xbad0
5678
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;
++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0xcd00p
0xbad0
5679
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);
p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0p
0xbad0
5679
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;
*p; // undefined behavior
Note that int* is a type by itself and so is int**
15
Pointers
Where in memory does a given object reside?0x0000 0xffff
0xbad0pp
0xaa04
4321
i
0xab00
0p
0xbad0
5679
j
0xcd00
0xcd00q
0xcb80
1234
k
0xaacc
std::cout << &i; // 0xab00, address-of operatorstd::cout << &j; // 0xcd00int* p = &i; // pointer declaratorstd::cout << &p; // 0xbad0int** pp = &p; // &p is of type int**p = &j;int* q = p; // p and q point to the same object
assert(*p == 1234); // dereference operatorint k = *p;*p = 5678;++(*p);p = nullptr;*p; // undefined behavior
Note that int* is a type by itself and so is int**
16
Arrays
Contiguous sequence of homogeneous objects in memory
0x0000 0xffff
123 456 789
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
123 456 789
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
123 456 789
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]
++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
123
a[0]
456
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]
++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
456
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];
a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
456
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior
// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
456
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lost
assert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
456
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab00
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);
++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
456
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab04
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);
*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
654
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab04
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;
b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
654
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab0c
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)
*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
654
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab0c
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behavior
if (b == a + 3) { ... } // ok, but not more than this
16
Arrays
Contiguous sequence of homogeneous objects in memory0x0000 0xffff
124
a[0]
654
a[1]
789
a[2]
0xab00 0xab04 0xab08 0xab0c
0xab0c
b
0xaa00
int a[3] = {123, 456, 789}; // int[3]++a[0];a[3]; // undefined behavior// "arrays decay to pointers at the slightest provocation"auto b = a; // int*, size information lostassert(b == &a[0]);++b; // increase by sizeof(int)assert(b == &a[1]);*b = 654;b += 2; // increase by 2 * sizeof(int)*b; // undefined behaviorif (b == a + 3) { ... } // ok, but not more than this
17
Memory layout of a process
• A process is a running program• When a program is started the operating system brings thecontents of the corresponding file into memory according towell-defined conventions
◦ Stack− function local variables− function call bookkeeping
◦ Heap− dynamic allocation
◦ Global data− literals and variables− initialized and uninitialized (set to 0)
◦ Program instructions
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
result
num
isqrt
i
n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
result
num
isqrt
i
n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
result
5num
isqrt
i
n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
result
5num
isqrt
i
n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
result
5num
isqrt
i
5n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
result
5num
isqrt
1i
5n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
result
5num
isqrt
2i
5n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
2result
5num
isqrt
2i
5n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
2result
5num
isqrt
2i
5n
%rsp
%rsp
18
Functions and the stack
int isqrt(int n){
int i = 1;while (i * i < n) {
++i;}if (i * i > n) {
--i;}return i;
}
int main(){
int num;std::cin >> num;int result = isqrt(num);std::cout << result << '\n';
}
Stack
main
2result
5num
isqrt
2i
5n
%rsp
%rsp
19
Stack frame
• A piece of memory allocated and dedicated to a functionexecution
• It contains local variables, return address, saved registers, . . .• Managed in a Last-In, First-Out (LIFO) way• The size of the stack frame is computed by the compiler• There is a special register (the stack pointer register, %rsp)that indicates the frame of the currently running function• At runtime the allocation/deallocation of a frame consistssimply in subtracting/adding that frame size to the stackpointer register
19
Stack frame
• A piece of memory allocated and dedicated to a functionexecution• It contains local variables, return address, saved registers, . . .
• Managed in a Last-In, First-Out (LIFO) way• The size of the stack frame is computed by the compiler• There is a special register (the stack pointer register, %rsp)that indicates the frame of the currently running function• At runtime the allocation/deallocation of a frame consistssimply in subtracting/adding that frame size to the stackpointer register
19
Stack frame
• A piece of memory allocated and dedicated to a functionexecution• It contains local variables, return address, saved registers, . . .• Managed in a Last-In, First-Out (LIFO) way
• The size of the stack frame is computed by the compiler• There is a special register (the stack pointer register, %rsp)that indicates the frame of the currently running function• At runtime the allocation/deallocation of a frame consistssimply in subtracting/adding that frame size to the stackpointer register
19
Stack frame
• A piece of memory allocated and dedicated to a functionexecution• It contains local variables, return address, saved registers, . . .• Managed in a Last-In, First-Out (LIFO) way• The size of the stack frame is computed by the compiler
• There is a special register (the stack pointer register, %rsp)that indicates the frame of the currently running function• At runtime the allocation/deallocation of a frame consistssimply in subtracting/adding that frame size to the stackpointer register
19
Stack frame
• A piece of memory allocated and dedicated to a functionexecution• It contains local variables, return address, saved registers, . . .• Managed in a Last-In, First-Out (LIFO) way• The size of the stack frame is computed by the compiler• There is a special register (the stack pointer register, %rsp)
that indicates the frame of the currently running function
• At runtime the allocation/deallocation of a frame consistssimply in subtracting/adding that frame size to the stackpointer register
19
Stack frame
• A piece of memory allocated and dedicated to a functionexecution• It contains local variables, return address, saved registers, . . .• Managed in a Last-In, First-Out (LIFO) way• The size of the stack frame is computed by the compiler• There is a special register (the stack pointer register, %rsp)
that indicates the frame of the currently running function• At runtime the allocation/deallocation of a frame consistssimply in subtracting/adding that frame size to the stackpointer register
20
Dynamic memory allocation
• It’s not always possible or convenient to construct objects onthe stack, where they would be destroyed at the end of thefunction that created them• An object can be constructed on the heap
◦ new operator− allocate memory− run the constructor
• The lifetime of an object on the heap is managed by thedeveloper◦ explicit destruction when not needed any more◦ delete operator
− run the destructor− deallocate memory
21
Dynamic memory allocation (cont.)Two notable examples
• dynamic collections of objectsint n;std::cin >> n;Particle* particles = new Particle[n];· · ·delete [] particles; // cleanup properly
• run-time polymorphism
struct Shape { · · · };struct Rectangle : Shape { · · · };struct Circle : Shape { · · · };
Shape* create_shape(char c){
switch (c) {case ’r’: return new Rectangle{· · ·};case ’c’: return new Circle{· · ·};default: return nullptr;
}}
Shape* s = create_shape(’c’);· · ·delete s; // cleanup properly
21
Dynamic memory allocation (cont.)Two notable examples• dynamic collections of objects
int n;std::cin >> n;Particle* particles = new Particle[n];· · ·delete [] particles; // cleanup properly
• run-time polymorphism
struct Shape { · · · };struct Rectangle : Shape { · · · };struct Circle : Shape { · · · };
Shape* create_shape(char c){
switch (c) {case ’r’: return new Rectangle{· · ·};case ’c’: return new Circle{· · ·};default: return nullptr;
}}
Shape* s = create_shape(’c’);· · ·delete s; // cleanup properly
21
Dynamic memory allocation (cont.)Two notable examples• dynamic collections of objects
int n;std::cin >> n;Particle* particles = new Particle[n];· · ·delete [] particles; // cleanup properly
• run-time polymorphism
struct Shape { · · · };struct Rectangle : Shape { · · · };struct Circle : Shape { · · · };
Shape* create_shape(char c){
switch (c) {case ’r’: return new Rectangle{· · ·};case ’c’: return new Circle{· · ·};default: return nullptr;
}}
Shape* s = create_shape(’c’);· · ·delete s; // cleanup properly
22
Stack vs Heap: space
struct S {int n;float f;double d;
};
auto foo_s() {S s;· · ·
}
auto foo_h() {S* s = new S;· · ·
}
foo_s
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S)
foo_h
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S) +
sizeof(S*)• plus new internal
space overhead
22
Stack vs Heap: space
struct S {int n;float f;double d;
};
auto foo_s() {S s;· · ·
}
auto foo_h() {S* s = new S;· · ·
}
foo_s
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S)
foo_h
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S) +
sizeof(S*)• plus new internal
space overhead
22
Stack vs Heap: space
struct S {int n;float f;double d;
};
auto foo_s() {S s;· · ·
}
auto foo_h() {S* s = new S;· · ·
}
foo_s
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S)
foo_h
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S) +
sizeof(S*)• plus new internal
space overhead
22
Stack vs Heap: space
struct S {int n;float f;double d;
};
auto foo_s() {S s;· · ·
}
auto foo_h() {S* s = new S;· · ·
}
foo_s
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S)
foo_h
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S) +
sizeof(S*)• plus new internal
space overhead
22
Stack vs Heap: space
struct S {int n;float f;double d;
};
auto foo_s() {S s;· · ·
}
auto foo_h() {S* s = new S;· · ·
}
foo_s
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S)
foo_h
0x00..00
0xff..ff
stack
heap
Occupancy:• sizeof(S) +
sizeof(S*)• plus new internal
space overhead
23
Stack vs Heap: time
Stackvoid stack(){
int m{123};· · ·
}
stack():subq %4, %rspmovl $123, (%rsp)· · ·addq $4, %rspret
Heap
void heap(){
int* m = new int{123};· · ·delete m;
}
heap():subq $8, %rspmovl $4, %edicall operator new(unsigned long)movl $123, (%rax)movq %rax, (%rsp)· · ·movl $4, %esimovq %rax, %rdicall operator delete(void*, unsigned long)addq $8, %rspret
$ g++ -O3 -std=c++11 heap.cpp && ./a.out1000000 iterations: 0.035745 s
i.e. ~35 ns for each new/delete (on my laptop)
23
Stack vs Heap: time
Stackvoid stack(){
int m{123};· · ·
}
stack():subq %4, %rspmovl $123, (%rsp)· · ·addq $4, %rspret
Heap
void heap(){
int* m = new int{123};· · ·delete m;
}
heap():subq $8, %rspmovl $4, %edicall operator new(unsigned long)movl $123, (%rax)movq %rax, (%rsp)· · ·movl $4, %esimovq %rax, %rdicall operator delete(void*, unsigned long)addq $8, %rspret
$ g++ -O3 -std=c++11 heap.cpp && ./a.out1000000 iterations: 0.035745 s
i.e. ~35 ns for each new/delete (on my laptop)
23
Stack vs Heap: time
Stackvoid stack(){
int m{123};· · ·
}
stack():subq %4, %rspmovl $123, (%rsp)· · ·addq $4, %rspret
Heap
void heap(){
int* m = new int{123};· · ·delete m;
}
heap():subq $8, %rspmovl $4, %edicall operator new(unsigned long)movl $123, (%rax)movq %rax, (%rsp)· · ·movl $4, %esimovq %rax, %rdicall operator delete(void*, unsigned long)addq $8, %rspret
$ g++ -O3 -std=c++11 heap.cpp && ./a.out1000000 iterations: 0.035745 s
i.e. ~35 ns for each new/delete (on my laptop)
23
Stack vs Heap: time
Stackvoid stack(){
int m{123};· · ·
}
stack():subq %4, %rspmovl $123, (%rsp)· · ·addq $4, %rspret
Heap
void heap(){
int* m = new int{123};· · ·delete m;
}
heap():subq $8, %rspmovl $4, %edicall operator new(unsigned long)movl $123, (%rax)movq %rax, (%rsp)· · ·movl $4, %esimovq %rax, %rdicall operator delete(void*, unsigned long)addq $8, %rspret
$ g++ -O3 -std=c++11 heap.cpp && ./a.out1000000 iterations: 0.035745 s
i.e. ~35 ns for each new/delete (on my laptop)
24
Weaknesses of a T*
• Critical information is not encoded in the type◦ Am I the owner of the pointee? Should I delete it?◦ Is the pointee an object or an array of objects? of what size?◦ Was it allocated with new, malloc or even something else (e.g.
fopen returns a FILE*)?
• Owning pointers are prone to leaks and double deletes• Owning pointers are unsafe in presence of exceptions• Runtime overhead
◦ dynamic allocation/deallocation◦ indirection
Shape* s = create_shape(); // deleteG4UImanager* m = G4UImanager::GetUIpointer(); // don't delete
24
Weaknesses of a T*
• Critical information is not encoded in the type• Owning pointers are prone to leaks and double deletes
• Owning pointers are unsafe in presence of exceptions• Runtime overhead
◦ dynamic allocation/deallocation◦ indirection
{auto p = new Circle{· · ·};· · ·// ops, forgot to delete p
}{
auto p = new Circle{· · ·};· · ·delete p;· · ·delete p; // ops, delete again
}
24
Weaknesses of a T*
• Critical information is not encoded in the type• Owning pointers are prone to leaks and double deletes• Owning pointers are unsafe in presence of exceptions
• Runtime overhead
◦ dynamic allocation/deallocation◦ indirection
{auto p = new Circle{· · ·};· · · // potentially throwing codedelete p;
}
24
Weaknesses of a T*
• Critical information is not encoded in the type• Owning pointers are prone to leaks and double deletes• Owning pointers are unsafe in presence of exceptions• Runtime overhead
◦ dynamic allocation/deallocation◦ indirection
25
Debugging memory problems
• Valgrind is a suite of debugging and profiling tools for memorymanagement, threading, caching, etc.• Valgrind Memcheck can detect
◦ invalid memory accesses◦ use of uninitialized values◦ memory leaks◦ bad frees
• It’s precise, but slow
25
Debugging memory problems
• Valgrind is a suite of debugging and profiling tools for memorymanagement, threading, caching, etc.• Valgrind Memcheck can detect
◦ invalid memory accesses◦ use of uninitialized values◦ memory leaks◦ bad frees
• It’s precise, but slow
$ g++ leak.cpp$ valgrind ./a.out==18331== Memcheck, a memory error detector...
26
Debugging memory problems (cont.)
• Address Sanitizer (ASan)• The compiler instruments the executable so that at runtimeASan can catch problems similar, but not identical, to valgrind• Faster than valgrind
26
Debugging memory problems (cont.)
• Address Sanitizer (ASan)• The compiler instruments the executable so that at runtimeASan can catch problems similar, but not identical, to valgrind• Faster than valgrind
$ g++ -fsanitize=address leak.cpp$ ./a.out
===================================================================18338==ERROR: LeakSanitizer: detected memory leaks...
27
Guidelines on dynamic objects
• Possibly don’t, i.e. use the stack• If unavoidable, use a resource-managing object, e.g.
◦ containers and strings◦ smart pointers◦ G4RunManager, see e.g. SetUserInitialization member
functions• Ideally the residual meaning of a raw pointer (T*) should be“I’m pointing to one element and I’m not its owner”
28
Passing by value may be expensive
Let’s consider a function that counts the number of words in a string
int count_words(std::string s)
// (*)
{int count = 0;· · ·return count;
}
int main(){
std::string text;
// (*)
std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
(*) I’m slightly cheating: not all stringmemory goes on the stack
main
res
text
count_words
count
s
28
Passing by value may be expensive
Let’s consider a function that counts the number of words in a string
int count_words(std::string s)
// (*)
{int count = 0;· · ·return count;
}
int main(){
std::string text;
// (*)
std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
(*) I’m slightly cheating: not all stringmemory goes on the stack
main
res
text
count_words
count
s
28
Passing by value may be expensive
Let’s consider a function that counts the number of words in a string
int count_words(std::string s)
// (*)
{int count = 0;· · ·return count;
}
int main(){
std::string text;
// (*)
std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
(*) I’m slightly cheating: not all stringmemory goes on the stack
main
res
Nel mezzo
del cammin di
nostra vita...
text
count_words
count
s
28
Passing by value may be expensive
Let’s consider a function that counts the number of words in a string
int count_words(std::string s)
// (*)
{int count = 0;· · ·return count;
}
int main(){
std::string text;
// (*)
std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
(*) I’m slightly cheating: not all stringmemory goes on the stack
main
res
Nel mezzo
del cammin di
nostra vita...
text
count_words
count
s
28
Passing by value may be expensive
Let’s consider a function that counts the number of words in a string
int count_words(std::string s)
// (*)
{int count = 0;· · ·return count;
}
int main(){
std::string text;
// (*)
std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
(*) I’m slightly cheating: not all stringmemory goes on the stack
main
res
Nel mezzo
del cammin di
nostra vita...
text
count_words
count
Nel mezzo
del cammin di
nostra vita...
s
28
Passing by value may be expensive
Let’s consider a function that counts the number of words in a string
int count_words(std::string s) // (*){
int count = 0;· · ·return count;
}
int main(){
std::string text; // (*)std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
(*) I’m slightly cheating: not all stringmemory goes on the stack
main
res
Nel mezzo
del cammin di
nostra vita...
text
count_words
count
Nel mezzo
del cammin di
nostra vita...
s
29
References
• A reference is another name (an alias) for an existing object
• A reference must be initialized to refer to a valid object12
iri
56
j
int i = 12;int j = 56;
int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
12
iri
56
j
int i = 12;int j = 56;
int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
12
iri
56
j
int i = 12;int j = 56;
int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
12
iri
56
j
int i = 12;int j = 56;int& ri = i; // reference declarator
assert(&ri == &i && ri == 12);ri = 34;assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
12
iri
56
j
int i = 12;int j = 56;int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);
ri = 34;assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
34
iri
56
j
int i = 12;int j = 56;int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;
assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
34
iri
56
j
int i = 12;int j = 56;int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;assert(i == 34);
ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
56
iri
56
j
int i = 12;int j = 56;int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;assert(i == 34);ri = j;
assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
56
iri
56
j
int i = 12;int j = 56;int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
29
References
• A reference is another name (an alias) for an existing object• A reference must be initialized to refer to a valid object
56
iri
56
j
int i = 12;int j = 56;int& ri = i; // reference declaratorassert(&ri == &i && ri == 12);ri = 34;assert(i == 34);ri = j;assert(&ri == &i && i == 56)
• i and ri denote the same object• A reference cannot rebind (be re-associated) to another object
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
texts
count_wordscount
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
texts
count_wordscount
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
Nel mezzo
del cammin di
nostra vita...
texts
count_wordscount
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
Nel mezzo
del cammin di
nostra vita...
texts
count_wordscount
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
Nel mezzo
del cammin di
nostra vita...
texts
count_words0count
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
Nel mezzo
del cammin di
nostra vita...
texts
count_words12345count
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
12345res
Nel mezzo
del cammin di
nostra vita...
texts
count_words12345count
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
Nel mezzo
del cammin di
nostra vita...
texts
count_wordscount
30
Passing by reference
int count_words(std::string& s){
int count = 0;· · ·return count;
}
int main(){
std::string text;std::cin >> text;int const res = count_words(text);std::cout << res << '\n';
}
main
res
Nel mezzo
del cammin di
nostra vita...
texts
count_wordscount
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const int
std::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";
std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const int
std::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";
std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const intstd::cout << x + 32; // ok, read-only
x += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";
std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const intstd::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modify
int const y; // error, not initialized and not modifiable later
std::string const message = "Hello";
std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const intstd::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";
std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const intstd::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";
std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const intstd::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";std::cout << message + " Francesco"; // ok, read-only
message += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const intstd::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modify
std::string const empty_message; // ok! empty string
31
const-safety
• Data qualified as const is logically immutable• Data that is meant to be immutable should be const
int const x = 1'000'000'000; // or const intstd::cout << x + 32; // ok, read-onlyx += 32; // error, trying to modifyint const y; // error, not initialized and not modifiable later
std::string const message = "Hello";std::cout << message + " Francesco"; // ok, read-onlymessage += " Francesco"; // error, trying to modifystd::string const empty_message; // ok! empty string
32
const and references
std::string name = "Francesco";
std::string& rname = name; // ok, can read/modify name via rnamestd::string const& crname = name; // ok, crname is a read-only view of name
std::string const name = "Francesco";
std::string& rname = name; // error, could modify name via rnamestd::string const& crname = name; // ok, can only read name via rcname
int count_words(std::string const& s){
· · ·}
32
const and references
std::string name = "Francesco";std::string& rname = name; // ok, can read/modify name via rname
std::string const& crname = name; // ok, crname is a read-only view of name
std::string const name = "Francesco";
std::string& rname = name; // error, could modify name via rnamestd::string const& crname = name; // ok, can only read name via rcname
int count_words(std::string const& s){
· · ·}
32
const and references
std::string name = "Francesco";std::string& rname = name; // ok, can read/modify name via rnamestd::string const& crname = name; // ok, crname is a read-only view of name
std::string const name = "Francesco";
std::string& rname = name; // error, could modify name via rnamestd::string const& crname = name; // ok, can only read name via rcname
int count_words(std::string const& s){
· · ·}
32
const and references
std::string name = "Francesco";std::string& rname = name; // ok, can read/modify name via rnamestd::string const& crname = name; // ok, crname is a read-only view of name
std::string const name = "Francesco";
std::string& rname = name; // error, could modify name via rnamestd::string const& crname = name; // ok, can only read name via rcname
int count_words(std::string const& s){
· · ·}
32
const and references
std::string name = "Francesco";std::string& rname = name; // ok, can read/modify name via rnamestd::string const& crname = name; // ok, crname is a read-only view of name
std::string const name = "Francesco";std::string& rname = name; // error, could modify name via rname
std::string const& crname = name; // ok, can only read name via rcname
int count_words(std::string const& s){
· · ·}
32
const and references
std::string name = "Francesco";std::string& rname = name; // ok, can read/modify name via rnamestd::string const& crname = name; // ok, crname is a read-only view of name
std::string const name = "Francesco";std::string& rname = name; // error, could modify name via rnamestd::string const& crname = name; // ok, can only read name via rcname
int count_words(std::string const& s){
· · ·}
32
const and references
std::string name = "Francesco";std::string& rname = name; // ok, can read/modify name via rnamestd::string const& crname = name; // ok, crname is a read-only view of name
std::string const name = "Francesco";std::string& rname = name; // error, could modify name via rnamestd::string const& crname = name; // ok, can only read name via rcname
int count_words(std::string const& s){
· · ·}
33
How to pass arguments to functions
• For input parameters◦ If the type is primitive, pass by value
int isqrt(int);
◦ Otherwise pass by const reference
int count_words(std::string const&);
• For input-output or output parameters, pass by non-constreference
void append_text_reading_from_cin(std::string&);
34
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
35
Class
• A class is one of the most important data abstractionmechanisms offered by the C++ language
• A class introduces a new type in the program• The actual data representation of the type is hidden behind awell-defined function-based interface• A new class is introduced by the class keyword (and itsalmost synonim struct)• The new type is then usable as if it were a native type
35
Class
• A class is one of the most important data abstractionmechanisms offered by the C++ language• A class introduces a new type in the program
• The actual data representation of the type is hidden behind awell-defined function-based interface• A new class is introduced by the class keyword (and itsalmost synonim struct)• The new type is then usable as if it were a native type
35
Class
• A class is one of the most important data abstractionmechanisms offered by the C++ language• A class introduces a new type in the program• The actual data representation of the type is hidden behind awell-defined function-based interface
• A new class is introduced by the class keyword (and itsalmost synonim struct)• The new type is then usable as if it were a native type
35
Class
• A class is one of the most important data abstractionmechanisms offered by the C++ language• A class introduces a new type in the program• The actual data representation of the type is hidden behind awell-defined function-based interface• A new class is introduced by the class keyword (and itsalmost synonim struct)
• The new type is then usable as if it were a native type
35
Class
• A class is one of the most important data abstractionmechanisms offered by the C++ language• A class introduces a new type in the program• The actual data representation of the type is hidden behind awell-defined function-based interface• A new class is introduced by the class keyword (and itsalmost synonim struct)• The new type is then usable as if it were a native type
36
Example: complex numbers
Let’s consider a type representing a complex number
class Complex {private:double r;double i;
public:Complex(double x = 0., double y = 0.) : r{x}, i{y} {}double real() const { return r; }double imag() const { return i; }double norm2() const { return r * r + i * i; } // member function
};
double norm2(Complex const& c) { // free functionreturn c.real() * c.real() + c.imag() * c.imag(();
}
Complex operator+(Complex const& l, Complex const& r) {return Complexl.real() + r.real(), l.imag() + r.imag();
}
Complex operator*(Complex const& l, Complex const& r) {· · ·}
37
Example: complex numbers (cont.)
auto mandelbrot(Complex const& c){
int i = 0;Complex z{0};while (norm2(z) < 4 && i < 100) {
z = z * z + c;++i;
}return i;
}
38
Class template
• We have implemented a class to manage complex numbers
• What if we want a Complex with float members?
template<typename F>
class Complex {double r;double i;
public:Complex(
double x = 0., double y = 0.
) : r{x}, i{y} {}double real() const { return r; }double imag() const { return i; }
};
template<typename FP>
class Complex {float r;float i;
public:Complex(
float x = float{}, float y = float{}
) : r{x}, i{y} {}float real() const { return r; }float imag() const { return i; }
};
38
Class template
• We have implemented a class to manage complex numbers• What if we want a Complex with float members?
template<typename F>
class Complex {double r;double i;
public:Complex(
double x = 0., double y = 0.
) : r{x}, i{y} {}double real() const { return r; }double imag() const { return i; }
};
template<typename FP>
class Complex {float r;float i;
public:Complex(
float x = 0.F, float y = 0.F
) : r{x}, i{y} {}float real() const { return r; }float imag() const { return i; }
};
38
Class template
• We have implemented a class to manage complex numbers• What if we want a Complex with float members?
template<typename F>
class Complex {double r;double i;
public:Complex(
double x = double{}, double y = double{}
) : r{x}, i{y} {}double real() const { return r; }double imag() const { return i; }
};
template<typename FP>
class Complex {float r;float i;
public:Complex(
float x = float{}, float y = float{}
) : r{x}, i{y} {}float real() const { return r; }float imag() const { return i; }
};
38
Class template
• We have implemented a class to manage complex numbers• What if we want a Complex with float members?
template<typename F>
class Complex {double r;double i;
public:Complex(
double x = double{}, double y = double{}
) : r{x}, i{y} {}double real() const { return r; }double imag() const { return i; }
};
template<typename FP>
class Complex {FP r;FP i;
public:Complex(
FP x = FP {}, FP y = FP {}
) : r{x}, i{y} {}FP real() const { return r; }FP imag() const { return i; }
};
38
Class template
• We have implemented a class to manage complex numbers• What if we want a Complex with float members?
template<typename F>
class Complex {double r;double i;
public:Complex(
double x = double{}, double y = double{}
) : r{x}, i{y} {}double real() const { return r; }double imag() const { return i; }
};
template<typename FP>class Complex {
FP r;FP i;
public:Complex(
FP x = FP {}, FP y = FP {}
) : r{x}, i{y} {}FP real() const { return r; }FP imag() const { return i; }
};
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);
FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}FP real() const { return r; }
// deduce the return type, C++14
FP imag() const { return i; }};
Complex c; // errorComplex<float> cf; // type is Complex<float>Complex<double> cd; // different type than cfcd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // errorComplex<int> ci; // error
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);
FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}auto real() const { return r; } // deduce the return type, C++14auto imag() const { return i; }
};
Complex c; // errorComplex<float> cf; // type is Complex<float>Complex<double> cd; // different type than cfcd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // errorComplex<int> ci; // error
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);
FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}auto real() const { return r; }
// deduce the return type, C++14
auto imag() const { return i; }};
Complex c; // error
Complex<float> cf; // type is Complex<float>Complex<double> cd; // different type than cfcd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // errorComplex<int> ci; // error
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);
FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}auto real() const { return r; }
// deduce the return type, C++14
auto imag() const { return i; }};
Complex c; // errorComplex<float> cf; // type is Complex<float>
Complex<double> cd; // different type than cfcd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // errorComplex<int> ci; // error
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);
FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}auto real() const { return r; }
// deduce the return type, C++14
auto imag() const { return i; }};
Complex c; // errorComplex<float> cf; // type is Complex<float>Complex<double> cd; // different type than cf
cd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // errorComplex<int> ci; // error
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);
FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}auto real() const { return r; }
// deduce the return type, C++14
auto imag() const { return i; }};
Complex c; // errorComplex<float> cf; // type is Complex<float>Complex<double> cd; // different type than cfcd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // error
Complex<int> ci; // error
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);
FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}auto real() const { return r; }
// deduce the return type, C++14
auto imag() const { return i; }};
Complex c; // errorComplex<float> cf; // type is Complex<float>Complex<double> cd; // different type than cfcd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // errorComplex<int> ci; // acceptable?
39
Class template (cont.)
template<typename FP>class Complex {
static_assert(std::is_floating_point_v<FP>);FP r;FP i;
public:Complex(FP x = FP{}, FP y = FP{}) : r{x}, i{y} {}auto real() const { return r; }
// deduce the return type, C++14
auto imag() const { return i; }};
Complex c; // errorComplex<float> cf; // type is Complex<float>Complex<double> cd; // different type than cfcd + Complex<double>{1., 2.}; // okcd + Complex<float>{1., 2.}; // errorComplex<int> ci; // error
40
Function template
template<class FP>
class Complex {float r;float i;· · ·
};
float norm2(Complex const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
double norm2(Complex<double> const& c)return c.real() * c.real() + c.imag() * c.imag();
}
Complex cf;norm2(cf);
Complex<double> cd;norm2(cd);
40
Function template
template<class FP>class Complex {
FP r;FP i;· · ·
};
float norm2(Complex const& c) { // errorreturn c.real() * c.real() + c.imag() * c.imag();
}
double norm2(Complex<double> const& c)return c.real() * c.real() + c.imag() * c.imag();
}
Complex cf; // errornorm2(cf); // error
Complex<double> cd;norm2(cd);
40
Function template
template<class FP>class Complex {
FP r;FP i;· · ·
};
float norm2(Complex<float> const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
double norm2(Complex<double> const& c)return c.real() * c.real() + c.imag() * c.imag();
}
Complex<float> cf;norm2(cf);
Complex<double> cd;norm2(cd);
40
Function template
template<class FP>class Complex {
FP r;FP i;· · ·
};
float norm2(Complex<float> const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
double norm2(Complex<double> const& c)return c.real() * c.real() + c.imag() * c.imag();
}
Complex<float> cf;norm2(cf);
Complex<double> cd;norm2(cd);
40
Function template
template<class FP>class Complex {
FP r;FP i;· · ·
};
float norm2(Complex<float> const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
double norm2(Complex<double> const& c)return c.real() * c.real() + c.imag() * c.imag();
}
Complex<float> cf;norm2(cf);
Complex<double> cd;norm2(cd); // error
40
Function template
template<class FP>class Complex {
FP r;FP i;· · ·
};
float norm2(Complex<float> const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
double norm2(Complex<double> const& c)return c.real() * c.real() + c.imag() * c.imag();
}
Complex<float> cf;norm2(cf);
Complex<double> cd;norm2(cd);
40
Function template
template<class FP>class Complex {
FP r;FP i;· · ·
};
template<class FP>FP norm2(Complex<FP> const& c) {
return c.real() * c.real() + c.imag() * c.imag();};
Complex<float> cf;norm2(cf);
Complex<double> cd;norm2(cd);
40
Function template
template<class FP>class Complex {
FP r;FP i;· · ·
};
template<class FP>auto norm2(Complex<FP> const& c) { // C++14
return c.real() * c.real() + c.imag() * c.imag();};
Complex<float> cf;norm2(cf);
Complex<double> cd;norm2(cd);
41
Function template (cont.)
template<typename FP>auto norm2(Complex<FP> const& c) { ... }
template<typename C>auto norm2(C const& c){ return c.real() * c.real() + c.imag() * c.imag(); }
norm2(cf); // oknorm2(cd); // okstd::complex<float> scf;norm2(scf); // ok!
namespace std {template<class T>class complex {
...public:T real() const;T imag() const;
};}
This shows the basic idea behindgeneric programming
41
Function template (cont.)
template<typename FP>auto norm2(Complex<FP> const& c) { ... }
template<typename C>auto norm2(C const& c){ return c.real() * c.real() + c.imag() * c.imag(); }
norm2(cf); // oknorm2(cd); // okstd::complex<float> scf;norm2(scf); // ok!
namespace std {template<class T>class complex {
...public:T real() const;T imag() const;
};}
This shows the basic idea behindgeneric programming
41
Function template (cont.)
template<typename FP>auto norm2(Complex<FP> const& c) { ... }
template<typename C>auto norm2(C const& c){ return c.real() * c.real() + c.imag() * c.imag(); }
norm2(cf); // ok
norm2(cd); // okstd::complex<float> scf;norm2(scf); // ok!
namespace std {template<class T>class complex {
...public:T real() const;T imag() const;
};}
This shows the basic idea behindgeneric programming
41
Function template (cont.)
template<typename FP>auto norm2(Complex<FP> const& c) { ... }
template<typename C>auto norm2(C const& c){ return c.real() * c.real() + c.imag() * c.imag(); }
norm2(cf); // oknorm2(cd); // ok
std::complex<float> scf;norm2(scf); // ok!
namespace std {template<class T>class complex {
...public:T real() const;T imag() const;
};}
This shows the basic idea behindgeneric programming
41
Function template (cont.)
template<typename FP>auto norm2(Complex<FP> const& c) { ... }
template<typename C>auto norm2(C const& c){ return c.real() * c.real() + c.imag() * c.imag(); }
norm2(cf); // oknorm2(cd); // okstd::complex<float> scf;norm2(scf); // ok!
namespace std {template<class T>class complex {
...public:T real() const;T imag() const;
};}
This shows the basic idea behindgeneric programming
41
Function template (cont.)
template<typename FP>auto norm2(Complex<FP> const& c) { ... }
template<typename C>auto norm2(C const& c){ return c.real() * c.real() + c.imag() * c.imag(); }
norm2(cf); // oknorm2(cd); // okstd::complex<float> scf;norm2(scf); // ok!
namespace std {template<class T>class complex {
...public:T real() const;T imag() const;
};}
This shows the basic idea behindgeneric programming
41
Function template (cont.)
template<typename FP>auto norm2(Complex<FP> const& c) { ... }
template<typename C>auto norm2(C const& c){ return c.real() * c.real() + c.imag() * c.imag(); }
norm2(cf); // oknorm2(cd); // okstd::complex<float> scf;norm2(scf); // ok!
namespace std {template<class T>class complex {
...public:T real() const;T imag() const;
};}
This shows the basic idea behindgeneric programming
42
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
43
What is a C++ program?
• A program consists of one or more translation units linkedtogether
• A translation unit is the result of the compilation of a source(one with a typical extension of .cpp, .cc, .C, . . . ) file withall its #included headers• A header contains declarations of C++ entities• A program starts at a global function called main
◦ int main()◦ int main(int argc, char* argv[])
• Two scenarios:
◦ A program that defines and uses a set of functions◦ A program that defines and uses a set of classes and functions
43
What is a C++ program?
• A program consists of one or more translation units linkedtogether• A translation unit is the result of the compilation of a source(one with a typical extension of .cpp, .cc, .C, . . . ) file withall its #included headers
• A header contains declarations of C++ entities• A program starts at a global function called main
◦ int main()◦ int main(int argc, char* argv[])
• Two scenarios:
◦ A program that defines and uses a set of functions◦ A program that defines and uses a set of classes and functions
43
What is a C++ program?
• A program consists of one or more translation units linkedtogether• A translation unit is the result of the compilation of a source(one with a typical extension of .cpp, .cc, .C, . . . ) file withall its #included headers• A header contains declarations of C++ entities
• A program starts at a global function called main
◦ int main()◦ int main(int argc, char* argv[])
• Two scenarios:
◦ A program that defines and uses a set of functions◦ A program that defines and uses a set of classes and functions
43
What is a C++ program?
• A program consists of one or more translation units linkedtogether• A translation unit is the result of the compilation of a source(one with a typical extension of .cpp, .cc, .C, . . . ) file withall its #included headers• A header contains declarations of C++ entities• A program starts at a global function called main
◦ int main()◦ int main(int argc, char* argv[])
• Two scenarios:
◦ A program that defines and uses a set of functions◦ A program that defines and uses a set of classes and functions
43
What is a C++ program?
• A program consists of one or more translation units linkedtogether• A translation unit is the result of the compilation of a source(one with a typical extension of .cpp, .cc, .C, . . . ) file withall its #included headers• A header contains declarations of C++ entities• A program starts at a global function called main
◦ int main()◦ int main(int argc, char* argv[])
• Two scenarios:
◦ A program that defines and uses a set of functions◦ A program that defines and uses a set of classes and functions
43
What is a C++ program?
• A program consists of one or more translation units linkedtogether• A translation unit is the result of the compilation of a source(one with a typical extension of .cpp, .cc, .C, . . . ) file withall its #included headers• A header contains declarations of C++ entities• A program starts at a global function called main
◦ int main()◦ int main(int argc, char* argv[])
• Two scenarios:◦ A program that defines and uses a set of functions
◦ A program that defines and uses a set of classes and functions
43
What is a C++ program?
• A program consists of one or more translation units linkedtogether• A translation unit is the result of the compilation of a source(one with a typical extension of .cpp, .cc, .C, . . . ) file withall its #included headers• A header contains declarations of C++ entities• A program starts at a global function called main
◦ int main()◦ int main(int argc, char* argv[])
• Two scenarios:◦ A program that defines and uses a set of functions◦ A program that defines and uses a set of classes and functions
44
A program with a set of functions
#ifndef FUNCTIONS_H // include guard#define FUNCTIONS_H
// file: functions.h, function declarations
void print(double);double pi();
#endif
// file: functions.cpp, function definitions
#include "functions.h"// other #include’s for the implementation of print and pi
void print(double d) { ... }double pi() { · · · }
Run the compiler to produce an object file• $ g++ · · · -c functions.cpp• functions.o
44
A program with a set of functions
#ifndef FUNCTIONS_H // include guard#define FUNCTIONS_H
// file: functions.h, function declarations
void print(double);double pi();
#endif
// file: functions.cpp, function definitions
#include "functions.h"// other #include’s for the implementation of print and pi
void print(double d) { ... }double pi() { · · · }
Run the compiler to produce an object file• $ g++ · · · -c functions.cpp• functions.o
44
A program with a set of functions
#ifndef FUNCTIONS_H // include guard#define FUNCTIONS_H
// file: functions.h, function declarations
void print(double);double pi();
#endif
// file: functions.cpp, function definitions
#include "functions.h"// other #include’s for the implementation of print and pi
void print(double d) { ... }double pi() { · · · }
Run the compiler to produce an object file• $ g++ · · · -c functions.cpp• functions.o
44
A program with a set of functions
#ifndef FUNCTIONS_H // include guard#define FUNCTIONS_H
// file: functions.h, function declarations
void print(double);double pi();
#endif
// file: functions.cpp, function definitions
#include "functions.h"// other #include’s for the implementation of print and pi
void print(double d) { ... }double pi() { · · · }
Run the compiler to produce an object file• $ g++ · · · -c functions.cpp• functions.o
44
A program with a set of functions
#ifndef FUNCTIONS_H // include guard#define FUNCTIONS_H
// file: functions.h, function declarations
void print(double);double pi();
#endif
// file: functions.cpp, function definitions
#include "functions.h"
// other #include’s for the implementation of print and pi
void print(double d) { ... }double pi() { · · · }
Run the compiler to produce an object file• $ g++ · · · -c functions.cpp• functions.o
44
A program with a set of functions
#ifndef FUNCTIONS_H // include guard#define FUNCTIONS_H
// file: functions.h, function declarations
void print(double);double pi();
#endif
// file: functions.cpp, function definitions
#include "functions.h"// other #include’s for the implementation of print and pi
void print(double d) { ... }double pi() { · · · }
Run the compiler to produce an object file• $ g++ · · · -c functions.cpp• functions.o
44
A program with a set of functions
#ifndef FUNCTIONS_H // include guard#define FUNCTIONS_H
// file: functions.h, function declarations
void print(double);double pi();
#endif
// file: functions.cpp, function definitions
#include "functions.h"// other #include’s for the implementation of print and pi
void print(double d) { ... }double pi() { · · · }
Run the compiler to produce an object file• $ g++ · · · -c functions.cpp• functions.o
45
A program with a set of functions (cont.)
// file: main.cpp, function use
#include "functions.h"
void test_pi() { print(pi()); }
int main() {
test_pi();
}
• Run the compiler to produce another object file
◦ $ g++ · · · -c main.cpp◦ main.o
• Run the linker to produce an executable from the object files
◦ $ g++ · · · main.o functions.o◦ a.out
• In most cases
◦ $ g++ · · · main.cpp functions.cpp◦ a.out
• Or use a build framework (make, cmake, scons, cmt, . . . )
45
A program with a set of functions (cont.)
// file: main.cpp, function use
#include "functions.h"
void test_pi() { print(pi()); }
int main() {
test_pi();
}
• Run the compiler to produce another object file
◦ $ g++ · · · -c main.cpp◦ main.o
• Run the linker to produce an executable from the object files
◦ $ g++ · · · main.o functions.o◦ a.out
• In most cases
◦ $ g++ · · · main.cpp functions.cpp◦ a.out
• Or use a build framework (make, cmake, scons, cmt, . . . )
45
A program with a set of functions (cont.)
// file: main.cpp, function use
#include "functions.h"
void test_pi() { print(pi()); }
int main() {test_pi();
}
• Run the compiler to produce another object file
◦ $ g++ · · · -c main.cpp◦ main.o
• Run the linker to produce an executable from the object files
◦ $ g++ · · · main.o functions.o◦ a.out
• In most cases
◦ $ g++ · · · main.cpp functions.cpp◦ a.out
• Or use a build framework (make, cmake, scons, cmt, . . . )
45
A program with a set of functions (cont.)
// file: main.cpp, function use
#include "functions.h"
void test_pi() { print(pi()); }
int main() {test_pi();
}
• Run the compiler to produce another object file◦ $ g++ · · · -c main.cpp◦ main.o
• Run the linker to produce an executable from the object files◦ $ g++ · · · main.o functions.o◦ a.out
• In most cases◦ $ g++ · · · main.cpp functions.cpp◦ a.out
• Or use a build framework (make, cmake, scons, cmt, . . . )
45
A program with a set of functions (cont.)
// file: main.cpp, function use
#include "functions.h"
void test_pi() { print(pi()); }
int main() {test_pi();
}
• Run the compiler to produce another object file◦ $ g++ · · · -c main.cpp◦ main.o
• Run the linker to produce an executable from the object files◦ $ g++ · · · main.o functions.o◦ a.out
• In most cases◦ $ g++ · · · main.cpp functions.cpp◦ a.out
• Or use a build framework (make, cmake, scons, cmt, . . . )
45
A program with a set of functions (cont.)
// file: main.cpp, function use
#include "functions.h"
void test_pi() { print(pi()); }
int main() {test_pi();
}
• Run the compiler to produce another object file◦ $ g++ · · · -c main.cpp◦ main.o
• Run the linker to produce an executable from the object files◦ $ g++ · · · main.o functions.o◦ a.out
• In most cases◦ $ g++ · · · main.cpp functions.cpp◦ a.out
• Or use a build framework (make, cmake, scons, cmt, . . . )
45
A program with a set of functions (cont.)
// file: main.cpp, function use
#include "functions.h"
void test_pi() { print(pi()); }
int main() {test_pi();
}
• Run the compiler to produce another object file◦ $ g++ · · · -c main.cpp◦ main.o
• Run the linker to produce an executable from the object files◦ $ g++ · · · main.o functions.o◦ a.out
• In most cases◦ $ g++ · · · main.cpp functions.cpp◦ a.out
• Or use a build framework (make, cmake, scons, cmt, . . . )
46
A program with a set of classes and functions
#ifndef COMPLEX_HPP#define COMPLEX_HPP
// file: complex.hpp
class Complex {double r;double i;
public:Complex(double x = 0., double y = 0.);double real() const;double imag() const;Complex& operator+=(Complex const& o);...
};
#endif
• Only method’s declarations, not their definition
+ Better physical decoupling: the implementation code in thecpp file can change without the need to recompile the clients
- Worse performance: the compiler cannot inline
46
A program with a set of classes and functions
#ifndef COMPLEX_HPP#define COMPLEX_HPP
// file: complex.hpp
class Complex {double r;double i;
public:Complex(double x = 0., double y = 0.);double real() const;double imag() const;Complex& operator+=(Complex const& o);...
};
#endif
• Only method’s declarations, not their definition+ Better physical decoupling: the implementation code in the
cpp file can change without the need to recompile the clients
- Worse performance: the compiler cannot inline
46
A program with a set of classes and functions
#ifndef COMPLEX_HPP#define COMPLEX_HPP
// file: complex.hpp
class Complex {double r;double i;
public:Complex(double x = 0., double y = 0.);double real() const;double imag() const;Complex& operator+=(Complex const& o);...
};
#endif
• Only method’s declarations, not their definition+ Better physical decoupling: the implementation code in the
cpp file can change without the need to recompile the clients- Worse performance: the compiler cannot inline
47
A program with a set of classes and functions (cont.)
#ifndef COMPLEX_FUNCTIONS_HPP#define COMPLEX_FUNCTIONS_HPP
// file: complex_functions.hpp
#include "complex.hpp"
inline bool operator==(Complex const& lhs, Complex const& rhs) {return lhs.r == rhs.r && lhs.i == rhs.i;
}
Complex operator+(Complex const& lhs, Complex const& rhs);
double norm2(Complex const& c);
...
Functions can be defined in a header file, but they need to bedeclared inline to prevent multiple definitions
47
A program with a set of classes and functions (cont.)
#ifndef COMPLEX_FUNCTIONS_HPP#define COMPLEX_FUNCTIONS_HPP
// file: complex_functions.hpp
#include "complex.hpp"
inline bool operator==(Complex const& lhs, Complex const& rhs) {return lhs.r == rhs.r && lhs.i == rhs.i;
}
Complex operator+(Complex const& lhs, Complex const& rhs);
double norm2(Complex const& c);
...
Functions can be defined in a header file, but they need to bedeclared inline to prevent multiple definitions
48
A program with a set of classes and functions (cont.)
// file: complex.cpp
#include "complex.hpp"
Complex::Complex(double x, double y) : r{x}, i{y} {}
double Complex::real() const { return r; }
double Complex::imag() const { return i; }
Complex& Complex::operator+=(Complex const& o) {r += o.r;i += o.i;return *this;
}
...
Run the compiler to produce an object file• $ g++ ... -c complex.cpp• complex.o
49
A program with a set of classes and functions (cont.)
// file: complex_functions.cpp
#include "complex_functions.hpp"#include "complex.hpp"
// operator==() already defined in the header file
Complex operator+(Complex const& lhs, Complex const& rhs) {auto result = lhs;return result += rhs;
}
double norm2(Complex const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
· · ·
• Better include complex.hpp explicitly◦ include guards protect from multiple inclusions
• Run the compiler to produce an object file◦ $ g++ ... -c complex_functions.cpp
49
A program with a set of classes and functions (cont.)
// file: complex_functions.cpp
#include "complex_functions.hpp"#include "complex.hpp"
// operator==() already defined in the header file
Complex operator+(Complex const& lhs, Complex const& rhs) {auto result = lhs;return result += rhs;
}
double norm2(Complex const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
· · ·
• Better include complex.hpp explicitly◦ include guards protect from multiple inclusions
• Run the compiler to produce an object file◦ $ g++ ... -c complex_functions.cpp
49
A program with a set of classes and functions (cont.)
// file: complex_functions.cpp
#include "complex_functions.hpp"#include "complex.hpp"
// operator==() already defined in the header file
Complex operator+(Complex const& lhs, Complex const& rhs) {auto result = lhs;return result += rhs;
}
double norm2(Complex const& c) {return c.real() * c.real() + c.imag() * c.imag();
}
· · ·
• Better include complex.hpp explicitly◦ include guards protect from multiple inclusions
• Run the compiler to produce an object file◦ $ g++ ... -c complex_functions.cpp
50
A program with a set of classes and functions (cont.)
// file: main.cpp
#include "complex.hpp"#include "complex_functions.hpp"
int main() {Complex c1{1., 2.};Complex c2{3.};Complex c3 = c1 + c2;norm2(c3);
}
• Run the compiler to produce an object file
◦ $ g++ ... -c main.cpp
• Run the linker to produce an executable
◦ $ g++ main.o complex_functions.o complex.o
• Or do all in one step
◦ $ g++ main.cpp complex_functions.cpp complex.cpp
50
A program with a set of classes and functions (cont.)
// file: main.cpp
#include "complex.hpp"#include "complex_functions.hpp"
int main() {Complex c1{1., 2.};Complex c2{3.};Complex c3 = c1 + c2;norm2(c3);
}
• Run the compiler to produce an object file◦ $ g++ ... -c main.cpp
• Run the linker to produce an executable◦ $ g++ main.o complex_functions.o complex.o
• Or do all in one step◦ $ g++ main.cpp complex_functions.cpp complex.cpp
51
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
52
The C++ standard library
• The standard library contains components of general use◦ containers (data structures)◦ algorithms◦ strings◦ input/output◦ random numbers◦ regular expressions◦ concurrency and parallelism◦ filesystem◦ . . .
• The subset containing containers and algorithms is known asSTL (Standard Template Library)• But templates are everywhere
52
The C++ standard library
• The standard library contains components of general use◦ containers (data structures)◦ algorithms◦ strings◦ input/output◦ random numbers◦ regular expressions◦ concurrency and parallelism◦ filesystem◦ . . .
• The subset containing containers and algorithms is known asSTL (Standard Template Library)
• But templates are everywhere
52
The C++ standard library
• The standard library contains components of general use◦ containers (data structures)◦ algorithms◦ strings◦ input/output◦ random numbers◦ regular expressions◦ concurrency and parallelism◦ filesystem◦ . . .
• The subset containing containers and algorithms is known asSTL (Standard Template Library)• But templates are everywhere
53
STL Containers
• Objects that contain and own other objects• Different characteristics and operations, some common traits• Implemented as class templates
Sequence The client decides where an element gets inserted• array, deque, forward_list, list, vector
Associative The container decides where an element gets insertedOrdered The elements are sorted
• map, multimap, set, multisetUnordered The elements are hashed
• unordered_*
54
Sequence containers
std::array
foo
0x00..00
0xff..ff
stack
heap
std::vector
foo
0x00..00
0xff..ff
stack
heap
std::list
foo
0x00..00
0xff..ff
stack
heap
54
Sequence containers
std::array
foo
0x00..00
0xff..ff
stack
heap
std::vector
foo
0x00..00
0xff..ff
stack
heap
std::list
foo
0x00..00
0xff..ff
stack
heap
54
Sequence containers
std::array
foo
0x00..00
0xff..ff
stack
heap
std::vector
foo
0x00..00
0xff..ff
stack
heap
std::list
foo
0x00..00
0xff..ff
stack
heap
55
Sequence containers (cont.)
• std::array◦ fixed size, contiguous in memory◦ typical operations
− operator[]− iteration
• std::vector◦ dynamic size, contiguous in memory◦ typical operations
− push_back− operator[]− iteration
• std::list◦ dynamic size, non-contiguous in memory, iterator stability◦ typical operations
− push_back, push_front, insert− iteration
56
Associative ordered containers
• They contain ordered values (set and multiset) or key-valuepairs (map and multimap)• Search, removal and insertion have logarithmic complexity
• Typically implemented as balanced (red-black) trees
By Cburnett – Own work, CC BY-SA 3.0https://commons.wikimedia.org/w/index.php?curid=1508398
value
color
parentleft
right
56
Associative ordered containers
• They contain ordered values (set and multiset) or key-valuepairs (map and multimap)• Search, removal and insertion have logarithmic complexity• Typically implemented as balanced (red-black) trees
By Cburnett – Own work, CC BY-SA 3.0https://commons.wikimedia.org/w/index.php?curid=1508398
value
color
parentleft
right
56
Associative ordered containers
• They contain ordered values (set and multiset) or key-valuepairs (map and multimap)• Search, removal and insertion have logarithmic complexity• Typically implemented as balanced (red-black) trees
By Cburnett – Own work, CC BY-SA 3.0https://commons.wikimedia.org/w/index.php?curid=1508398
value
color
parentleft
right
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end(); // avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) { // probable warningdo_something_with(c[i]);
}
auto const end = c.end(); // avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end(); // avoid to repeat the call to c.end()
for (auto it = c.begin(); it != c.end(); ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end(); // avoid to repeat the call to c.end()for (auto it = c.begin(); it != end; ++it) {
do_something_with(*it);}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end();
// avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end();
// avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) { // avoid a copy!do_something_with(v);
}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end();
// avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end();
// avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm
lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end();
// avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm
lambda expression
57
Iteration
for (int = 0; i != c.size(); ++i) {
// probable warning
do_something_with(c[i]);}
auto const end = c.end();
// avoid to repeat the call to c.end()
for (auto it = c.begin(); it != end; ++it) {do_something_with(*it);
}
for (auto& v : c) {
// avoid a copy!
do_something_with(v);}
std::for_each(c.begin(), c.end(), [](auto& v) { do_something_with(v); });
standard algorithm lambda expression
58
Hands-on
• Take containers.cpp• Generate N random numbers between 1 and 100 and insert
them in a vector, list, (multi)set. Insert at the beginning and atthe end.• For small N’s, iterate over the containers and print the numbers• Measure the time for inserting N numbers• Discuss the results
59
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
60
Polymorphism
The provision of a single interface to entities of different types
static Based on templates
template<class Iterator, class T>Iterator std::find(Iterator f, Iterator l, T const& v);
std::vector<int> v;auto it = std::find(v.begin(), v.end(), 12);
std::list<Command> l;auto it = std::find(l.begin(), l.end(), Command{"send"});
dynamic Based on inheritance and virtual functions
60
Polymorphism
The provision of a single interface to entities of different types
static Based on templates
template<class Iterator, class T>Iterator std::find(Iterator f, Iterator l, T const& v);
std::vector<int> v;auto it = std::find(v.begin(), v.end(), 12);
std::list<Command> l;auto it = std::find(l.begin(), l.end(), Command{"send"});
dynamic Based on inheritance and virtual functions
60
Polymorphism
The provision of a single interface to entities of different types
static Based on templates
template<class Iterator, class T>Iterator std::find(Iterator f, Iterator l, T const& v);
std::vector<int> v;auto it = std::find(v.begin(), v.end(), 12);
std::list<Command> l;auto it = std::find(l.begin(), l.end(), Command{"send"});
dynamic Based on inheritance and virtual functions
61
Dynamic polymorphism
Typical use case
struct Circle { // is a ShapePoint c;double r;void move_to(Point p);double area() const;
};
struct Rectangle { // is a ShapePoint ul;Point lr;void move_to(Point p);double area() const;
};
std::vector<Shape > shapes; // wishfor (auto const& s : shapes) { s.area(); } // wishfor (auto& s : shapes) { s.move_to(Point{1,2}); } // wish
Let’s ignore access control (private, public, . . . ) for a moment
62
Dynamic polymorphism (cont.)
struct Shape {virtual ~Shape(); // no ‘= 0‘virtual double area() const = 0;
};
struct Circle { // is a Shape
{
Point c;double r;
~Circle();
double area() const;};
struct Rectangle { // is a Shape
{
Point ul;Point lr;
~Rectangle();
double area() const;};
create_shape();auto s = create_shape();s.area();
62
Dynamic polymorphism (cont.)
struct Shape { // base class
virtual ~Shape(); // no ‘= 0‘virtual double area() const = 0;
};
struct Circle : Shape { // derived classPoint c;double r;
~Circle();
double area() const;};
struct Rectangle : Shape { // derived classPoint ul;Point lr;
~Rectangle();
double area() const;};
create_shape();auto s = create_shape();s.area();
62
Dynamic polymorphism (cont.)
struct Shape {
virtual ~Shape(); // no ‘= 0‘virtual double area() const = 0;
};
struct Circle : Shape {Point c;double r;
~Circle();
double area() const;};
struct Rectangle : Shape {Point ul;Point lr;
~Rectangle();
double area() const;};
create_shape(); // either a Circle or a Rectangle
auto s = create_shape();s.area();
62
Dynamic polymorphism (cont.)
struct Shape {
virtual ~Shape(); // no ‘= 0‘virtual double area() const = 0;
};
struct Circle : Shape {Point c;double r;
~Circle();
double area() const;};
struct Rectangle : Shape {Point ul;Point lr;
~Rectangle();
double area() const;};
Shape create_shape();
auto s = create_shape();s.area();
62
Dynamic polymorphism (cont.)
struct Shape {
virtual ~Shape(); // no ‘= 0‘virtual double area() const = 0;
};
struct Circle : Shape {Point c;double r;
~Circle();
double area() const;};
struct Rectangle : Shape {Point ul;Point lr;
~Rectangle();
double area() const;};
Shape create_shape();auto s = create_shape();s.area(); // error
62
Dynamic polymorphism (cont.)
struct Shape { // abstract base class
virtual ~Shape(); // no ‘= 0‘
virtual double area() const = 0; // pure virtual function};
struct Circle : Shape {Point c;double r;
~Circle();
double area() const override;};
struct Rectangle : Shape {Point ul;Point lr;
~Rectangle();
double area() const override;};
Shape create_shape();auto s = create_shape();s.area();
62
Dynamic polymorphism (cont.)
struct Shape {
virtual ~Shape(); // no ‘= 0‘
virtual double area() const = 0;};
struct Circle : Shape {Point c;double r;
~Circle();
double area() const override;};
struct Rectangle : Shape {Point ul;Point lr;
~Rectangle();
double area() const override;};
Shape create_shape(); // errorauto s = create_shape();s.area();
62
Dynamic polymorphism (cont.)
struct Shape {
virtual ~Shape(); // no ‘= 0‘
virtual double area() const = 0;};
struct Circle : Shape {Point c;double r;
~Circle();
double area() const override;};
struct Rectangle : Shape {Point ul;Point lr;
~Rectangle();
double area() const override;};
Shape* create_shape(); // return new Circle/Rectangleauto s = create_shape();s->area();
62
Dynamic polymorphism (cont.)
struct Shape {virtual ~Shape(); // no ‘= 0‘virtual double area() const = 0;
};
struct Circle : Shape {Point c;double r;~Circle();double area() const override;
};
struct Rectangle : Shape {Point ul;Point lr;~Rectangle();double area() const override;
};
Shape* create_shape();auto s = create_shape();s->area();delete s;
62
Dynamic polymorphism (cont.)
struct Shape {virtual ~Shape(); // no ‘= 0‘virtual double area() const = 0;
};
struct Circle : Shape {Point c;double r;~Circle();double area() const override;
};
struct Rectangle : Shape {Point ul;Point lr;~Rectangle();double area() const override;
};
std::unique_ptr<Shape> create_shape(); // use a smart pointerauto s = create_shape();s->area();// automatically deleted at end of scope
63
On dynamic polymorphism• Overridden functions must have the same signature
◦ Beware of member function hiding
• Pure virtual functions cannot be defined (i.e. have animplementation) – with one exception• Cannot create objects of an abstract base class• Dynamic polymorphism works by pointer
and by reference
• Dynamic polymorphism and value semantics don’t play welltogether◦ Consider replacing copy/move operations with a clone operation
struct B { virtual void f(int) = 0; };struct D : B { void f(int) override {} };
B b; // errorB* b1 = new D; // ok, owning pointer, remember to deleteb1->f(); // calls D::fD d; // okB* b2 = &d // ok, non-owning pointer, don’t deleteb2->f(); // calls D::fB& b3 = d; // okb3.f(); // calls D::f
63
On dynamic polymorphism• Overridden functions must have the same signature
◦ Beware of member function hiding• Pure virtual functions cannot be defined (i.e. have animplementation) – with one exception• Cannot create objects of an abstract base class
• Dynamic polymorphism works by pointer
and by reference
• Dynamic polymorphism and value semantics don’t play welltogether◦ Consider replacing copy/move operations with a clone operation
struct B { virtual void f(int) = 0; };struct D : B { void f(int) override {} };
B b; // error
B* b1 = new D; // ok, owning pointer, remember to deleteb1->f(); // calls D::fD d; // okB* b2 = &d // ok, non-owning pointer, don’t deleteb2->f(); // calls D::fB& b3 = d; // okb3.f(); // calls D::f
63
On dynamic polymorphism• Overridden functions must have the same signature
◦ Beware of member function hiding• Pure virtual functions cannot be defined (i.e. have animplementation) – with one exception• Cannot create objects of an abstract base class• Dynamic polymorphism works by pointer
and by reference• Dynamic polymorphism and value semantics don’t play well
together◦ Consider replacing copy/move operations with a clone operation
struct B { virtual void f(int) = 0; };struct D : B { void f(int) override {} };
B b; // errorB* b1 = new D; // ok, owning pointer, remember to deleteb1->f(); // calls D::f
D d; // okB* b2 = &d // ok, non-owning pointer, don’t deleteb2->f(); // calls D::fB& b3 = d; // okb3.f(); // calls D::f
63
On dynamic polymorphism• Overridden functions must have the same signature
◦ Beware of member function hiding• Pure virtual functions cannot be defined (i.e. have animplementation) – with one exception• Cannot create objects of an abstract base class• Dynamic polymorphism works by pointer
and by reference• Dynamic polymorphism and value semantics don’t play well
together◦ Consider replacing copy/move operations with a clone operation
struct B { virtual void f(int) = 0; };struct D : B { void f(int) override {} };
B b; // errorB* b1 = new D; // ok, owning pointer, remember to deleteb1->f(); // calls D::fD d; // okB* b2 = &d // ok, non-owning pointer, don’t deleteb2->f(); // calls D::f
B& b3 = d; // okb3.f(); // calls D::f
63
On dynamic polymorphism• Overridden functions must have the same signature
◦ Beware of member function hiding• Pure virtual functions cannot be defined (i.e. have animplementation) – with one exception• Cannot create objects of an abstract base class• Dynamic polymorphism works by pointer and by reference
• Dynamic polymorphism and value semantics don’t play welltogether◦ Consider replacing copy/move operations with a clone operation
struct B { virtual void f(int) = 0; };struct D : B { void f(int) override {} };
B b; // errorB* b1 = new D; // ok, owning pointer, remember to deleteb1->f(); // calls D::fD d; // okB* b2 = &d // ok, non-owning pointer, don’t deleteb2->f(); // calls D::fB& b3 = d; // okb3.f(); // calls D::f
63
On dynamic polymorphism• Overridden functions must have the same signature
◦ Beware of member function hiding• Pure virtual functions cannot be defined (i.e. have animplementation) – with one exception• Cannot create objects of an abstract base class• Dynamic polymorphism works by pointer and by reference• Dynamic polymorphism and value semantics don’t play welltogether◦ Consider replacing copy/move operations with a clone operation
struct B { virtual void f(int) = 0; };struct D : B { void f(int) override {} };
B b; // errorB* b1 = new D; // ok, owning pointer, remember to deleteb1->f(); // calls D::fD d; // okB* b2 = &d // ok, non-owning pointer, don’t deleteb2->f(); // calls D::fB& b3 = d; // okb3.f(); // calls D::f
64
Mixing interface and implementation
struct Shape {
Point p;Shape(Point p) : p{p} {}
virtual ~Shape();virtual Point where() const = 0;
};
struct Circle : Shape {Point c;double r;Circle(Point p, double d) : c{p}, r{d} {}~Circle();Point where() const override { return c; }
};
struct Rectangle : Shape {Point ul;Point lr;Rectangle(Point p1, Point p2) : ul{p1}, lr{p2} {}~Rectangle();Point where() const override { return (ul + lr) / 2; }
};
Not recommended, especially for data members
64
Mixing interface and implementation
struct Shape {Point p;Shape(Point p) : p{p} {}virtual ~Shape();virtual Point where() const = 0;
};
struct Circle : Shape {
Point c;
double r;Circle(Point p, double d) : Shape{p}, r{d} {}~Circle();Point where() const override { return p; } // p is inherited
};
struct Rectangle : Shape {
Point ul;
Point lr;Rectangle(Point p1, Point p2) : Shape{p1}, lr{p2} {}~Rectangle();Point where() const override { return (p + lr) / 2; } // p is inherited
};
Not recommended, especially for data members
64
Mixing interface and implementation
struct Shape {Point p;Shape(Point p) : p{p} {}virtual ~Shape();virtual Point where() const { return p; } // default impl.
};
struct Circle : Shape {
Point c;
double r;Circle(Point p, double d) : Shape{p}, r{d} {}~Circle();
Point where() const override { return p; }
};
struct Rectangle : Shape {
Point ul;
Point lr;Rectangle(Point p1, Point p2) : Shape{p1}, lr{p2} {}~Rectangle();Point where() const override { return (p + lr) / 2; }
};
Not recommended, especially for data members
64
Mixing interface and implementation
struct Shape {Point p;Shape(Point p) : p{p} {}virtual ~Shape();virtual Point where() const { return p; } // default impl.
};
struct Circle : Shape {
Point c;
double r;Circle(Point p, double d) : Shape{p}, r{d} {}~Circle();
Point where() const override { return p; }
};
struct Rectangle : Shape {
Point ul;
Point lr;Rectangle(Point p1, Point p2) : Shape{p1}, lr{p2} {}~Rectangle();Point where() const override { return (Shape::where() + lr) / 2; }
};
Not recommended, especially for data members
64
Mixing interface and implementation
struct Shape {Point p;Shape(Point p) : p{p} {}virtual ~Shape();virtual Point where() const { return p; } // default impl.
};
struct Circle : Shape {
Point c;
double r;Circle(Point p, double d) : Shape{p}, r{d} {}~Circle();
Point where() const override { return p; }
};
struct Rectangle : Shape {
Point ul;
Point lr;Rectangle(Point p1, Point p2) : Shape{p1}, lr{p2} {}~Rectangle();Point where() const override { return (Shape::where() + lr) / 2; }
};
Not recommended, especially for data members
65
Overriding vs overloading
struct B{
virtual void f(int);};
struct D : B{
void f(int);
// overriding, virtuality is "inherited"
};
D d;B& b = d;b.f(1); // call D::f(int)
65
Overriding vs overloading
struct B{
virtual void f(int);};
struct D : B{
void f(int);
// overriding, virtuality is "inherited"
};
D d;B& b = d;
b.f(1); // call D::f(int)
65
Overriding vs overloading
struct B{
virtual void f(int);};
struct D : B{
void f(int);
// overriding, virtuality is "inherited"
};
D d;B& b = d;b.f(1);
// call D::f(int)
65
Overriding vs overloading
struct B{
virtual void f(int);};
struct D : B{
void f(int);
// overriding, virtuality is "inherited"
};
D d;B& b = d;b.f(1); // call D::f(int)
65
Overriding vs overloading
struct B{
virtual void f(int);};
struct D : B{
void f(int); // overriding, virtuality is "inherited"};
D d;B& b = d;b.f(1); // call D::f(int)
65
Overriding vs overloading
struct B{
virtual void f(int);};
struct D : B{
virtual void f(int); // overriding, virtuality is "inherited"};
D d;B& b = d;b.f(1); // call D::f(int)
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned);};
D d;B& b = d;b.f(1); // call B::f(int)b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned);};
D d;B& b = d;
b.f(1); // call B::f(int)b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned);};
D d;B& b = d;b.f(1);
// call B::f(int)b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned);};
D d;B& b = d;b.f(1); // call B::f(int)
b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned); // overloading! this is another function};
D d;B& b = d;b.f(1); // call B::f(int)
b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned); // overloading! this is another function};
D d;B& b = d;b.f(1); // call B::f(int)b.f(1U);
// call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned); // overloading! this is another function};
D d;B& b = d;b.f(1); // call B::f(int)b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
virtual void f(unsigned); // still overloading};
D d;B& b = d;b.f(1); // call B::f(int)b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned) override; // error: does not override};
D d;B& b = d;b.f(1); // call B::f(int)b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
66
Overriding vs overloading (cont.)
struct B{
virtual void f(int);};
struct D : B{
void f(unsigned) override; // error: does not override};
D d;B& b = d;b.f(1); // call B::f(int)b.f(1U); // call B::f(int)
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
void f(int);};
D d;B const& b = d;b.f(1); // call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
void f(int);};
D d;B const& b = d;
b.f(1); // call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
void f(int);};
D d;B const& b = d;b.f(1);
// call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
void f(int);};
D d;B const& b = d;b.f(1); // call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
void f(int); // overloading! this is another function};
D d;B const& b = d;b.f(1); // call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
virtual void f(int); // still overloading};
D d;B const& b = d;b.f(1); // call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
void f(int) override; // error: does not override};
D d;B const& b = d;b.f(1); // call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
67
Overriding vs overloading (cont.)
struct B{
virtual void f(int) const;};
struct D : B{
void f(int) override; // error: does not override};
D d;B const& b = d;b.f(1); // call B::f(int) const
Virtual functions should specify exactly one of virtual, override,or final
68
Slicing
A base class without pure virtual functions is not abstract any more
• It can be instantiated• It can be copied
◦ unless precautions are taken, e.g. copy/move operations aredeleted
struct Shape{
Point p;Shape(Point p): p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }
};
struct Rectangle : Shape {· · ·}
Shape s;Shape s2 = s1;Rectangle rect{· · ·};Shape s = rect;
68
Slicing
A base class without pure virtual functions is not abstract any more• It can be instantiated
• It can be copied◦ unless precautions are taken, e.g. copy/move operations are
deleted
struct Shape{
Point p;Shape(Point p): p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }
};
struct Rectangle : Shape {· · ·}
Shape s; // desirable?
Shape s2 = s1;Rectangle rect{· · ·};Shape s = rect;
68
Slicing
A base class without pure virtual functions is not abstract any more• It can be instantiated• It can be copied
◦ unless precautions are taken, e.g. copy/move operations aredeleted
struct Shape{
Point p;Shape(Point p): p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }
};
struct Rectangle : Shape {· · ·}
Shape s;Shape s2 = s1; // desirable?
Rectangle rect{· · ·};Shape s = rect;
68
Slicing
A base class without pure virtual functions is not abstract any more• It can be instantiated• It can be copied
◦ unless precautions are taken, e.g. copy/move operations aredeleted
struct Shape{
Point p;Shape(Point p): p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }
};
struct Rectangle : Shape {· · ·}
Shape s;Shape s2 = s1;Rectangle rect{· · ·};Shape s = rect; // desirable??
69
Slicing (cont.)
void process1(Shape& shape){
· · · shape.where() · · ·}
void process2(Shape shape){
· · · shape.where() · · ·}
auto rect = Rectangle{Point{1., 7.}, Point{3., 1.}};
process1(rect);process2(rect);
69
Slicing (cont.)
void process1(Shape& shape){
· · · shape.where() · · ·}
void process2(Shape shape){
· · · shape.where() · · ·}
auto rect = Rectangle{Point{1., 7.}, Point{3., 1.}};process1(rect);
process2(rect);
69
Slicing (cont.)
void process1(Shape& shape) // by reference{
· · · shape.where() · · ·}
void process2(Shape shape){
· · · shape.where() · · ·}
auto rect = Rectangle{Point{1., 7.}, Point{3., 1.}};process1(rect);
process2(rect);
69
Slicing (cont.)
void process1(Shape& shape){
· · · shape.where() · · · // Point{2., 4.}}
void process2(Shape shape){
· · · shape.where() · · ·}
auto rect = Rectangle{Point{1., 7.}, Point{3., 1.}};process1(rect);
process2(rect);
69
Slicing (cont.)
void process1(Shape& shape){
· · · shape.where() · · · // Point{2., 4.}}
void process2(Shape shape){
· · · shape.where() · · ·}
auto rect = Rectangle{Point{1., 7.}, Point{3., 1.}};process1(rect);process2(rect);
69
Slicing (cont.)
void process1(Shape& shape){
· · · shape.where() · · · // Point{2., 4.}}
void process2(Shape shape) // by value{
· · · shape.where() · · ·}
auto rect = Rectangle{Point{1., 7.}, Point{3., 1.}};process1(rect);process2(rect);
69
Slicing (cont.)
void process1(Shape& shape){
· · · shape.where() · · · // Point{2., 4.}}
void process2(Shape shape){
· · · shape.where() · · · // Point{1., 7.}}
auto rect = Rectangle{Point{1., 7.}, Point{3., 1.}};process1(rect);process2(rect);
70
Keeping the base class abstract
• It’s good practice to keep base classes abstract• A base class with no pure virtual functions is not abstract anymore
• To prevent this, declare the destructor as pure virtual• Yet the destructor needs to be defined (i.e. implemented)
◦ So that derived classes are properly destroyed
struct Shape {Point ul;Shape(Point p): p{p} {}virtual ~Shape();virtual Point where() const; // non-pure virtual function
};
Shape::~Shape() = default; // or any other implementation
Shape s; // not really desirable
70
Keeping the base class abstract
• It’s good practice to keep base classes abstract• A base class with no pure virtual functions is not abstract anymore• To prevent this, declare the destructor as pure virtual
• Yet the destructor needs to be defined (i.e. implemented)◦ So that derived classes are properly destroyed
struct Shape {Point ul;Shape(Point p): p{p} {}virtual ~Shape() = 0;virtual Point where() const; // non-pure virtual function
};
Shape::~Shape() = default; // or any other implementation
Shape s; // error
70
Keeping the base class abstract
• It’s good practice to keep base classes abstract• A base class with no pure virtual functions is not abstract anymore• To prevent this, declare the destructor as pure virtual• Yet the destructor needs to be defined (i.e. implemented)
◦ So that derived classes are properly destroyed
struct Shape {Point ul;Shape(Point p): p{p} {}virtual ~Shape() = 0;virtual Point where() const; // non-pure virtual function
};Shape::~Shape() = default; // or any other implementation
Shape s; // error
71
Access control
A member of a class can bepublic Its name can be used anywhere without access
restrictionprivate Its name can be used only by members and friends of
the class in which it is declaredprotected Its name can be used only by members and friends of
the class in which it is declared, by classes derivedfrom that class, and by their friends
72
Accessibility through derivation
class B {private:· · ·
protected:· · ·
public:· · ·
};
class D : public|protected|private B {};
Derivation itself can bepublic public in B → public in D
protected in B → protected in D• Sub-typing, is-a
private public or protected in B → private in D• Implementation inheritance
protected public or protected in B → protected in D• Rarely, if ever, used
73
protected access
class Shape {
Point p;
public:
Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape { // is-a
private:
Point lr;
public:
Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · ·
abs(.x - lr.x)
· · · }};
• protected and public members represent an interface• Data members are a poor interface, keep them private
73
protected access
class Shape {
Point p;public:Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape {
private:
Point lr;public:Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · ·
abs(.x - lr.x)
· · · }};
• protected and public members represent an interface• Data members are a poor interface, keep them private
73
protected access
class Shape {
Point p;public:Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape {
private:
Point lr;public:Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · · abs(p.x - lr.x) · · · }
};
• protected and public members represent an interface• Data members are a poor interface, keep them private
73
protected access
class Shape {
Point p;public:Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape {private:Point lr;
public:Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · · abs(p.x - lr.x) · · · }
};
• protected and public members represent an interface• Data members are a poor interface, keep them private
73
protected access
class Shape {protected:Point p;
public:Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape {private:Point lr;
public:Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · · abs(p.x - lr.x) · · · }
};
• protected and public members represent an interface• Data members are a poor interface, keep them private
73
protected access
class Shape {protected:Point p;
public:Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape {private:Point lr;
public:Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · · abs(where().x - lr.x) · · · }
};
• protected and public members represent an interface• Data members are a poor interface, keep them private
73
protected access
class Shape {private:Point p;
public:Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape {private:Point lr;
public:Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · · abs(where().x - lr.x) · · · }
};
• protected and public members represent an interface• Data members are a poor interface, keep them private
73
protected access
class Shape {private:Point p;
public:Shape(Point p) : p{p} {}virtual ~Shape() = default;virtual Point where() const { return p; }virtual double area() const = 0;
};
class Rectangle : public Shape {private:Point lr;
public:Rectangle(Point p1, Point p2) : Shape{p1}, lr{r} {}~Rectangle();double area() const override { · · · abs(where().x - lr.x) · · · }
};
• protected and public members represent an interface• Data members are a poor interface, keep them private
74
Structural inheritance
• Inheritance is applicable also in non-polymorphic situations• To reuse and possibly extend the implementation and theinterface of a class• A way to create a distinct type with the same implementationand interface of another
• Consider private inheritance or composition
class Vector : public std::vector<int> {using std::vector<int>::vector; // inherit constructors
};
void process(std::vector<int>&); // #1void process(Vector&); // #2void signal(std::vector<int>&); // #3
std::vector<int> v1;Vector v2;process(v1); // call #1process(v2); // call #2signal(v2); // call #3
74
Structural inheritance
• Inheritance is applicable also in non-polymorphic situations• To reuse and possibly extend the implementation and theinterface of a class• A way to create a distinct type with the same implementationand interface of another
• Consider private inheritance or composition
class Vector : public std::vector<int> {using std::vector<int>::vector; // inherit constructors
};
void process(std::vector<int>&); // #1void process(Vector&); // #2void signal(std::vector<int>&); // #3
std::vector<int> v1;Vector v2;process(v1); // call #1process(v2); // call #2signal(v2); // call #3
74
Structural inheritance
• Inheritance is applicable also in non-polymorphic situations• To reuse and possibly extend the implementation and theinterface of a class• A way to create a distinct type with the same implementationand interface of another
• Consider private inheritance or composition
class Vector : public std::vector<int> {using std::vector<int>::vector; // inherit constructors
};
void process(std::vector<int>&); // #1void process(Vector&); // #2
void signal(std::vector<int>&); // #3
std::vector<int> v1;Vector v2;process(v1); // call #1process(v2); // call #2
signal(v2); // call #3
74
Structural inheritance
• Inheritance is applicable also in non-polymorphic situations• To reuse and possibly extend the implementation and theinterface of a class• A way to create a distinct type with the same implementationand interface of another
• Consider private inheritance or composition
class Vector : public std::vector<int> {using std::vector<int>::vector; // inherit constructors
};
void process(std::vector<int>&); // #1void process(Vector&); // #2void signal(std::vector<int>&); // #3
std::vector<int> v1;Vector v2;process(v1); // call #1process(v2); // call #2signal(v2); // call #3
74
Structural inheritance
• Inheritance is applicable also in non-polymorphic situations• To reuse and possibly extend the implementation and theinterface of a class• A way to create a distinct type with the same implementationand interface of another• Consider private inheritance or composition
class Vector : public std::vector<int> {using std::vector<int>::vector; // inherit constructors
};
void process(std::vector<int>&); // #1void process(Vector&); // #2void signal(std::vector<int>&); // #3
std::vector<int> v1;Vector v2;process(v1); // call #1process(v2); // call #2signal(v2); // call #3
75
Destruction and inheritance
• In case of polymorphic inheritance, the destructor of a baseclass should be◦ public and virtual, or◦ protected and non-virtual
• In case of structural inheritance, don’t delete through a pointerto the base class
class Vector : public std::vector<int> {};
Vector v; // ok
Vector* pv = new Vector;delete pv; // ok
std::vector<int>* ps = new Vector;delete ps; // undefined behavior
75
Destruction and inheritance
• In case of polymorphic inheritance, the destructor of a baseclass should be◦ public and virtual, or◦ protected and non-virtual
• In case of structural inheritance, don’t delete through a pointerto the base class
class Vector : public std::vector<int> {};
Vector v; // ok
Vector* pv = new Vector;delete pv; // ok
std::vector<int>* ps = new Vector;delete ps; // undefined behavior
75
Destruction and inheritance
• In case of polymorphic inheritance, the destructor of a baseclass should be◦ public and virtual, or◦ protected and non-virtual
• In case of structural inheritance, don’t delete through a pointerto the base class
class Vector : public std::vector<int> {};
Vector v; // ok
Vector* pv = new Vector;delete pv; // ok
std::vector<int>* ps = new Vector;delete ps; // undefined behavior
75
Destruction and inheritance
• In case of polymorphic inheritance, the destructor of a baseclass should be◦ public and virtual, or◦ protected and non-virtual
• In case of structural inheritance, don’t delete through a pointerto the base class
class Vector : public std::vector<int> {};
Vector v; // ok
Vector* pv = new Vector;delete pv; // ok
std::vector<int>* ps = new Vector;delete ps; // undefined behavior
75
Destruction and inheritance
• In case of polymorphic inheritance, the destructor of a baseclass should be◦ public and virtual, or◦ protected and non-virtual
• In case of structural inheritance, don’t delete through a pointerto the base class
class Vector : public std::vector<int> {};
Vector v; // ok
Vector* pv = new Vector;delete pv; // ok
std::vector<int>* ps = new Vector;delete ps; // undefined behavior
76
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
77
C++ memory model
• The C++ memory model contemplates a multi-threadedexecution of a program• A thread is a single flow of control within a program
◦ Multiple threads can run concurrently/in parallel on multipleCPU cores
◦ Every thread can potentially access every object and function inthe program, i.e. they share the same address space
◦ The interleaving of each thread’s instructions is undefined
Thread 1y = 1;r1 = x;
Thread 2x = 1;r2 = y;
Execution 1y = 1;r1 = x;x = 1;r2 = y;r1 == 0r2 == 1
Execution 2x = 1;r2 = y;y = 1;r1 = x;r1 == 1r2 == 0
Execution 3y = 1;x = 1;r2 = y;r1 = x;r1 == 1r2 == 1
NB: initially x = y = 0
77
C++ memory model
• The C++ memory model contemplates a multi-threadedexecution of a program• A thread is a single flow of control within a program
◦ Multiple threads can run concurrently/in parallel on multipleCPU cores
◦ Every thread can potentially access every object and function inthe program, i.e. they share the same address space
◦ The interleaving of each thread’s instructions is undefined
Thread 1y = 1;r1 = x;
Thread 2x = 1;r2 = y;
Execution 1y = 1;r1 = x;x = 1;r2 = y;r1 == 0r2 == 1
Execution 2x = 1;r2 = y;y = 1;r1 = x;r1 == 1r2 == 0
Execution 3y = 1;x = 1;r2 = y;r1 = x;r1 == 1r2 == 1
NB: initially x = y = 0
77
C++ memory model
• The C++ memory model contemplates a multi-threadedexecution of a program• A thread is a single flow of control within a program
◦ Multiple threads can run concurrently/in parallel on multipleCPU cores
◦ Every thread can potentially access every object and function inthe program, i.e. they share the same address space
◦ The interleaving of each thread’s instructions is undefined
Thread 1y = 1;r1 = x;
Thread 2x = 1;r2 = y;
Execution 1y = 1;r1 = x;x = 1;r2 = y;r1 == 0r2 == 1
Execution 2x = 1;r2 = y;y = 1;r1 = x;r1 == 1r2 == 0
Execution 3y = 1;x = 1;r2 = y;r1 = x;r1 == 1r2 == 1
NB: initially x = y = 0
77
C++ memory model
• The C++ memory model contemplates a multi-threadedexecution of a program• A thread is a single flow of control within a program
◦ Multiple threads can run concurrently/in parallel on multipleCPU cores
◦ Every thread can potentially access every object and function inthe program, i.e. they share the same address space
◦ The interleaving of each thread’s instructions is undefined
Thread 1y = 1;r1 = x;
Thread 2x = 1;r2 = y;
Execution 1y = 1;r1 = x;x = 1;r2 = y;r1 == 0r2 == 1
Execution 2x = 1;r2 = y;y = 1;r1 = x;r1 == 1r2 == 0
Execution 3y = 1;x = 1;r2 = y;r1 = x;r1 == 1r2 == 1
NB: initially x = y = 0
77
C++ memory model
• The C++ memory model contemplates a multi-threadedexecution of a program• A thread is a single flow of control within a program
◦ Multiple threads can run concurrently/in parallel on multipleCPU cores
◦ Every thread can potentially access every object and function inthe program, i.e. they share the same address space
◦ The interleaving of each thread’s instructions is undefined
Thread 1y = 1;r1 = x;
Thread 2x = 1;r2 = y;
Execution 1y = 1;r1 = x;x = 1;r2 = y;r1 == 0r2 == 1
Execution 2x = 1;r2 = y;y = 1;r1 = x;r1 == 1r2 == 0
Execution 3y = 1;x = 1;r2 = y;r1 = x;r1 == 1r2 == 1
NB: initially x = y = 0
78
C++ memory model (cont.)
• C++ guarantees that two threads can update and accessseparate memory locations without interfering with each other• For all other situations updates and accesses have to beproperly synchronized◦ atomics, locks, memory fences
• If updates and accesses to the same location by multiplethreads are not properly synchronized, there is a data race◦ undefined behavior
• Data races can be made visible by transformations applied bythe compiler or by the processor
Sequential consistency for data-race-free programs
78
C++ memory model (cont.)
• C++ guarantees that two threads can update and accessseparate memory locations without interfering with each other• For all other situations updates and accesses have to beproperly synchronized◦ atomics, locks, memory fences
• If updates and accesses to the same location by multiplethreads are not properly synchronized, there is a data race◦ undefined behavior
• Data races can be made visible by transformations applied bythe compiler or by the processor
Sequential consistency for data-race-free programs
79
std::thread
• A thread object uniquely represents a particular thread ofexecution• thread allows to:
◦ create a new thread of execution◦ join a thread (i.e. wait for completion)◦ detach a thread (i.e. no thread represents that thread)◦ perform other management and query operations
• threads are movable but not copyable• A thread object does not represent a thread of execution afterdefault construction, after being moved from, or after asuccessful call to detach or join• A thread must have been joined or detached when a thread isdestroyed
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();void do_something(Thing);
int main(){
do_something();}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();
void do_something(Thing);
int main(){
do_something();}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();
void do_something(Thing);
int main(){
std::thread t(do_something);t.join();
}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();void do_something(Thing);
int main(){
std::thread t(do_something); // error, ambiguoust.join();
}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();void do_something(Thing);
int main(){
std::thread t([]{ do_something(); });t.join();
}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();void do_something(Thing);
int main(){
Thing s = · · ·;std::thread t([]{ do_something(); });t.join();
}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();void do_something(Thing);
int main(){
Thing s = · · ·;std::thread t([=]{ do_something(s); });t.join();
}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();void do_something(Thing);
int main(){
Thing s = · · ·;std::thread t([&]{ do_something(s); });t.join();
}
80
std::thread (cont.)
• A thread is started constructing a thread with an invokable(e.g. a function or a lambda) and possibly some arguments• The result, if any, is ignored
void do_something();void do_something(Thing);
int main(){
Thing s = · · ·;std::thread t([](Thing r){ do_something(r); }, s);t.join();
}
81
π
constexpr int N = 200’000;constexpr double STEP = 1. / N;
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;pi_loop(0, N, sum);return sum * STEP;
}
int main(){
std::cout << pi() << '\n'; // 3.14159}
82
π with two threads
constexpr int N = 200’000;constexpr double STEP = 1. / N;
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;pi_loop(0, N, sum);return sum * STEP;
}
double pi(){
double sum = 0.;std::thread t1(
[&]{ pi_loop(0, N/2, sum); }
);std::thread t2(
[&]{ pi_loop(N/2, N, sum); }
);
t1.join();t2.join();
return sum * STEP;}
// pi() gives 1.72974 (!)
82
π with two threads
constexpr int N = 200’000;constexpr double STEP = 1. / N;
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;pi_loop(0, N, sum);return sum * STEP;
}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });
t1.join();t2.join();
return sum * STEP;}
// pi() gives 1.72974 (!)
82
π with two threads
constexpr int N = 200’000;constexpr double STEP = 1. / N;
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;pi_loop(0, N, sum);return sum * STEP;
}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });t1.join();t2.join();return sum * STEP;
}
// pi() gives 1.72974 (!)
82
π with two threads
constexpr int N = 200’000;constexpr double STEP = 1. / N;
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;pi_loop(0, N, sum);return sum * STEP;
}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });t1.join();t2.join();return sum * STEP;
}// pi() gives 1.72974 (!)
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;
std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·
shared.do_something();
· · ·}
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;
std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·
shared.do_something();
· · ·}
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·mx.lock();shared.do_something();
· · ·}
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·mx.lock();shared.do_something();mx.unlock();· · ·
}
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·std::lock_guard<std::mutex> lk(mx); // RAIIshared.do_something();
· · ·} // ~lock_guard calls mx.unlock()
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·std::lock_guard lk(mx); // CTADshared.do_something();
· · ·} // ~lock_guard calls mx.unlock()
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·std::lock_guard lk(mx);shared.do_something();
· · · // may be a lot of code and take much time} // ~lock_guard calls mx.unlock()
83
Mutual exclusion
• Concurrent accesses to a shared memory location, when atleast one is in write mode, must be properly synchronized toavoid data races• To access a shared location a thread must acquire (lock) a
mutex• If the mutex is not available the thread has to wait• The concept is valid for a generic shared resource
Thing shared;std::mutex mx; // to protect shared
// can be called concurrently by multiple threadsvoid do_something_with_shared(){
· · ·{ std::lock_guard lk(mx);
shared.do_something();} // ~lock_guard calls mx.unlock() here· · ·
}
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });t1.join();t2.join();return sum * STEP;
}
// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;std::lock_guard lk(mx);sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });t1.join();t2.join();return sum * STEP;
}
// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum, std::mutex
&
mx){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;std::lock_guard lk(mx);sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });t1.join();t2.join();return sum * STEP;
}
// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum, std::mutex& mx){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;std::lock_guard lk(mx);sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });t1.join();t2.join();return sum * STEP;
}
// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum, std::mutex& mx){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;std::lock_guard lk(mx);sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum, mx); });std::thread t2([&]{ pi_loop(N/2, N, sum, mx); });t1.join();t2.join();return sum * STEP;
}
// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum, std::mutex& mx){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;std::lock_guard lk(mx);sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::mutex mx;std::thread t1([&]{ pi_loop(0, N/2, sum, mx); });std::thread t2([&]{ pi_loop(N/2, N, sum, mx); });t1.join();t2.join();return sum * STEP;
}
// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum, std::mutex& mx){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;std::lock_guard lk(mx);sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::mutex mx;std::thread t1([&]{ pi_loop(0, N/2, sum, mx); });std::thread t2([&]{ pi_loop(N/2, N, sum, mx); });t1.join();t2.join();return sum * STEP;
}// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
84
π with two threads (cont.)
void pi_loop(int from, int to, double& sum, std::mutex& mx){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;std::lock_guard lk(mx);sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::mutex mx;std::thread t1([&]{ pi_loop(0, N/2, sum, mx); });std::thread t2([&]{ pi_loop(N/2, N, sum, mx); });t1.join();t2.join();return sum * STEP;
}// pi() gives 3.14159 (phew!)
This is a bad solution, since there is a lot of contention on sum
85
π with threads (cont.)
Let’s step back. . . no mutex
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum = 0.;std::thread t1([&]{ pi_loop(0, N/2, sum); });std::thread t2([&]{ pi_loop(N/2, N, sum); });t1.join();t2.join();return sum * STEP;
}
85
π with threads (cont.)
Let’s step back. . . no mutex
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum[2] = {0., 0.};std::thread t1([&]{ pi_loop(0, N/2, sum[0]); });std::thread t2([&]{ pi_loop(N/2, N, sum[1]); });t1.join();t2.join();return (sum[0] + sum[1]) * STEP;
}
85
π with threads (cont.)
Let’s step back. . . no mutex
void pi_loop(int from, int to, double& sum){
for (int i = from; i != to; ++i) {auto x = (i + 0.5) * STEP;sum += 4.0 / (1.0 + x * x);
}}
double pi(){
double sum[2] = {0., 0.};std::thread t1([&]{ pi_loop(0, N/2, sum[0]); });pi_loop(N/2, N, sum[1]); // run in this threadt1.join();
return (sum[0] + sum[1]) * STEP;}
86
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
87
STL Algorithms
• Generic functions that operate on ranges of objects
• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...
Sorting sort partial_sort nth_element ...Set set_union set_intersection set_difference
...Min/Max min max minmax lexicographical_compare
clamp ...Numeric iota accumulate inner_product partial_sum
adjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
87
STL Algorithms
• Generic functions that operate on ranges of objects• Implemented as function templates
Non-modifying all_of any_of for_each count count_ifmismatch equal find find_if adjacent_findsearch ...
Modifying copy fill generate transform removereplace swap reverse rotate shuffle sampleunique ...
Partitioning partition stable_partition ...Sorting sort partial_sort nth_element ...
Set set_union set_intersection set_difference...
Min/Max min max minmax lexicographical_compareclamp ...
Numeric iota accumulate inner_product partial_sumadjacent_difference ...
88
Range
• A range is defined by a pair of iterators [first, last), with lastreferring to one past the last element in the range◦ the range is half-open◦ first == last means the range is empty◦ last can be used to return failure
• An iterator allows to go through the elements of the associatedrange◦ operations to advance, access, compare◦ typically obtained from containers calling specific methods
• An iterator is a generalization of a pointer◦ it supports the same operations, possibly through overloaded
operators◦ certainly * ++ -> == !=, maybe -- + - += -= <
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTADauto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first
last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)
auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)
while (first != last) { // comparison... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto const last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array<int,3> a = {123, 456, 789};
// CTAD
auto first = a.begin(); // or std::begin(a)auto const last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
89
Range (cont.)
0x0000 0xffff
a
123 456 789
0xab00 0xab04 0xab08 0xab0c
first last
std::array
<int,3>
a = {123, 456, 789}; // CTADauto first = a.begin(); // or std::begin(a)auto const last = a.end(); // or std::end(a)while (first != last) { // comparison
... *first ...; // access++first; // advance
}
• std::array<T>::iterator models theRandomAccessIterator concept
90
Generic programming
• A style of programming in which algorithms are written interms of concepts
• A concept is a set of requirements that a type needs to satisfy
◦ e.g. supported expressions, nested typedefs, memory layout, . . .
template <class Iterator, class T>Iteratorfind(Iterator first, Iterator last, const T& value){
for (; first != last; ++first)if (*first == value)
break;return first;
}
90
Generic programming
• A style of programming in which algorithms are written interms of concepts• A concept is a set of requirements that a type needs to satisfy
◦ e.g. supported expressions, nested typedefs, memory layout, . . .
template <class Iterator, class T>Iteratorfind(Iterator first, Iterator last, const T& value){
for (; first != last; ++first)if (*first == value)
break;return first;
}
90
Generic programming
• A style of programming in which algorithms are written interms of concepts• A concept is a set of requirements that a type needs to satisfy
◦ e.g. supported expressions, nested typedefs, memory layout, . . .
template <class Iterator, class T>Iteratorfind(Iterator first, Iterator last, const T& value){
for (; first != last; ++first)if (*first == value)
break;return first;
}
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};
auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first
last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();
auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();
while (first != last) {... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
91
Range (cont.)
0x0000 0xffff
l
123 456789
0xab00 0xad080xac04 0xae0c
first last
std::forward_list<int> l = {123, 456, 789};auto first = l.begin();auto const last = l.end();while (first != last) {
... *first ...;++first;
}
• std::forward_list<T>::iterators models theForwardIterator concept
92
Algorithms and ranges
• Examples
std::vector v = { 23, 54, 41, 0, 18 };
// sort the vector in ascending orderstd::sort(std::begin(v), std::end(v));
// sum up the vector elements, initializing the sum to 0auto s = std::accumulate(std::begin(v), std::end(v), 0);
// append the partial sums of the vector elements into a liststd::list<int> l;std::partial_sum(std::begin(v), std::end(v), std::back_inserter(l));
// find the first element with value 42auto it = std::find(std::begin(v), std::end(v), 42);
• Some algorithms are customizable passing a function
auto it = std::find_if(v.begin(), v.end(), filter );
93
Hands-on
• Consider again containers.cpp and implement a functiontemplate that prints at most the first N elements of acontainer, be it a vector, a list, a (multi)set.• Starting from algo.cpp and following the hints, write code to
◦ sum all the elements of the vector◦ compute the average of the first half and of the second half of
the vector◦ remove duplicate elements◦ . . .
94
Why using standard algorithms
• They are correct
• They express intent more clearly than a raw for loop• They enable parallelism for the masses
◦ parallel algorithms available in C++17, implementations areappearing
#include <execution>
std::vector<int> v = · · ·;std::sort(std::execution::par, v.begin(), v.end());auto it = std::find(std::execution::par, v.begin(), v.end(), 42);
94
Why using standard algorithms
• They are correct• They express intent more clearly than a raw for loop
• They enable parallelism for the masses
◦ parallel algorithms available in C++17, implementations areappearing
#include <execution>
std::vector<int> v = · · ·;std::sort(std::execution::par, v.begin(), v.end());auto it = std::find(std::execution::par, v.begin(), v.end(), 42);
94
Why using standard algorithms
• They are correct• They express intent more clearly than a raw for loop• They enable parallelism for the masses
◦ parallel algorithms available in C++17, implementations areappearing
#include <execution>
std::vector<int> v = · · ·;std::sort(std::execution::par, v.begin(), v.end());auto it = std::find(std::execution::par, v.begin(), v.end(), 42);
94
Why using standard algorithms
• They are correct• They express intent more clearly than a raw for loop• They enable parallelism for the masses
◦ parallel algorithms available in C++17, implementations areappearing
#include <execution>
std::vector<int> v = · · ·;std::sort(std::execution::par, v.begin(), v.end());auto it = std::find(std::execution::par, v.begin(), v.end(), 42);
95
Functions
• A function associates a sequence of statements (the functionbody) with a name and a list of zero or more parameters
std::string join(std::string const& s, int i){
return s + ’-’ + std::to_string(i);}
join("XYZ", 5); // std::string{"XYZ-5"}
• A function may return a value• A function returning a bool is called a predicate
bool less(int n, int m) { return n < m; }
• Multiple functions can have the same name → overloading◦ different parameter lists
95
Functions
• A function associates a sequence of statements (the functionbody) with a name and a list of zero or more parameters
auto join(std::string const& s, int i){
return s + ’-’ + std::to_string(i);}
join("XYZ", 5); // std::string{"XYZ-5"}
• A function may return a value• A function returning a bool is called a predicate
bool less(int n, int m) { return n < m; }
• Multiple functions can have the same name → overloading◦ different parameter lists
96
Algorithms and functions
template <class Iterator, class T>Iterator find(Iterator first, Iterator last, const T& value){
for (; first != last; ++first)if (*first == value)
break;return first;
}
auto it = find(v.begin(), v.end(), 42);
template <class Iterator, class T>Iterator find_if(Iterator first, Iterator last, T pred){
for (; first != last; ++first)if (pred(*first)) // unary predicate
break;return first;
}
bool lt42(int n) { return n < 42; }
auto it = find_if(v.begin(), v.end(), lt42);
96
Algorithms and functions
template <class Iterator, class T>Iterator find(Iterator first, Iterator last, const T& value){
for (; first != last; ++first)if (*first == value)
break;return first;
}
auto it = find(v.begin(), v.end(), 42);
template <class Iterator, class T>Iterator find_if(Iterator first, Iterator last, T pred){
for (; first != last; ++first)if (pred(*first)) // unary predicate
break;return first;
}
bool lt42(int n) { return n < 42; }
auto it = find_if(v.begin(), v.end(), lt42);
96
Algorithms and functions
template <class Iterator, class T>Iterator find(Iterator first, Iterator last, const T& value){
for (; first != last; ++first)if (*first == value)
break;return first;
}
auto it = find(v.begin(), v.end(), 42);
template <class Iterator, class T>Iterator find_if(Iterator first, Iterator last, T pred){
for (; first != last; ++first)if (pred(*first)) // unary predicate
break;return first;
}
bool lt42(int n) { return n < 42; }
auto it = find_if(v.begin(), v.end(), lt42);
96
Algorithms and functions
template <class Iterator, class T>Iterator find(Iterator first, Iterator last, const T& value){
for (; first != last; ++first)if (*first == value)
break;return first;
}
auto it = find(v.begin(), v.end(), 42);
template <class Iterator, class T>Iterator find_if(Iterator first, Iterator last, T pred){
for (; first != last; ++first)if (pred(*first)) // unary predicate
break;return first;
}
bool lt42(int n) { return n < 42; }
auto it = find_if(v.begin(), v.end(), lt42);
97
Function objects
A mechanism to define something-callable-like-a-function
• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function
• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function
• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
97
Function objects
A mechanism to define something-callable-like-a-function• A class with an operator()
struct LessThan42 {
auto lt42(int n){
return n < 42;}
LessThan42 lt42{};
auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),lt42
); // *it == 32
struct LessThan42 {auto operator()(int n) const{
return n < 42;}
};
auto lt42 = LessThan42{};auto b = lt42(32); // true
vector v{61,32,51};auto it = find_if(
begin(v), end(v),LessThan42{}
); // *it == 32
98
Function objects (cont.)
A function object, being the instance of a class, can have state
class LessThan {int m_;
public:explicit LessThan(int m) : m_{m} {}auto operator()(int n) const {
return n < m_;}
};
LessThan lt6{6};auto b1 = lt6(5); // true
LessThan lt4{4};auto b2 = lt4(5); // false
vector v{8,4,5};auto i1 = find_if(..., lt6); // *i1 == 4auto i2 = find_if(..., lt4); // i2 == end, i.e. not found
98
Function objects (cont.)
A function object, being the instance of a class, can have state
class LessThan {int m_;
public:explicit LessThan(int m) : m_{m} {}auto operator()(int n) const {
return n < m_;}
};
LessThan lt6{6};auto b1 = lt6(5); // true
LessThan lt4{4};auto b2 = lt4(5); // false
vector v{8,4,5};auto i1 = find_if(..., lt6); // *i1 == 4auto i2 = find_if(..., lt4); // i2 == end, i.e. not found
98
Function objects (cont.)
A function object, being the instance of a class, can have state
class LessThan {int m_;
public:explicit LessThan(int m) : m_{m} {}auto operator()(int n) const {
return n < m_;}
};
LessThan lt6{6};auto b1 = lt6(5); // true
LessThan lt4{4};auto b2 = lt4(5); // false
vector v{8,4,5};auto i1 = find_if(..., lt6); // *i1 == 4auto i2 = find_if(..., lt4); // i2 == end, i.e. not found
98
Function objects (cont.)
A function object, being the instance of a class, can have state
class LessThan {int m_;
public:explicit LessThan(int m) : m_{m} {}auto operator()(int n) const {
return n < m_;}
};
LessThan lt6{6};auto b1 = lt6(5); // true
LessThan lt4{4};auto b2 = lt4(5); // false
vector v{8,4,5};auto i1 = find_if(..., lt6); // *i1 == 4auto i2 = find_if(..., lt4); // i2 == end, i.e. not found
98
Function objects (cont.)
A function object, being the instance of a class, can have state
class LessThan {int m_;
public:explicit LessThan(int m) : m_{m} {}auto operator()(int n) const {
return n < m_;}
};
LessThan lt6{6};auto b1 = lt6(5); // true
LessThan lt4{4};auto b2 = lt4(5); // false
vector v{8,4,5};auto i1 = find_if(..., lt6); // *i1 == 4auto i2 = find_if(..., lt4); // i2 == end, i.e. not found
98
Function objects (cont.)
A function object, being the instance of a class, can have state
class LessThan {int m_;
public:explicit LessThan(int m) : m_{m} {}auto operator()(int n) const {
return n < m_;}
};
LessThan lt6{6};auto b1 = LessThan{6}(5); // true
LessThan lt4{4};auto b2 = LessThan{4}(5); // false
vector v{8,4,5};auto i1 = find_if(..., LessThan{6}); // *i1 == 4auto i2 = find_if(..., LessThan{4}); // i2 == end, i.e. not found
99
Function objects (cont.)
An example from the standard library
#include <random>
// random bit generator (mersenne twister)std::mt19937 gen;
// generate N 32-bit unsigned integer numbersfor (int n = 0; n != N; ++n) {
std::cout << gen() << ’\n’;}
// generate N floats distributed normally (mean: 0., stddev: 1.)std::normal_distribution<float> dist;for (int n = 0; n != N; ++n) {
std::cout << dist(gen) << ’\n’;}
// generate N ints distributed uniformly between 1 and 6 includedstd::uniform_int_distribution<> roll_dice(1, 6);for (int n = 0; n != N; ++n) {
std::cout << roll_dice(gen) << ’\n’;}
100
Lambda expression
• A concise way to create an unnamed function object• Useful to pass actions/callbacks to algorithms, threads,frameworks, . . .
struct LessThan42 {auto operator()(int n){
return n < 42;}
};
class LessThan {int m_;
public:explicit LessThan(int m)
: m_{m} {}auto operator()(int n) const{
return n < m_;}
};
find_if(..., LessThan42{});
find_if(..., [](int n) {return n < 42;
});
find_if(..., LessThan{42});
find_if(..., [](int n) {return n < m;
});
100
Lambda expression
• A concise way to create an unnamed function object• Useful to pass actions/callbacks to algorithms, threads,frameworks, . . .
struct LessThan42 {auto operator()(int n){
return n < 42;}
};
class LessThan {int m_;
public:explicit LessThan(int m)
: m_{m} {}auto operator()(int n) const{
return n < m_;}
};
find_if(..., LessThan42{});
find_if(..., [](int n) {return n < 42;
});
find_if(..., LessThan{42});
find_if(..., [](int n) {return n < m;
});
100
Lambda expression
• A concise way to create an unnamed function object• Useful to pass actions/callbacks to algorithms, threads,frameworks, . . .
struct LessThan42 {auto operator()(int n){
return n < 42;}
};
class LessThan {int m_;
public:explicit LessThan(int m)
: m_{m} {}auto operator()(int n) const{
return n < m_;}
};
find_if(..., LessThan42{});
find_if(..., [](int n) {return n < 42;
});
find_if(..., LessThan{42});
find_if(..., [](int n) {return n < m;
});
100
Lambda expression
• A concise way to create an unnamed function object• Useful to pass actions/callbacks to algorithms, threads,frameworks, . . .
struct LessThan42 {auto operator()(int n){
return n < 42;}
};
class LessThan {int m_;
public:explicit LessThan(int m)
: m_{m} {}auto operator()(int n) const{
return n < m_;}
};
find_if(..., LessThan42{});
find_if(..., [](int n) {return n < 42;
});
find_if(..., LessThan{42});
int m{42};find_if(..., [=](int n) {
return n < m;}
);
100
Lambda expression
• A concise way to create an unnamed function object• Useful to pass actions/callbacks to algorithms, threads,frameworks, . . .
struct LessThan42 {auto operator()(int n){
return n < 42;}
};
class LessThan {int m_;
public:explicit LessThan(int m)
: m_{m} {}auto operator()(int n) const{
return n < m_;}
};
find_if(..., LessThan42{});
find_if(..., [](int n) {return n < 42;
});
find_if(..., LessThan{42});
find_if(..., [m = 42](int n) {return n < m;
});
101
Lambda expression (cont.)
The evaluation of a lambda expression produces an unnamedfunction object (a closure)• The operator() corresponds to the code of the body of thelambda expression• The data members are the captured local variables
◦ [] capture nothing◦ [=] capture all by value◦ [k] capture k by value◦ [&] capture all by reference◦ [&k] capture k by reference◦ [=, &k] capture all by value but k by reference◦ [&, k] capture all by reference but k by value
• Global variables are available without being captured
102
Hands-on
• Starting from algo_functions.cpp and following the hints,write code to◦ multiply all the elements of the vector◦ sort the vector in descending order◦ move the even numbers to the beginning◦ create another vector with the squares of the numbers in the
first vector◦ find the first multiple of 3 or 7◦ erase from the vector all the multiples of 3 or 7◦ . . .
103
std::function
• Type-erased wrapper that can store and invoke any callableentity with a certain signature◦ function, function object, lambda, member function
• Some space and time overhead, so use only if a templateparameter is not satisfactory
#include <functional>
int sum_squares(int x, int y) { return x * x + y * y; }
int main() {std::vector<std::function<int(int, int)>> v {
std::plus<>{}, // has a compatible operator()std::multiplies<>{}, // idem&sum_squares)
};for (int k = 10; k <= 1000; k *= 10) {
v.push_back([k](int x, int y) -> int { return k * x + y; });}
for (auto const& f : v) { std::cout << f(4, 5) << ’\n’; }}
103
std::function
• Type-erased wrapper that can store and invoke any callableentity with a certain signature◦ function, function object, lambda, member function
• Some space and time overhead, so use only if a templateparameter is not satisfactory
#include <functional>
int sum_squares(int x, int y) { return x * x + y * y; }
int main() {std::vector<std::function<int(int, int)>> v {
std::plus<>{}, // has a compatible operator()std::multiplies<>{}, // idem&sum_squares)
};for (int k = 10; k <= 1000; k *= 10) {
v.push_back([k](int x, int y) -> int { return k * x + y; });}
for (auto const& f : v) { std::cout << f(4, 5) << ’\n’; }}
104
Outline
Introduction
Objects and types
Process memory layout, pointers, references
Classes, class templates and function templates
One program, multiple files
Containers
Polymorphism
Concurrency
Algorithms and functions
Smart pointers
105
Weaknesses of a T*
• Critical information is not encoded in the type◦ Am I the owner of the pointee? Should I delete it?◦ Is the pointee an object or an array of objects? of what size?◦ Was it allocated with new, malloc or even something else (e.g.
fopen returns a FILE*)?
• Owning pointers are prone to leaks and double deletes• Owning pointers are unsafe in presence of exceptions
Shape* s = create_shape(); // deleteG4UImanager* m = G4UImanager::GetUIpointer(); // don’t delete
105
Weaknesses of a T*
• Critical information is not encoded in the type• Owning pointers are prone to leaks and double deletes
• Owning pointers are unsafe in presence of exceptions
{auto p = new Circle{· · ·};· · ·// ops, forgot to delete p
}{
auto p = new Circle{· · ·};· · ·delete p;· · ·delete p; // ops, delete again
}
105
Weaknesses of a T*
• Critical information is not encoded in the type• Owning pointers are prone to leaks and double deletes• Owning pointers are unsafe in presence of exceptions
{auto p = new Circle{· · ·};· · · // potentially throwing codedelete p;
}
106
Guidelines on dynamic objects
• Possibly don’t• A raw pointer (T*) should be non-owning• Use a resource-managing object
◦ e.g., standard containers and strings◦ smart pointers◦ G4RunManager, see e.g. SetUserInitialization member
functions
107
Resource management
• Dynamic memory is just one of the many types of resourcesmanipulated by a program:◦ thread, mutex, socket, file, . . .
• C++ offers powerful tools to manage resources◦ "C++ is my favorite garbage collected language because it
generates so little garbage"
108
Smart pointers
• Objects that behave like pointers, but also manage the lifetimeof the pointee• Leverage the RAII idiom
◦ Resource Acquisition Is Initialization◦ Resource (e.g. memory) is acquired in the constructor◦ Resource (e.g. memory) is released in the destructor
• Importance of how the destructor is designed in C++◦ deterministic: guaranteed execution at the end of the scope◦ order of execution opposite to order of construction
• Guaranteed no leak nor double release, even in presence ofexceptions
109
Smart pointers (cont.)
template<typename Pointee>class SmartPointer {
Pointee* m_p;public:explicit SmartPointer(Pointee* p): m_p{p} {}~SmartPointer() { delete m_p; }
Pointee* operator->() { return m_p; }Pointee& operator*() { return *m_p; }
};
class Histo { · · · };
{SmartPointer<Histo> sp{new Histo{}};
sp->fill();(*sp).fill();
}
At the end of the scope (i.e. at the closing }) sp is destroyed andits destructor deletes the pointee
109
Smart pointers (cont.)
template<typename Pointee>class SmartPointer {
Pointee* m_p;public:explicit SmartPointer(Pointee* p): m_p{p} {}~SmartPointer() { delete m_p; }
Pointee* operator->() { return m_p; }Pointee& operator*() { return *m_p; }
};
class Histo { · · · };
{SmartPointer<Histo> sp{new Histo{}};
sp->fill();(*sp).fill();
}
At the end of the scope (i.e. at the closing }) sp is destroyed andits destructor deletes the pointee
109
Smart pointers (cont.)
template<typename Pointee>class SmartPointer {
Pointee* m_p;public:explicit SmartPointer(Pointee* p): m_p{p} {}~SmartPointer() { delete m_p; }
Pointee* operator->() { return m_p; }Pointee& operator*() { return *m_p; }
};
class Histo { · · · };
{SmartPointer<Histo> sp{new Histo{}};sp->fill();(*sp).fill();
}
At the end of the scope (i.e. at the closing }) sp is destroyed andits destructor deletes the pointee
109
Smart pointers (cont.)
template<typename Pointee>class SmartPointer {
Pointee* m_p;public:explicit SmartPointer(Pointee* p): m_p{p} {}~SmartPointer() { delete m_p; }Pointee* operator->() { return m_p; }Pointee& operator*() { return *m_p; }
};
class Histo { · · · };
{SmartPointer<Histo> sp{new Histo{}};sp->fill();(*sp).fill();
}
At the end of the scope (i.e. at the closing }) sp is destroyed andits destructor deletes the pointee
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph);
// by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_unique<Histo>(); // better, C++14take(ph); // error, non-copyabletake(std::move(ph)); // ok, movable
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph);
// by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_unique<Histo>(); // better, C++14take(ph); // error, non-copyabletake(std::move(ph)); // ok, movable
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph);
// by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit new
auto ph = std::make_unique<Histo>(); // better, C++14take(ph); // error, non-copyabletake(std::move(ph)); // ok, movable
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph);
// by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_unique<Histo>(); // better, C++14
take(ph); // error, non-copyabletake(std::move(ph)); // ok, movable
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph);
// by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_unique<Histo>(); // better, C++14take(ph);
// error, non-copyabletake(std::move(ph)); // ok, movable
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph); // by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_unique<Histo>(); // better, C++14take(ph); // error, non-copyable
take(std::move(ph)); // ok, movable
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph);
// by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_unique<Histo>(); // better, C++14take(ph); // error, non-copyabletake(std::move(ph));
// ok, movable
110
std::unique_ptr<T>
Standard smart pointer• Exclusive ownership• No space nor time overhead• Non-copyable, movable
class Histo { · · · };
void take(std::unique_ptr<Histo> ph);
// by value
std::unique_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_unique<Histo>(); // better, C++14take(ph); // error, non-copyabletake(std::move(ph)); // ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_shared<Histo>(); // bettertake(ph); // ok, copyabletake(std::move(ph)); // ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_shared<Histo>(); // bettertake(ph); // ok, copyabletake(std::move(ph)); // ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit new
auto ph = std::make_shared<Histo>(); // bettertake(ph); // ok, copyabletake(std::move(ph)); // ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_shared<Histo>(); // better
take(ph); // ok, copyabletake(std::move(ph)); // ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_shared<Histo>(); // bettertake(ph);
// ok, copyabletake(std::move(ph)); // ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_shared<Histo>(); // bettertake(ph); // ok, copyable
take(std::move(ph)); // ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_shared<Histo>(); // bettertake(ph); // ok, copyabletake(std::move(ph));
// ok, movable
111
std::shared_ptr<T>
Standard smart pointer• Shared ownership (reference counted)• Some space and time overhead
◦ for the management, not for access• Copyable and movable
class Histo { · · · };
void take(std::shared_ptr<Histo> ph);
std::shared_ptr<Histo> ph{new Histo{}}; // explicit newauto ph = std::make_shared<Histo>(); // bettertake(ph); // ok, copyabletake(std::move(ph)); // ok, movable
112
On smart _ptr<T>
• Prefer unique_ptr unless you need shared_ptr◦ You can always move a unique_ptr into a shared_ptr
• Access to the raw pointer is available
◦ e.g. to pass to legacy APIs◦ smart _ptr<T>::get()
− returns a non-owning T*
◦ unique_ptr<T>::release()
− returns an owning T*− must be explicitly managed
• Partial, improving support for arrays
std::unique_ptr<int[]> p{new int[n]}; // destructor calls ‘delete []‘
112
On smart _ptr<T>
• Prefer unique_ptr unless you need shared_ptr◦ You can always move a unique_ptr into a shared_ptr
• Access to the raw pointer is available◦ e.g. to pass to legacy APIs◦ smart _ptr<T>::get()
− returns a non-owning T*◦ unique_ptr<T>::release()
− returns an owning T*− must be explicitly managed
• Partial, improving support for arrays
std::unique_ptr<int[]> p{new int[n]}; // destructor calls ‘delete []‘
112
On smart _ptr<T>
• Prefer unique_ptr unless you need shared_ptr◦ You can always move a unique_ptr into a shared_ptr
• Access to the raw pointer is available◦ e.g. to pass to legacy APIs◦ smart _ptr<T>::get()
− returns a non-owning T*◦ unique_ptr<T>::release()
− returns an owning T*− must be explicitly managed
• Partial, improving support for arrays
std::unique_ptr<int[]> p{new int[n]}; // destructor calls ‘delete []‘
113
smart _ptr and functionsPass a smart pointer to a function only if the function needs to relyon the smart pointer itself
• by value of a unique_ptr, to transfer ownership
void take(std::unique_ptr<Histo> u);auto u = std::make_unique<Histo>();take(u); // errortake(std::move(u)); // ok
• by value of a shared_ptr, to keep the resource alive
auto s = std::make_shared<Histo>();std::thread t{[=] { do_something_with(s); }};
• by reference, to interact with the smart pointer itself
void print_count(std::shared_ptr<Histo> const& s) {std::cout << s.use_count() << ’\n’;
};auto s = std::make_shared<Histo>();print_count(s);
113
smart _ptr and functionsPass a smart pointer to a function only if the function needs to relyon the smart pointer itself• by value of a unique_ptr, to transfer ownership
void take(std::unique_ptr<Histo> u);auto u = std::make_unique<Histo>();take(u); // errortake(std::move(u)); // ok
• by value of a shared_ptr, to keep the resource alive
auto s = std::make_shared<Histo>();std::thread t{[=] { do_something_with(s); }};
• by reference, to interact with the smart pointer itself
void print_count(std::shared_ptr<Histo> const& s) {std::cout << s.use_count() << ’\n’;
};auto s = std::make_shared<Histo>();print_count(s);
113
smart _ptr and functionsPass a smart pointer to a function only if the function needs to relyon the smart pointer itself• by value of a unique_ptr, to transfer ownership
void take(std::unique_ptr<Histo> u);auto u = std::make_unique<Histo>();take(u); // errortake(std::move(u)); // ok
• by value of a shared_ptr, to keep the resource alive
auto s = std::make_shared<Histo>();std::thread t{[=] { do_something_with(s); }};
• by reference, to interact with the smart pointer itself
void print_count(std::shared_ptr<Histo> const& s) {std::cout << s.use_count() << ’\n’;
};auto s = std::make_shared<Histo>();print_count(s);
113
smart _ptr and functionsPass a smart pointer to a function only if the function needs to relyon the smart pointer itself• by value of a unique_ptr, to transfer ownership
void take(std::unique_ptr<Histo> u);auto u = std::make_unique<Histo>();take(u); // errortake(std::move(u)); // ok
• by value of a shared_ptr, to keep the resource alive
auto s = std::make_shared<Histo>();std::thread t{[=] { do_something_with(s); }};
• by reference, to interact with the smart pointer itself
void print_count(std::shared_ptr<Histo> const& s) {std::cout << s.use_count() << ’\n’;
};auto s = std::make_shared<Histo>();print_count(s);
114
smart _ptr and functions (cont.)
• Otherwise pass the pointee by (const) reference/pointer
void fill(std::shared_ptr<Histo> s) { if (s) s->fill(); }void fill(Histo* t) { if (t) t->fill(); } // bettervoid fill(Histo& t) { t.fill(); } // better
auto s = make_shared<Histo>();fill(s);fill(s->get());if (s) fill(*s);
• Return a smart _ptr from a function if the function hasdynamically allocated a resource that is passed to the caller
auto factory() { return std::make_unique<Histo>(); }
auto u = factory(); // std::unique_ptr<Histo>
std::shared_ptr<Histo> s = std::move(u);
std::shared_ptr<Histo> s = factory();
114
smart _ptr and functions (cont.)
• Otherwise pass the pointee by (const) reference/pointer
void fill(std::shared_ptr<Histo> s) { if (s) s->fill(); }void fill(Histo* t) { if (t) t->fill(); } // bettervoid fill(Histo& t) { t.fill(); } // better
auto s = make_shared<Histo>();fill(s);fill(s->get());if (s) fill(*s);
• Return a smart _ptr from a function if the function hasdynamically allocated a resource that is passed to the caller
auto factory() { return std::make_unique<Histo>(); }
auto u = factory(); // std::unique_ptr<Histo>
std::shared_ptr<Histo> s = std::move(u);
std::shared_ptr<Histo> s = factory();
114
smart _ptr and functions (cont.)
• Otherwise pass the pointee by (const) reference/pointer
void fill(std::shared_ptr<Histo> s) { if (s) s->fill(); }void fill(Histo* t) { if (t) t->fill(); } // bettervoid fill(Histo& t) { t.fill(); } // better
auto s = make_shared<Histo>();fill(s);fill(s->get());if (s) fill(*s);
• Return a smart _ptr from a function if the function hasdynamically allocated a resource that is passed to the caller
auto factory() { return std::make_unique<Histo>(); }
auto u = factory(); // std::unique_ptr<Histo>std::shared_ptr<Histo> s = std::move(u);
std::shared_ptr<Histo> s = factory();
114
smart _ptr and functions (cont.)
• Otherwise pass the pointee by (const) reference/pointer
void fill(std::shared_ptr<Histo> s) { if (s) s->fill(); }void fill(Histo* t) { if (t) t->fill(); } // bettervoid fill(Histo& t) { t.fill(); } // better
auto s = make_shared<Histo>();fill(s);fill(s->get());if (s) fill(*s);
• Return a smart _ptr from a function if the function hasdynamically allocated a resource that is passed to the caller
auto factory() { return std::make_unique<Histo>(); }
auto u = factory(); // std::unique_ptr<Histo>std::shared_ptr<Histo> s = std::move(u);
std::shared_ptr<Histo> s = factory();
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,
>{std::fopen(· · ·),&std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,
>{std::fopen(· · ·),&std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,
>{std::fopen(· · ·),&std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,
>{std::fopen(· · ·),&std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,int(*)(FILE*)
>{std::fopen(· · ·),&std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,decltype(&std::fclose)
>{std::fopen(· · ·),&std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,std::function<int(FILE*)>
>{std::fopen(· · ·),std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,std::function<void(FILE*)>
>{std::fopen(· · ·),std::fclose
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,std::function<void(FILE*)>
>{std::fopen(· · ·),[](FILE* f){ std::fclose(f); }
};
115
smart _ptr custom deleter
• smart _ptr is a general-purpose resource handler• The resource release is not necessarily done with delete• unique_ptr and shared_ptr support a custom deleter
FILE* f = std::fopen(· · ·);· · ·std::fclose(f);
Usual problems:• Who owns the resource?• Forgetting to release• Releasing twice• Early return/throw
auto s = std::shared_ptr<FILE>{std::fopen(· · ·),std::fclose
};
auto u = std::unique_ptr<FILE,std::function<void(FILE*)>
>{std::fopen(· · ·),[](auto f){ std::fclose(f); }
};