• No results found

TDP004 - Objektorienterd programmering

N/A
N/A
Protected

Academic year: 2022

Share "TDP004 - Objektorienterd programmering"

Copied!
75
0
0

Loading.... (view fulltext now)

Full text

(1)

TDP004 - Objektorienterd programmering

Mallar Handout

Anpassat från material av Christoffer Holm János Dani & Pontus Haglund

Ins tu onen för datavetenskap

(2)

1 Namnrymder

2 Funk onsmallar

3 Klassmallar

4 Exempel

(3)

1 Namnrymder

2 Funk onsmallar

3 Klassmallar

4 Exempel

(4)

3 / 46

Namnrymder

Egen namnrymd

‚ Namnrymder är bra om man har många saker med samma namn

std

är den mest kända namnrymden

‚ men det går också a skapa egna namnrymder där

man kan placera sina saker för a hålla det separerade

från resten av programmet

(5)

4 / 46

Namnrymder

Egen namnrymd namespace NS {

class My_Class {

};

int my_fun(int x) {

return x;

} }

(6)

4 / 46

Namnrymder

Egen namnrymd

‚ Man kan även här separerar deklara on och defini on

‚ Det fungerar som vanligt men man lägger ll

namnrymden innan namnet på det man definierar

(7)

4 / 46

Namnrymder

Egen namnrymd

namespace NS {

class My_Class;

int my_fun(int x);

}

class NS::My_Class {

};

int NS::my_fun(int x) {

return x;

}

(8)

4 / 46

Namnrymder

Egen namnrymd

‚ Allt man kan göra med

std

kan man även göra med sina egna namnrymder

‚ Inklusive a inkludera hela namnrymden

‚ Även här gäller det a man inte göra

using namespace NS

i en h-fil

(9)

4 / 46

Namnrymder

Egen namnrymd

// main.cc

int main() {

NS::My_Class m{};

cout << NS::my_fun(3) << endl;

}

(10)

4 / 46

Namnrymder

Egen namnrymd

// main.cc

using namespace NS;

int main() {

My_Class m{};

cout << my_fun(3) << endl;

}

(11)

1 Namnrymder

2 Funk onsmallar

3 Klassmallar

4 Exempel

(12)

6 / 46

Funk onsmallar

Exempel

int sum(vector<int> const& array) {

int result{};

for (int const& e : array) {

result += e;

}

return result;

}

(13)

6 / 46

Funk onsmallar

Exempel

double sum(vector<double> const& array) {

double result{};

for (double const& e : array) {

result += e;

}

return result;

}

(14)

6 / 46

Funk onsmallar

Exempel

string sum(vector<string> const& array) {

string result{};

for (string const& e : array) {

result += e;

}

return result;

}

(15)

7 / 46

Funk onsmallar

Exempel

‚ De alla är nästan samma kod

‚ jobbigt a behöva skriva det om och om igen

‚ Det vore bra om kompilatorn kunde lösa de a...

(16)

8 / 46

Funk onsmallar

Mallar

template <typename T>

T sum(vector<T> const& array) {

T result{};

for (T const& e : array) {

result += e;

}

return e;

}

(17)

9 / 46

Funk onsmallar

Mallar

De a skapar en funk onsmall

Det är inte en funk on

‚ en funk onsmall är en funk ons generator...

‚ ... en mall som berä ar för kompilatorn hur en funk on ska genereras!

T

är e namn som berä ar vart kompilatorn ska fylla ut med typer som användaren anger

T

kallas för en mallparameter

(18)

10 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum<int>(v1) << endl;

cout << sum<double>(v2) << endl;

}

T sum(vector<T> const&) int sum(vector<int> const&) double sum(vector<double> const&)

(19)

11 / 46

Funk onsmallar

Instansiering

‚ Vi fyller i vad

T

är inom

<...>

De a kallas instansiering

‚ Kompilatorn kommer instansiera (skapa) 2 separata funk oner:

int sum(vector<int> const&)

och

double sum(vector<double> const&)

‚ Vi kan även låta kompilatorn själv härleda vad

T

är...

(20)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&)

int sum(vector<int> const&) double sum(vector<double> const&)

(21)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&)

int sum(vector<int> const&) double sum(vector<double> const&)

(22)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&)

int sum(vector<int> const&) double sum(vector<double> const&)

