tddd38/726g82- advancedprogrammingin c++tddd38/lecture/slides/inheritance.pdf · 4/35 inheritance...

Post on 17-Aug-2020

3 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

TDDD38/726G82 -Advanced programming inC++Inheritance & Polymorphism

Christoffer Holm

Department of Computer and informa on science

1 Inheritance2 Polymorphism3 Excep on Handling4 Smart Pointers

1 Inheritance2 Polymorphism3 Excep on Handling4 Smart Pointers

3 / 35

InheritanceMental Model

class Employee{string name{"Christoffer"};int id{44};

};class Teacher : public Employee{string course{"TDDD38"};

};Teacher c{};

3 / 35

InheritanceMental 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

4 / 35

InheritanceProtected 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!

}};

4 / 35

InheritanceProtected 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!

}};

5 / 35

InheritanceProtected members

protectedmembers are:

‚ inaccessible outside the class;

‚ accessible within derived classes;

‚ accessible by friends of the class.

6 / 35

InheritanceConstructors

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}

{}

7 / 35

InheritanceIni aliza on & Destruc on

class Base{int x{1};

};class Derived1 : public Base{double y{2.34};

};class Derived11 final: public Derived1

{int z{56};

};

Derived11 obj{};

7 / 35

InheritanceIni aliza on & Destruc on

class Base{int x{1};

};class Derived1 : public Base{double y{2.34};

};class Derived11 final: public Derived1

{int z{56};

};

Derived11 obj{};

Derived11

7 / 35

InheritanceIni aliza on & Destruc on

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

7 / 35

InheritanceIni aliza on & Destruc on

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

7 / 35

InheritanceIni aliza on & Destruc on

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

7 / 35

InheritanceIni aliza on & Destruc on

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

Basey 2.34

Derived1

Derived11

7 / 35

InheritanceIni aliza on & Destruc on

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

Basey 2.34

Derived1

z 56

Derived11

7 / 35

InheritanceIni aliza on & Destruc on

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

Basey 2.34

Derived1

z 56

7 / 35

InheritanceIni aliza on & Destruc on

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

Basey 2.34

Derived1

7 / 35

InheritanceIni aliza on & Destruc on

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

Basey 2.34

7 / 35

InheritanceIni aliza on & Destruc on

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

7 / 35

InheritanceIni aliza on & Destruc on

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

7 / 35

InheritanceIni aliza on & Destruc on

class Base{int x{1};

};class Derived1 : public Base{double y{2.34};

};class Derived11 final: public Derived1

{int z{56};

};

Derived11 obj{};

8 / 35

InheritanceIni aliza on & Destruc on

An object is ini alized in the following order:

1. ini alize base classes (call constructors);

2. ini alize all data members in declara on order.

An object is destroyed in the following order:

1. destroy all data members in reverse order;

2. destroy base classes in reverse order.

9 / 35

InheritanceTypes of Inheritance

‚ public inheritance

‚ protected inheritance

‚ private inheritance

9 / 35

InheritanceTypes of Inheritance

‚ public inheritance

‚ class Derived : public Base

‚ All public and protected members of Base areavailable as public and protected respec vely inDerived.

‚ protected inheritance

‚ private inheritance

9 / 35

InheritanceTypes of Inheritance

‚ public inheritance

‚ protected inheritance

‚ class Derived : protected Base

‚ All public and protected members of Base areavailable as protected in Derived.

‚ private inheritance

9 / 35

InheritanceTypes of Inheritance

‚ public inheritance

‚ protected inheritance

‚ private inheritance

‚ class Derived : private Base

‚ All members of Base are inherited as private andtherefore inaccessible from Derived.

10 / 35

InheritanceWhat will happen? Why?

struct Base{~Base() { cout << "Base" << endl; }

};struct Derived : public Base{~Derived() { cout << "Derived" << endl; }

};int main(){Derived d{};

}

1 Inheritance2 Polymorphism3 Excep on Handling4 Smart Pointers

12 / 35

PolymorphismDynamic 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 Baseusing Base::Base;// override default constructorDerived(): Derived{print2} { }

};int main(){Base* bp {new Base{}};bp->print();delete bp;

bp = new Derived{};bp->print();

}

13 / 35

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

}

14 / 35

PolymorphismWhat 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;

}

14 / 35

PolymorphismWhat 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;

}

15 / 35

PolymorphismVirtual destructor

‚ bp is of type Base* (the sta c type of bp);

‚ dele ng bp will call the destructor of Base regardless ofwhat the dynamic type of bp is;

‚ However, if the destructor of base is virtual thecompiler will use dynamic dispatch to call the overridendestructor from Derived, which in turn will call theBase destructor.

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

16 / 35

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

}

17 / 35

PolymorphismVirtual Table

Base* bp{new Base{}};

vptr

val1 1

val2 2

Base

d 3.4

Derived1

Derived11

bpBase *

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

17 / 35

PolymorphismVirtual Table

Base* bp{new Derived1{}};

