TDDD38/726G82 - Advanced programming in C++ Inheritance & - - PowerPoint PPT Presentation

tddd38 726g82 advanced programming in c
SMART_READER_LITE
LIVE PREVIEW

TDDD38/726G82 - Advanced programming in C++ Inheritance & - - PowerPoint PPT Presentation

TDDD38/726G82 - Advanced programming in C++ Inheritance & Polymorphism Christoffer Holm Department of Computer and informaon science 1 Inheritance 2 Polymorphism 3 Excepon Handling 4 Smart Pointers 1 Inheritance 2 Polymorphism


slide-1
SLIDE 1

TDDD38/726G82 - Advanced programming in C++

Inheritance & Polymorphism

Christoffer Holm

Department of Computer and informaon science

slide-2
SLIDE 2

1 Inheritance 2 Polymorphism 3 Excepon Handling 4 Smart Pointers

slide-3
SLIDE 3

1 Inheritance 2 Polymorphism 3 Excepon Handling 4 Smart Pointers

slide-4
SLIDE 4

3 / 35

Inheritance

Mental Model class Employee { string name{"Christoffer"}; int id{44}; }; class Teacher : public Employee { string course{"TDDD38"}; }; Teacher c{};

slide-5
SLIDE 5

3 / 35

Inheritance

Mental Model class Employee { string name{"Christoffer"}; int id{44}; }; class Teacher : public Employee { string course{"TDDD38"}; }; Teacher c{};

name

Christoffer

id

44

Employee course

TDDD38

Teacher

slide-6
SLIDE 6

4 / 35

Inheritance