(23)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&)

int sum(vector<int> const&) double sum(vector<double> const&)

(24)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&)

int sum(vector<int> const&) double sum(vector<double> const&)

(25)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&) int sum(vector<int> const&)

double sum(vector<double> const&)

(26)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&) int sum(vector<int> const&)

double sum(vector<double> const&)

(27)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&) int sum(vector<int> const&)

double sum(vector<double> const&)

(28)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&) int sum(vector<int> const&)

double sum(vector<double> const&)

(29)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&) int sum(vector<int> const&)

double sum(vector<double> const&)

(30)

12 / 46

Funk onsmallar

Instansiering int main() {

vector<int> v1{1, 2, 3};

vector<double> v2{4.5, 6.7};

cout << sum(v1) << endl;

cout << sum(v2) << endl;

}

T sum(vector<T> const&) int sum(vector<int> const&) double sum(vector<double> const&)

(31)

13 / 46

Funk onsmallar

Instansiering

‚ Kompilatorn är väldigt smart

‚ Den kan härleda vad

T

är även om det är ”inbakat”i datatypen

MEN: den kan endast härleda baserat på parametrar...

‚ I nästa exempel blir det omöjligt för kompilatorn a fastställa typen

‚ Vi kan ange en standard typ

(32)

14 / 46

Funk onsmallar

Fungerar ej

template <typename T>

T create() {

return T{};

}

int main() {

// Vad ska den skapa här?!

cout << create() << endl;

// här kommer vi skapa en double cout << create<double>() << endl;

}

(33)

15 / 46

Funk onsmallar

Standardtyp

template <typename T = int>

T create() {

return T{};

}

int main() {

// <...> saknas, så det blir standardtypen int cout << create() << endl;

// här kommer vi skapa en double cout << create<double>() << endl;

}

(34)

16 / 46

Funk onsmallar

Flera mallparametrar

‚ Självklart kan vi ha fler än en mallparameter

‚ De a ger oss två olika typer som kompilatorn kan fylla i

‚ Alla mallparametrar som förekommer som funk ons

parametrar kan kompilatorn härleda...

(35)

17 / 46

Funk onsmallar

template <typename T, typename U>

T add(T a, U b) {

return a + b;

}

int main() {

// skriver ut 4

cout << add<int, int>(1.2, 3.4) << endl;

// skriver ut 3

cout << add(1, 2.3) << endl;

// skriver ut 3.3

cout << add<double>(1, 2.3) << endl;

}

(36)

18 / 46

Funk onsmallar

Fler mallparametrar

‚ E ersom a returtypen är

T

kommer det all d returnera samma typ som första parametern

‚ De a gör så a om vi inte är försik ga med vilka typer vi skickar som parametrar så kan det bli väldigt fel

‚ E sä vi kan lösa de a på är a tvinga användaren

ange vad denne vill a returtypen ska vara

(37)

19 / 46

Funk onsmallar

Fler mallparametrar

template <typename Ret, typename T, typename U>

Ret add(T a, U b) {

return a + b;

}

int main() {

// funkar ej!

cout << add(1, 2.3) << endl;

}

(38)

19 / 46

Funk onsmallar

Fler mallparametrar

template <typename Ret, typename T, typename U>

Ret add(T a, T b) {

return a + b;

}

int main() {

// ger svaret 3.3

cout << add<double>(1, 2.3) << endl;

}

(39)

20 / 46

Funk onsmallar

Fler mallparametrar

‚ Det är vik gt a mallparametern som representerar returtypen är först

‚ kompilatorn kan härleda alla parametrar som är funk onsparameterar

‚ Men när användaren anger mallparametrar explicit innanför

<...>

så kommer kompilatorn sä a dessa från vänster ll höger

‚ E ersom a vi vill a användaren anger returtypen

måste den därför vara först

(40)

21 / 46

Funk onsmallar

Finns e bä re sä ...

(41)

22 / 46

Funk onsmallar

autosom returtyp

template <typename T, typename U>

auto add(T a, U b) {

return a + b;

}

int main() {

// skriver ut 4

cout << add<int, int>(1.2, 3.4) << endl;

// skriver ut 3.3

cout << add(1, 2.3) << endl;

}

(42)

23 / 46

Funk onsmallar

autosom returtyp

‚ När vi anger

auto