vptr

val1 1

val2 2

Base

d 3.4

Derived1

Derived11

bpBase *

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

17 / 35

PolymorphismVirtual Table

Base* bp{new Derived11{}};

vptr

val1 1

val2 2

Base

d 3.4

Derived1

Derived11

bpBase *

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

18 / 35

PolymorphismRun- me type informa on (RTTI)

‚ Each entry in the vtable contains informa on about thedynamic 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;

}

19 / 35

PolymorphismRun- me type informa on (RTTI)

‚ typeid is used to check the exact dynamic type;

‚ We can use dynamic_cast to cast pointers orreferences to objects into some pointer or referencewhich is compa ble with the dynamic type of theobject.

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;

}

20 / 35

PolymorphismRun- me type informa on (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 Derivedcout << bp->foo() << endl;// will work, since we converted bp to Derived* which has access to foocout << dynamic_cast<Derived1&>(*bp).foo() << endl;// will throw an exception of type std::bad_castcout << dynamic_cast<Derived11&>(*bp).foo() << endl;

}

21 / 35

PolymorphismWhat 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 ";

}

22 / 35

PolymorphismSlicing

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

informa on about d being a Derived.‚ Always use references or pointers!

1 Inheritance2 Polymorphism3 Excep on Handling4 Smart Pointers

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){return;

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){return;

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){return;

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){return;

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){return;

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){throw std::exception{""};

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){throw std::exception{""};

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){throw std::exception{""};

}

24 / 35

Excep on HandlingModel

int main(){try{fun1();// ...

}catch (std::exception& e){cerr << e.what();

}}

void fun1(){// ...fun2();// ...return;

}

void fun2(){throw std::exception{""};

}

25 / 35

Excep on HandlingExcep ons

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

objects derived from std::exception;‚ there are several excep on classes defined in

<stdexcept>;‚ always throw by-value: don’t throw pointers;‚ always catch by-reference to avoid slicing;‚ if an excep on isn’t caught std::terminate will be

called, thus termina ng the program immediately.

26 / 35

Excep on HandlingLife me & 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";

}

27 / 35

Excep on HandlingExcep on usage

‚ Excep ons are very slow when they are thrown;

‚ should only be thrown in excep onal situa ons;

‚ don’t use excep ons for control flow, it will severelyslow down your program.

28 / 35

Excep on Handlingnoexcept

‚ Due to stack unwinding, the compiler have to generatesome extra code to handle excep ons;

‚ this extra generated code can be costly, especially if it isnot used;

‚ the noexcept-specifier tells the compiler that noexcep ons will be thrown from a func on;

‚ declaring func ons as noexcept will allow the compilerto not generate code for excep on handling.

29 / 35

Excep on Handlingnoexcept

void fun() noexcept;

‚ A func on declared noexcept is allowed to callthrowing func ons, as long as the excep on is caughtbefore it reaches the noexcept func on;

‚ If an excep on is thrown inside a noexcept func on,std::terminate is called, thus abor ng the program.

29 / 35

Excep on Handlingnoexcept

void fun() noexcept;

‚ A func on declared noexcept is allowed to callthrowing func ons, as long as the excep on is caughtbefore it reaches the noexcept func on;

‚ If an excep on is thrown inside a noexcept func on,std::terminate is called, thus abor ng the program.

29 / 35

Excep on Handlingnoexcept

void fun() noexcept;

‚ A func on declared noexcept is allowed to callthrowing func ons, as long as the excep on is caughtbefore it reaches the noexcept func on;

‚ If an excep on is thrown inside a noexcept func on,std::terminate is called, thus abor ng the program.

1 Inheritance2 Polymorphism3 Excep on Handling4 Smart Pointers

31 / 35

Smart PointersExcep ons 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;

};

32 / 35

Smart PointersExcep ons 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;

};

32 / 35

Smart PointersExcep ons 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;

};

Segmenta on Fault

33 / 35

Smart PointersExcep ons and Memory Management

struct Cls{Cls(int x, int y) : data1{get(x)}{try{data2 = get(y);

}catch (...){delete data1;throw;

}}~Cls(){// ...

}// ...

};

33 / 35

Smart PointersExcep ons and Memory Management

struct Cls{Cls(int x, int y) : data1{get(x)}{try{data2 = get(y);

}catch (...){delete data1;throw;

}}~Cls(){// ...

}// ...

};

Painfully Tedious

34 / 35

Smart PointersSmart Pointers

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

34 / 35

Smart PointersSmart Pointers

‚ Use RAII to automa cally 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

34 / 35

Smart PointersSmart Pointers

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

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

‚ std::shared_ptr

34 / 35

Smart PointersSmart Pointers

‚ Use RAII to automa cally 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.

34 / 35

Smart PointersSmart Pointers

‚ Use RAII to automa cally 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.

35 / 35

Smart PointersNice solu on

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;

};

35 / 35

Smart PointersNice solu on

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;

};

Perfec on!

www.liu.se

top related