Protected members class Base { public: Base(int x) : x{x} { } private: int x; }; struct Derived : Base { Derived(int x) : Base{x} { } int get() { return x; // Error! } };

slide-7
SLIDE 7

4 / 35

Inheritance

Protected members class Base { public: Base(int x) : x{x} { } protected: int x; }; struct Derived : Base { Derived(int x) : Base{x} { } int get() { return x; // OK! } };

slide-8
SLIDE 8

5 / 35

Inheritance

Protected members protected members are:

‚ inaccessible outside the class; ‚ accessible within derived classes; ‚ accessible by friends of the class.

slide-9
SLIDE 9

6 / 35

Inheritance

Constructors class Base { public: Base(int x); private: int x; }; Base::Base(int x) : x{x} { } class Derived : public Base { public: Derived(int x, double y); private: double y; }; Derived::Derived(int x, double y) : Base{x}, y{y} { }

slide-10
SLIDE 10

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

slide-11
SLIDE 11

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

Derived11

slide-12
SLIDE 12

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

Derived1 Derived11

slide-13
SLIDE 13

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

Base Derived1 Derived11

slide-14
SLIDE 14

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

Base Derived1 Derived11

slide-15
SLIDE 15

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

Base y

2.34

Derived1 Derived11

slide-16
SLIDE 16

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

Base y

2.34

Derived1 z

56

Derived11

slide-17
SLIDE 17

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

Base y

2.34

Derived1 z

56

slide-18
SLIDE 18

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

Base y

2.34

Derived1

slide-19
SLIDE 19

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

Base y

2.34

slide-20
SLIDE 20

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

Base

slide-21
SLIDE 21

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

x

1

slide-22
SLIDE 22

7 / 35

Inheritance

Inializaon & Destrucon class Base { int x{1}; }; class Derived1 : public Base { double y{2.34}; }; class Derived11 final : public Derived1 { int z{56}; }; Derived11 obj{};

slide-23
SLIDE 23

8 / 35

Inheritance

Inializaon & Destrucon

An object is inialized in the following order:

  • 1. inialize base classes (call constructors);
  • 2. inialize all data members in declaraon order.

An object is destroyed in the following order:

  • 1. destroy all data members in reverse order;
  • 2. destroy base classes in reverse order.
slide-24
SLIDE 24

9 / 35

Inheritance

Types of Inheritance

‚ public inheritance ‚ protected inheritance ‚ private inheritance

slide-25
SLIDE 25

9 / 35

Inheritance

Types of Inheritance

‚ public inheritance ‚ class Derived : public Base ‚ All public and protected members of Base are available as public and protected respecvely in

Derived.

‚ protected inheritance ‚ private inheritance

slide-26
SLIDE 26

9 / 35

Inheritance

Types of Inheritance

‚ public inheritance ‚ protected inheritance ‚ class Derived : protected Base ‚ All public and protected members of Base are available as protected in Derived. ‚ private inheritance

slide-27
SLIDE 27

9 / 35

Inheritance

Types of Inheritance

‚ public inheritance ‚ protected inheritance ‚ private inheritance ‚ class Derived : private Base ‚ All members of Base are inherited as private and therefore inaccessible from Derived.

slide-28
SLIDE 28

10 / 35

Inheritance

What will happen? Why? struct Base { ~Base() { cout << "Base" << endl; } }; struct Derived : public Base { ~Derived() { cout << "Derived" << endl; } }; int main() { Derived d{}; }

slide-29
SLIDE 29

1 Inheritance 2 Polymorphism 3 Excepon Handling 4 Smart Pointers

slide-30
SLIDE 30

12 / 35

Polymorphism

Dynamic dispatch

void print1() { cout << "1" << endl; } struct Base { Base() = default; void print() { foo(); } protected: using function_t = void (*)(); Base(function_t foo) : foo{foo} { } private: function_t foo{print1}; }; void print2() { cout << "2" << endl; } struct Derived : public Base { // inherit constructors from Base using Base::Base; // override default constructor Derived() : Derived{print2} { } }; int main() { Base* bp {new Base{}}; bp->print(); delete bp; bp = new Derived{}; bp->print(); }

slide-31
SLIDE 31

13 / 35

Polymorphism

Easier dynamic dispatch

struct Base { virtual void print() { cout << "1" << endl; } }; struct Derived : public Base { void print() override { cout << "2" << endl; } }; int main() { Base* bp {new Base{}}; bp->print(); delete bp; bp = new Derived{}; bp->print(); }

slide-32
SLIDE 32

14 / 35

Polymorphism

What will happen? Why? struct Base { ~Base() { cout << "Base" << endl; } }; struct Derived : public Base { ~Derived() { cout << "Derived" << endl; } }; int main() { Base* bp{new Derived()}; delete bp; }

slide-33
SLIDE 33

14 / 35

Polymorphism

What will happen? Why? struct Base { virtual ~Base() { cout << "Base" << endl; } }; struct Derived : public Base { ~Derived() { cout << "Derived" << endl; } }; int main() { Base* bp{new Derived()}; delete bp; }

slide-34
SLIDE 34

15 / 35

Polymorphism

Virtual destructor

‚ bp is of type Base* (the stac type of bp); ‚ deleng bp will call the destructor of Base regardless of what the dynamic type of bp is; ‚ However, if the destructor of base is virtual the compiler will use dynamic dispatch to call the overriden destructor from Derived, which in turn will call the

Base destructor.

Therefore we should always declare destructors as virtual for types which will be used through pointers.

slide-35
SLIDE 35

16 / 35

Polymorphism

Virtual Table

struct Base { virtual ~Base(); virtual void fun(); int val1{1}; int val2{2}; }; struct Derived1 : public Base { void fun() override; double d{3.4}; }; struct Derived11 : public Derived1 { void fun() final; }; void Base::fun() { cout << val1 << ' ' << val2; } void Derived1::fun() { Base::fun(); cout << ' ' << d; } void Derived11::fun() { cout << "Derived11 "; Derived1::fun(); }

slide-36
SLIDE 36

17 / 35

Polymorphism

Virtual Table Base* bp{new Base{}};

vptr val1

1

val2

2

Base d

3.4

Derived1 Derived11 bp Base * vtable for Base +dtor: Base::~Base +fun: Base::fun vtable for Derived1 +dtor: Derived1::~Derived1 +fun: Derived1::fun vtable for Derived11 +dtor: Derived11::~Derived11 +fun: Derived11::fun

slide-37
SLIDE 37

17 / 35

Polymorphism

Virtual Table Base* bp{new Derived1{}};

vptr val1

1

val2

2

Base d

3.4

Derived1 Derived11 bp Base * vtable for Base +dtor: Base::~Base +fun: Base::fun vtable for Derived1 +dtor: Derived1::~Derived1 +fun: Derived1::fun vtable for Derived11 +dtor: Derived11::~Derived11 +fun: Derived11::fun

slide-38
SLIDE 38

17 / 35

Polymorphism

Virtual Table Base* bp{new Derived11{}};

vptr val1

1

val2

2

Base d

3.4

Derived1 Derived11 bp Base * vtable for Base +dtor: Base::~Base +fun: Base::fun vtable for Derived1 +dtor: Derived1::~Derived1 +fun: Derived1::fun vtable for Derived11 +dtor: Derived11::~Derived11 +fun: Derived11::fun

slide-39
SLIDE 39

18 / 35

Polymorphism

Run-me type informaon (RTTI)

‚ Each entry in the vtable contains informaon about the dynamic type; ‚ This data is accessible with typeid.

struct Base { virtual ~Base() = default; }; struct Derived1 : public Base { }; struct Derived11 : public Derived1 { }; int main() { Base b; Derived1 d1, d2; Derived11 d11; cout << typeid(b).name() << endl; cout << typeid(d1).hash_code() << endl; cout << (typeid(d1) == typeid(b)) << endl; cout << (typeid(d1) == typeid(d2)) << endl; cout << (typeid(d1) == typeid(d11)) << endl; }

slide-40
SLIDE 40

19 / 35

Polymorphism

Run-me type informaon (RTTI)

‚ typeid is used to check the exact dynamic type; ‚ We can use dynamic_cast to cast pointers or references to objects into some pointer or reference which is compable with the dynamic type of the

  • bject.

struct Base { virtual ~Base() = default; }; struct Derived1 : public Base { }; struct Derived11 : public Derived1 { }; int main() { Base* bp{new Derived1()}; cout << (dynamic_cast<Base*>(bp) == nullptr) << endl; cout << (dynamic_cast<Derived11*>(bp) == nullptr) << endl; }

slide-41
SLIDE 41

20 / 35

Polymorphism

Run-me type informaon (RTTI)

struct Base { virtual ~Base() = default; }; struct Derived1 : public Base { int foo() { return 1; } }; struct Derived11 : public Derived1 { }; int main() { Base* bp{new Derived1()}; // won't work, since foo is a non-virtual function in Derived cout << bp->foo() << endl; // will work, since we converted bp to Derived* which has access to foo cout << dynamic_cast<Derived1&>(*bp).foo() << endl; // will throw an exception of type std::bad_cast cout << dynamic_cast<Derived11&>(*bp).foo() << endl; }

slide-42
SLIDE 42

21 / 35

Polymorphism

What will happen? Why?

struct Base { virtual ~Base() = default; }; struct Derived1 : public Base { }; struct Derived11 : public Derived1 { }; struct Derived2 : public Base { }; int main() { Base* bp{new Derived1()}; if (dynamic_cast<Base*>(bp)) cout << "B "; if (dynamic_cast<Derived1*>(bp)) cout << "D1 "; if (dynamic_cast<Derived11*>(bp)) cout << "D11 "; if (dynamic_cast<Derived2*>(bp)) cout << "D2 "; }

slide-43
SLIDE 43

22 / 35

Polymorphism

Slicing

struct Base { virtual void print() {cout << x;} int x{1}; }; struct Derived : public Base { void print() override {cout << y;} int y{2}; }; void print(Base b) { b.print(); } int main() { Derived d{}; print(d); }

‚ Copying d into b will cause slicing; ‚ Will only copy the Base part of d and thus lose all informaon about d being a Derived. ‚ Always use references or pointers!

slide-44
SLIDE 44

1 Inheritance 2 Polymorphism 3 Excepon Handling 4 Smart Pointers

slide-45
SLIDE 45

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { return; }

slide-46
SLIDE 46

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { return; }

slide-47
SLIDE 47

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { return; }

slide-48
SLIDE 48

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { return; }

slide-49
SLIDE 49

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { return; }

slide-50
SLIDE 50

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { throw std::exception{""}; }

slide-51
SLIDE 51

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { throw std::exception{""}; }

slide-52
SLIDE 52

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { throw std::exception{""}; }

slide-53
SLIDE 53

24 / 35

Excepon Handling

Model

int main() { try { fun1(); // ... } catch (std::exception& e) { cerr << e.what(); } } void fun1() { // ... fun2(); // ... return; } void fun2() { throw std::exception{""}; }

slide-54
SLIDE 54

25 / 35

Excepon Handling

Excepons

‚ Anything can be thrown; ‚ however the language and the standard library throws

  • bjects derived from std::exception;

‚ there are several excepon classes defined in

<stdexcept>;

‚ always throw by-value: don’t throw pointers; ‚ always catch by-reference to avoid slicing; ‚ if an excepon isn’t caught std::terminate will be called, thus terminang the program immediately.

slide-55
SLIDE 55

26 / 35

Excepon Handling

Lifeme & Stack Unwinding

int foo() { int z{}; throw z; } struct Cls { Cls() try : y{foo()} { } catch (int i) { cerr << i; throw "cls error"; } int y; }; int main() try { int x{}; Cls c{}; // ... } catch (char const* str) { cerr << str; } catch (std::exception& e) { cerr << e.what(); } catch (...) { cerr << "Unknown error"; }

slide-56
SLIDE 56

27 / 35

Excepon Handling

Excepon usage

‚ Excepons are very slow when they are thrown; ‚ should only be thrown in exceponal situaons; ‚ don’t use excepons for control flow, it will severely slow down your program.

slide-57
SLIDE 57

28 / 35

Excepon Handling

noexcept

‚ Due to stack unwinding, the compiler have to generate some extra code to handle excepons; ‚ this extra generated code can be costly, especially if it is not used; ‚ the noexcept-specifier tells the compiler that no excepons will be thrown from a funcon; ‚ declaring funcons as noexcept will allow the compiler to not generate code for excepon handling.

slide-58
SLIDE 58

29 / 35

Excepon Handling

noexcept void fun() noexcept;

A funcon declared noexcept is allowed to call throwing funcons, as long as the excepon is caught before it reaches the noexcept funcon; If an excepon is thrown inside a noexcept funcon,

std::terminate is called, thus aborng the program.

slide-59
SLIDE 59

29 / 35

Excepon Handling

noexcept void fun() noexcept;

‚ A funcon declared noexcept is allowed to call throwing funcons, as long as the excepon is caught before it reaches the noexcept funcon; If an excepon is thrown inside a noexcept funcon,

std::terminate is called, thus aborng the program.

slide-60
SLIDE 60

29 / 35

Excepon Handling

noexcept void fun() noexcept;

‚ A funcon declared noexcept is allowed to call throwing funcons, as long as the excepon is caught before it reaches the noexcept funcon; ‚ If an excepon is thrown inside a noexcept funcon,

std::terminate is called, thus aborng the program.

slide-61
SLIDE 61

1 Inheritance 2 Polymorphism 3 Excepon Handling 4 Smart Pointers

slide-62
SLIDE 62

31 / 35

Smart Pointers

Excepons and Memory Management

int* get(int x) { if (x < 0) throw std::out_of_range{""}; return new int{x}; } struct Cls { Cls(int x, int y) : data1{get(x)}, data2{get(y)} { } ~Cls() { delete data1; delete data2; } int* data1; int* data2; };

slide-63
SLIDE 63

32 / 35

Smart Pointers

Excepons and Memory Management

struct Cls { Cls(int x, int y) try : data1{get(x)}, data2{get(y)} { } catch (...) { delete data1; throw; } ~Cls() { delete data1; delete data2; } int* data1; int* data2; };

slide-64
SLIDE 64

32 / 35

Smart Pointers

Excepons and Memory Management

struct Cls { Cls(int x, int y) try : data1{get(x)}, data2{get(y)} { } catch (...) { delete data1; throw; } ~Cls() { delete data1; delete data2; } int* data1; int* data2; };

Segmentaon Fault

slide-65
SLIDE 65

33 / 35

Smart Pointers

Excepons and Memory Management

struct Cls { Cls(int x, int y) : data1{get(x)} { try { data2 = get(y); } catch (...) { delete data1; throw; } } ~Cls() { // ... } // ... };

slide-66
SLIDE 66

33 / 35

Smart Pointers

Excepons and Memory Management

struct Cls { Cls(int x, int y) : data1{get(x)} { try { data2 = get(y); } catch (...) { delete data1; throw; } } ~Cls() { // ... } // ... };

Painfully Tedious

slide-67
SLIDE 67

34 / 35

Smart Pointers

Smart Pointers

‚ Use RAII to automacally handle memory; ‚ reside in <memory>; ‚ std::unique_ptr ‚ std::shared_ptr

slide-68
SLIDE 68

34 / 35

Smart Pointers

Smart Pointers

‚ Use RAII to automacally handle memory; ‚ reside in <memory>; ‚ std::unique_ptr ‚ Represent ownership; ‚ each unique_ptr points to a unique object; ‚ when the pointer is destroyed, the object is deallocated; ‚ cannot be copied, only moved. ‚ std::shared_ptr

slide-69
SLIDE 69

34 / 35

Smart Pointers

Smart Pointers

‚ Use RAII to automacally handle memory; ‚ reside in <memory>; ‚ std::unique_ptr

// hand off manually allocated memory std::unique_ptr<int> ptr1{new int{5}}; // let the smart pointer handle it std::unique_ptr<int> ptr2{make_unique<int>(5)}; // move ptr2 to ptr3 std::unique_ptr<int> ptr3{std::move(ptr2)}; // ptr2 is now null

‚ std::shared_ptr

slide-70
SLIDE 70

34 / 35

Smart Pointers

Smart Pointers

‚ Use RAII to automacally handle memory; ‚ reside in <memory>; ‚ std::unique_ptr ‚ std::shared_ptr ‚ Represent shared ownership on an object; ‚ Can be copied; ‚ Will deallocate the memory when all shared pointers have been destroyed; ‚ Should be avoided if possible since it is quite expensive.

slide-71
SLIDE 71

34 / 35

Smart Pointers

Smart Pointers

‚ Use RAII to automacally handle memory; ‚ reside in <memory>; ‚ std::unique_ptr ‚ std::shared_ptr

std::shared_ptr<int> ptr1{new int{5}}; std::shared_ptr<int> ptr2{make_shared<int>(5)}; std::shared_ptr<int> ptr3{ptr2}; // both ptr2 and ptr3 point to the same object // the object will be deallocated once both ptr2 and ptr3 // have been destroyed.

slide-72
SLIDE 72

35 / 35

Smart Pointers

Nice soluon

std::unique_ptr<int> get(int x) { if (x < 0) throw std::out_of_range{""}; return std::make_unique<int>(x); } struct Cls { Cls(int x, int y) : data1{get(x)}, data2{get(y)} { } ~Cls() = default; std::unique_ptr<int> data1; std::unique_ptr<int> data2; };

slide-73
SLIDE 73

35 / 35

Smart Pointers

Nice soluon

std::unique_ptr<int> get(int x) { if (x < 0) throw std::out_of_range{""}; return std::make_unique<int>(x); } struct Cls { Cls(int x, int y) : data1{get(x)}, data2{get(y)} { } ~Cls() = default; std::unique_ptr<int> data1; std::unique_ptr<int> data2; };

Perfecon!

slide-74
SLIDE 74

www.liu.se