som returtyp betyder det a kompilatorn ska härleda vad som returneras

‚ de a funkar endast om alla retursatser i funk onen all d under alla omständigheter returnerar samma typ

auto

tar inte bort typsäkerheten, den bara funderar ut rä typ

‚ om vi har flera retursatser som returnerar olika

kommer det inte a kompilera

(43)

24 / 46

Funk onsmallar

autosom returtyp

auto do_stuff(int x) {

if (x < 0) {

return false; // bool }

return x; // int }

(44)

24 / 46

Funk onsmallar

autosom returtyp

auto do_stuff(int x) {

if (x < 0) {

return false; // bool }

return x; // int }

Kompiler

ar ej

(45)

1 Namnrymder

2 Funk onsmallar

3 Klassmallar

4 Exempel

(46)

26 / 46

Klassmallar

optional

‚ Ibland kan det vara bra a ha en datatyp som ibland innehåller e värde

‚ denna datatyp kallas en

optional

, den har an ngen e värde, som man kan plocka ut

‚ eller så finns inget värde och då får man segmenta on fault (eller liknande) när man försöker plocka ut det

‚ De a innebär a man all d måste fråga huruvida

värdet finns innan man plockar ut det

(47)

27 / 46

Klassmallar

optional

Op onal skulle exempelvis kunna vara användbart om man skulle vilja implementera en funk on som söker e er e värde som uppfyller e kriterium i en databehållare. Om e sådant värde hi as så returneras det men om värdet inte finns så returneras ingen ng.

I exemplet använder vi en klass som har en

unique_ptr

som

pekare ll datamedlemen, de a är en smartpekare som är

den enda pekaren som kan peka på resursen. Den sköter

också llbakalämning av resursen automa skt.

(48)

28 / 46

Klassmallar

Exempel

classOptional_Int {

public:

// sätter data till nullptr Optional_Int() =default;

Optional_Int(intx);

int& get();

boolhas_value()const;

private:

unique_ptr<int> data{};

};

Optional_Int::Optional_Int(int x) : data{make_unique<int>(x)}

{ }

// hämta värdet int& Optional_Int::get() {

return *data;

}

// kolla om det finns ett värde boolOptional_Int::has_value() const {

return data !=nullptr;

}

(49)

28 / 46

Klassmallar

Exempel

classOptional_Double {

public:

// sätter data till nullptr Optional_Double() =default;

Optional_Double(doublex);

double& get();

boolhas_value()const;

private:

unique_ptr<double> data{};

};

Optional_Double::Optional_Double(doublex) : data{make_unique<double>(x)}

{ }

// hämta värdet

double& Optional_Double::get() {

return *data;

}

// kolla om det finns ett värde boolOptional_Double::has_value()const {

return data !=nullptr;

}

(50)

29 / 46

Klassmallar

Generellt?

‚ Vi kan fortsä a skapa en op onal för varje datatyp

‚ De a är ju dock oerhört dskrävande, speciellt om vi vill a det ska fungerar för alla möjliga datatyper (inklusive typer skapade av andra)

‚ Nej, då är nog de a fel sä a göra det på

Vi använder klassmallar istället

(51)

30 / 46

Klassmallar

Klassmallar

template<typenameT>

classOptional {

public:

Optional() =default;

Optional(T x)

: data{make_unique<T>(x)}

{ }

T& get() {

return*data;

}

boolhas_value()const {

returndata !=nullptr;

} private:

unique_ptr<T> data{};

};

int main() {

// skapa en tom optional Optional<int> o1 {};

// skapa en optional med 5 Optional<int> o2 {5};

// skapa en optional med 3.1 Optional<double> o3 {3.1};

if(o1.has_value()) {

cout <<"Falskt!"<< endl;

}

else if(o2.has_value()) {

cout << o2.get() << endl;

} }

(52)

31 / 46

Klassmallar

Klassmallar

Klassmallar fungerar som funk onsmallar med vissa skillnader

‚ En klassmall är en generator för typer

‚ Så

Optional

är inte en typ, medan exempelvis

Optional<int>

är det

‚ Fr.o.m. C++17 kan kompilatorn (för det mesta) härleda

mallparametrarna från konstruktor anropen om varje

mallparameter finns som parameter ll konstruktorn

(53)

32 / 46

Klassmallar

Instansiering int main() {

// här måste vi ange typen Optional<int> o1 {};

// funkar i C++17, ger T = int Optional o2 {5};

// funkar alltid Optional<int> o3 {5};

}

(54)

33 / 46

Klassmallar

Uppdelning i h och cc-filer då?

‚ Men när vi skriver klasser ska vi separera deklara on och defini on i olika filer?

‚ E ersom a klassen är en mall, så beror varje medlemsfunk on på en mall parameter

‚ Därför måste vi för varje funk onskropp som vi definierar utanför klassdefini onen ange a de a beror på en mall

Notera: medlemsfunk oner i en klassmall är inte

nödvändigtvis funk onsmallar

(55)

34 / 46

Klassmallar

Uppdelning i h och cc-filer då?

template<typenameT>

classOptional {

public:

// sätter data till nullptr Optional() =default;

Optional(T x);

T& get();

boolhas_value()const;

private:

unique_ptr<T> data{};

};

template <typenameT>

Optional<T>::Optional(T x) : data{make_unique<T>(x)}

{ }

template <typenameT>

T& Optional<T>::get() {

return *data;

}

template <typenameT>

boolOptional<T>::has_value()const {

return data !=nullptr;

}

(56)

35 / 46

Klassmallar

Uppdelning i h och cc-filer då?

‚ Det finns e problem...

‚ När kompilatorn kompilerar en fil som använder

Optional

måste den känna ll allt om

Optional

utan a kolla i filer som inte har inkluderats

‚ De a innebär a den kommer inte se innehållet i

optional.cc

‚ Därför måste hela defini onen av klassmallen finnas llgänglig i h-filen

‚ Men det finns en lösning...

(57)

36 / 46

Klassmallar

// optional.h

#ifndef OPTIONAL_H

#define OPTIONAL_H template <typename T>

class Optional {

public:

// ...

T& get();

// ...

};

#include "optional.tcc"

#endif

// main.cc

#include "optional.h"

int main() {

// ...

}

// optional.tcc0po template <typename T>

T& Optional<T>::get() {

return *data;

} // ...

(58)

37 / 46

Klassmallar

Uppdelning i h och cc-filer då?

‚ Vi kan inkludera implementa onen i h-filen

‚ Det är rekommenderat a använda filändelsen tcc för implementa on filen så a vi inte förvirrar den med cc filer

‚ Om vi försöker kompilera tcc filer kommer vi a få flera defini oner av samma medlemsfunk oner

‚ En defini on från main.cc och en från optional.tcc

‚ Se därför ll a inte kompilera tcc filer

(59)

38 / 46

Klassmallar

De a går såklart även bra

med funk onsmallar

(60)

39 / 46

Klassmallar

Uppdelning i h och cc-filer då?

// fil.h

#ifndef FIL_H // deklaration

template <typename T, typename U>

auto sum(T a, U b);

#include "fil.tcc"

#endif FIL_H

// fil.tcc

//definition

template <typename T, typename U>

auto sum(T a, T b) {

return a + b;

}

(61)

1 Namnrymder

2 Funk onsmallar

3 Klassmallar

4 Exempel

(62)

41 / 46

Exempel

Ännu mer generell sumÖVERKURS int main()

{

vector<int> v1{1, 2, 3};

cout << sum(v1) << endl; // funkar vector<string> v2{"h", "e", "j"};

cout << sum(v2) << endl; // funkar array<int, 3> a{1, 2, 3};

cout << sum(a) << endl; // funkar ej }

(63)

42 / 46

Exempel

Ännu mer generell sumÖVERKURS

template <typename Container>

auto sum(Container const& c) {

/* värdetypen */ result{};

for (auto const& e : c) {

result += e;

}

return result;

}

(64)

42 / 46

Exempel

Ännu mer generell sumÖVERKURS

‚ Varje behållare har en inretyp som heter

value_type

‚ de a är e alias (e annat namn) för den typ som behållaren innehåller

‚ för a komma åt värdetypen använder vi oss av de a

(65)

42 / 46

Exempel

Ännu mer generell sumÖVERKURS

template <typename Container>

auto sum(Container const& c) {

Container::value_type result{};

for (auto const& e : c) {

result += e;

}

return result;

}

(66)

42 / 46

Exempel

Ännu mer generell sumÖVERKURS

‚ De a fungerar inte för a kompilatorn vet inte om

value_type

är en funk on, en variabel eller en datatyp. Den vet det först när vi har bestämt vad

T

är

‚ de a kallas a

value_type

är e dependent name

‚ kompilatorn får inte acceptera de a, för a vad

value_type

är kan variera beroende på vad

T

är

‚ därför måste vi specificera a

value_type

är en

datatyp med nyckelordet

typename

(67)

42 / 46

Exempel

Ännu mer generell sumÖVERKURS

template <typename Container>

auto sum(Container const& c) {

typename Container::value_type result{};

for (auto const& e : c) {

result += e;

}

return result;

}

(68)

42 / 46

Exempel

Ännu mer generell sumÖVERKURS

typename Container::value_type

är även vår returtyp

‚ Därför kan det även vara bra a vara tydlig vad

returtypen är genom a explicit ange det

(69)

42 / 46

Exempel

Ännu mer generell sumÖVERKURS

template <typename Container>

typename Container::value_type //returtyp sum(Container const& c)

{

typename Container::value_type result{};

for (auto const& e : c) {

result += e;

}

return result;

}

(70)

43 / 46

Exempel

Inretyper

‚ Det finns mycket i C++ som har inreklasser

‚ Standardbiblioteket kryllar av inretyper

‚ Exempelvis så innehåller iteratorer all d också en

value_type

‚ D.v.s. vilken datatyp som iteratorn pekar på

‚ Det finns även andra inretyper som behållare och iteratorer har

‚ Ti a på cppreference.com för a se alla

(71)

44 / 46

Exempel

Iteratorer

template <typename Iterator>

auto sum(Iterator first, Iterator last) {

typename Iterator::value_type result{};

for (auto it{first}; it != last; ++it) {

result += *it;

}

return result;

}

(72)

44 / 46

Exempel

Iteratorer

int main() {

set<int> s{1, 2, 3};

cout << sum(s) << endl;

cout << sum(s.begin(), s.end()) << endl;

vector<int> v{1, 2, 3};

cout << sum(v) << endl;

cout << sum(v.begin(), v.end()) << endl;

}

(73)

45 / 46

Exempel

Egen inretypÖVERKURS

‚ Självklart kan man skapa egna inretyper ll sin klassmall

‚ Man kan an ngen skapa alias m.h.a.

using

‚ eller så kan man skapa inreklasser genom a deklarera en klass inu i sin klass

‚ Notera a inreklasser inte rik gt är klassmallar

‚ Men de är unika beroende på

T

for arande e ersom

a de beror på en klassmall

(74)

46 / 46

Exempel

Egen inretypÖVERKURS

template <typename T>

class My_Class {

using type = T;

class My_Inner {

};

};

template <typenameT>

autocreate_inner() {

return typenameMy_Class<T>::My_Inner{};

}

template <typenameT>

typename My_Class<T>::type create_type() {

return typenameMy_Class<T>::type{};

}

int main() {

My_Class<int>::My_Inner my_inner{};

My_Class<int>::type my_type{};

}

(75)

www.liu.se

References

Related documents

MEN i fall där objekt av våra egna klasstyper har pekare som datamedlemmar, framförallt pekare till manuellt allokerat minne så kommer vi behöva definera hur detta ska fungera i

I den “nya” modellen måste istället initiativ i högre grad komma från stadens administrativa enheter, både för att säkra att strategiska beslut hanteras, men också för

Den är mindre lämplig att använda då man vill sortera elementen ofta eftersom detta är långsammare än för t ex vector.. Det går också långsamt att iterera igenom elementen,

För att vara redo för framtiden måste vi förstå det tankesätt, de farhågor och de möjligheter som morgondagens arbetskraft tror att denna nya teknik kommer att skapa..

stegrats, införa billi~aro arbe temoteder under takttaqanie av höjandat a v fabrikatets kvalitet ~enom förb~ttrin~ av konstruktion , material och et~ kontroll vid

För att få ett effektivt arbetssätt bör man vara öppen för att skapa flera olika typer av specialiserade mikroprocesser.. Ett vanligt fel är att man försöker klämma in

48 Nat 4WD Ljusdals MK Ford Escort Cosw Utgått. Lars

25 Grupp A 0-2000 Skepptuna MK Ford Escort Utgått. Andreas