Inhoud Les 1
Les 4
Les 7
Les 10
Les 13
Les 16
Les 19
Les 2
Les 5
Les 8
Les 11
Les 14
Les 17
Les 20
Les 3
Les 6
Les 9
Les 12
Les 15
Les 18
Les 21
0
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 1
Werkvormen OGOPRG OGOPRG-co1 + OGOPRG-pr1 = 112 SBU. 21 uur theorie. 14 uur practicum. 77 uur zelfstudie = 9,5 uur/week zelfstudie! Toetsing: Schriftelijke toets OGOPRG-co1 in week 8 en 10 van dit kwartaal. OGOPRG-pr1 practicumopgaven worden afgetekend op het practicum. Alle opgaven moeten voldoende zijn.
2
Inhoud Objectgeoriënteerd Programmeren in C++. responsibility driven design (ontwerpen uitgaande van
verantwoordelijkheden). information hiding (het afschermen van informatie door middel van het scheiden van interface en implementatie). abstraction (het afschermen van complexiteit door middel van het scheiden van interface en implementatie). inheritance (het mogelijk maken van een nieuwe vorm van hergebruik, ... is een ... in plaats van ... heeft een ...). polymorphism (veelvormigheid mogelijk gemaakt door dynamic binding).
Objectgeoriënteerd Ontwerpen met UML.
klasse- en objectdiagrammen. use-case-diagram. sequence- en collaborationdiagrammen. toestands- en activiteitendiagrammen. 3
Plaats in curriculum Bouwt verder op GESPRG en MICPRG. Voorbereiding voor RTSYST (Real-Time Systemen) in
ECK Voorbereiding voor minor Embedded Systems
4
Leermiddelen Blackboard OGOPRG (en ook
http://bd.eduweb.hhs.nl/ogoprg): Sheets, handouts. Studiewijzer. Practicumopdrachten. Dictaat: Objectgeoriënteerd Programmeren in C++.
Boek: Warmer & Kleppe, Praktisch UML,
5de editie ISBN 9789043020558.
Ontwikkelomgeving: Microsoft Visual Studio Express 2013 for Windows
Desktop. Visual Paradigm for UML 12.0. 5
Een stapje verder... ...met programmeren en ontwerpen. Van gestructureerd naar objectgeoriënteerd.
C++ is een uitbreiding op C: alles wat in C kan, kan ook in C++. veel wat in C kan, kan in C++ beter (struct, array, c-string enz).
GESPRG en MICPRG zijn het fundament voor OGOPRG. Zie dictaat blz. 1. 6
Dat is een lang verhaal... Gestructureerde programmeertalen: ±1945 assembler, ±1957 FORTRAN, ±1960 Algol60,
±1972 C (1989 std ANSI C)
Software crisis: Software niet op tijd geleverd. Software duurder dan afgesproken. Software niet foutloos.
Idee voor de oplossing: Herbruikbare software componenten maken. Deze componenten gebruiken bij maken van grote uitbreidbare en
onderhoudbare software systemen.
Objectgeoriënteerde programmeertalen: ±1967 Simula, ±1976 Smalltalk, ±1983 C++ (1998 std C++), ±1995 Java
(Sun), ±2000 C# (Microsoft).
7
... maar ook een kort verhaal
8
... maar ook een kort verhaal
9
Inleiding C++ Bjarne Stroustrup:
“C++ is designed to: be a better C.
support data abstraction. support object-oriented programming. support generic programming.”
Huiswerk: Dictaat Hs. 1 en practicumopdracht 1. 10
Practicumopdracht 1a #include
#include <string> using namespace std; int main() { cout << "Geef je email adres: "; string mailAdres; cin >> mailAdres; string::size_type indexAt = mailAdres.find("@"); if (indexAt != string::npos) { cout << "Gebruiker: " << mailAdres.substr(0, indexAt) << endl; cout << "Machine: " << mailAdres.substr(indexAt + 1) << endl; } else { cout << mailAdres << " is geen geldig email adres!" << endl; } cout << "Druk op de return-toets." << endl; cin.get(); cin.get(); return 0; } 11
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 2
Vraag Welke software hoeft nooit
uitgebreid of veranderd te worden?
Software die niemand gebruikt. 13
Nieuw! Objectgeoriënteerd Programmeren is een nieuwe
manier van denken ... ... over hoe we code en informatie in een computerprogramma kunnen structureren. Programmeer paradigma’s: imperative (C, Pascal).
functional (LISP, Haskell). logic (Prolog). object oriented (C++, Java, C#).
generic (ADA, C++, Java, C#). 14
Objectgeoriënteerd Denken De manier van probleem oplossen die gebruikt wordt
bij de objectgeoriënteerde programmeertalen en ontwerpmethoden lijkt vaak op de manier van probleem oplossen die mensen in het dagelijks leven ook gebruiken.
15
Voorbeeld Ik wil mijn oma een bosje bloemen sturen.
• object • message + arguments • receiver's responsibility • method (information hiding)
16
Sturen van een bosje bloemen… 1: Bezorg bloemen
4: Betaal
2: Bezorg bloemen
6: Betaal 5: Stort
3: Bezorg bloemen
7: Betaal
17
UML sequentiediagram
18
Vraag Wat is het verschil tussen een message en een functie? Een message heeft een bepaalde receiver. De method die bij de message hoort is afhankelijk van de receiver. De receiver van een message kan ook tijdens run-time worden bepaald. Dynamic binding between the message (function name) and method (code).
19
Class en object Sonja (bloemiste):
• class • instance (object) • hierarchy • inheritance (base and derived) 20
Verband tussen classes Aggregation = heeft een (of meer) Inheritance = is een (speciaal soort)
UML klassendiagram. 21
Method binding Zoek in class van het receiver object. Als daar geen method is zoek dan in de base class van
de class van het receiver object. Als daar geen method is zoek dan in de base class van de base class van de class van het receiver object. Enzovoort. Een method uit de base class kan overriden worden door een method in een derived class. 22
Doel van object oriëntatie Construeren van herbruikbare software componenten. Gebruiken van deze componenten bij het construeren
van grote aanpasbare en uitbreidbare systemen.
Huiswerk: Bestudeer hoofdstuk 2 t/m 2.1 van het dictaat. 24
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 3
Herbruikbare component: Breuk Waarom wil je programma’s maken die rekenen met
breuken in plaats van met floating point getallen (double)? Waarom wil je een component Breuk maken? Hoe doe je dat in C? Wat zijn de nadelen van de C oplossing? Hoe kan het beter in C++? Wat zijn de voordelen van de C++ oplossing? Kan het nog mooier? Dictaat H2.2 t/m 2.28 (4 lessen).
26
Breuk in C Gebruik struct voor dataopslag. Gebruik functies voor bewerkingen.
Struct type declaratie
typedef struct { /* een breuk bestaat uit: */ int boven; /* een teller en */ int onder; /* een noemer */ } Breuk; Breuk normaliseer(Breuk b); Breuk som(Breuk b1, Breuk b2);
Prototypes of Functie declaraties
Functie definitie Breuk som(Breuk b1, Breuk b2) { Breuk s; s.boven = b1.boven * b2.onder + b1.onder * b2.boven; s.onder = b1.onder * b2.onder; s = normaliseer(s); 27 return s; }
Gebruik Breuk b1, b2, b3; b1.boven = 5; b1.onder = 12; b2.boven = 4; b2.onder = 9; b3 = som(b1, b2)
Kan overal in het programma staan!
Nadelen Breuk in C b1.onder = 0 is een “ramp die wacht om te gebeuren”. Programmeur die het beter denkt te weten kan zelf breuken gaan optellen: b3.teller = b1.teller + b2.teller;
b3.noemer = b1.noemer + b2.noemer;
OOPS!
Verschillende programmeurs kunnen in verschillende delen
van het programma de Breuk component uitbreiden: B.v. functies: maal, times, en multiply. 28
Eigenschappen van C Breuk Onderhoudbaarheid. Slecht! Fout in component is niet gemakkelijk te vinden. Iedereen kan data van component “verzieken”. Iedereen kan algoritme implementeren zonder implementatie van de
component te gebruiken. Als er “iets” niet goed gaat met component (b.v. vermenigvuldigen van breuken) moeten we het hele programma doorzoeken.
Aanpasbaarheid en uitbreidbaarheid. Te Goed! Iedereen kan component aanpassen en uitbreiden. Herbruikbaarheid. Slecht! Onduidelijk welke functies bij component horen en welke functies bij deze applicatie horen (en toevallig deze component gebruiken).
29
Breuk in C++ Gebruik class voor dataopslag en bewerkingen. class Breuk { Class declaratie public: void leesin(); Public memberfuncties void drukaf() const; = interface void plus(Breuk b); private: Private data members int boven; int onder; Private memberfunctie void normaliseer(); }; Memberfunctie definitie void Breuk::plus(Breuk b) { boven = boven * b.onder + onder * b.boven; onder *= b.onder; normaliseer(); }
30
Gebruik Breuk a, b; a.leesin(); b.leesin(); a.plus(b); a.drukaf();
Voordelen Breuk in C++ b1.onder = 0 geeft compilerfout. Programmeur die het beter denkt te weten kan zelf geen breuken gaan optellen (zonder Breuk::plus te wijzigen). Component Breuk kan slechts op 1 plaats in het programma uitgebreid worden. 31
Eigenschappen van C++ Breuk Onderhoudbaarheid.
Goed!
Fout in component is gemakkelijk te vinden. Als er “iets” niet goed gaat met component hoef je alleen de implementatie van de
component te doorzoeken. Fout moet in memberfuncties van de component zitten. Je kunt niet (eenvoudig) om de interface van de class heenwerken. (Je kan zelf geen plus maken als je niet bij boven en onder kunt komen.)
Aanpasbaarheid en uitbreidbaarheid. Component kan maar op 1 plaats uitgebreid worden.
Goed!
Private delen kunnen aangepast worden zonder dat de interface
verandert. Dus zonder dat de code die de component gebruikt dit merkt! Zie practicum opgave 2c.
Herbruikbaarheid. Duidelijk welke functies bij component horen.
Huiswerk: Bestudeer paragraaf 2.2 van het dictaat.
Redelijk. 32
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 4
Kenmerken van objecten In de vorige lessen hebben we de volgende objecten
leren kennen: Sonja en Klazien (Groningse bloemiste) (van de class Bloemiste) en a en b (van de class Breuk). Kenmerken: Geheugen (state). Elk object heeft zijn “eigen” geheugen.
Gedrag (behavior). Alle objecten van dezelfde class hebben
hetzelfde gedrag. Identiteit (identity). Elk object heeft een “eigen” identiteit (b.v. een naam). 34
Class Breuk (versie 1) class Breuk { public: Breuk(); Breuk(int t); Breuk(int t, int n); int teller() const; int noemer() const; void plus(Breuk b); void abs(); private: int boven; int onder; void normaliseer(); };
•Data members •Memberfuncties: •Constructors •Vraag-functies. •Doe-functies.
int main() { Breuk a, b(-2), c(21, -9); // ...
35
Class Breuk (versie 1) int main() { Breuk a, b(-2), c(21, cout << a.teller() << cout << b.teller() << cout << c.teller() << c.abs(); cout << c.teller() << cin.get(); return 0; }
-9); "/" << a.noemer() << endl; "/" << b.noemer() << endl; "/" << c.noemer() << endl; "/" << c.noemer() << endl;
36
Implementatie constructors Breuk::Breuk(): boven(0), onder(1) { } Initialization list Breuk::Breuk(int t): boven(t), onder(1) { } Breuk::Breuk(int t, int n): boven(t), onder(n) { normaliseer(); } int main() { Breuk h("half"); return 0; }
Werkt dit? Wat moet je doen om dit wel te laten werken? 37
Constructor Compiler roept de constructor automatisch aan. Reserveer geheugen op de stack voor c en roep constructor c.Breuk(21, -9) aan
{ Breuk c(21, -9); // ... // ... // ... }
Roep destructor c.~Breuk() aan en geef geheugen van c weer vrij 38
Constructor en type conversie Compiler roept de constructor automatisch aan als dat
nodig is voor type conversie. De C++ compiler denkt met je mee! Als je dat niet wilt moet je explicit voor de constructor zetten.
c.plus(Breuk(5));
{ Breuk c(21, -9); // ... c.plus(5); // ... }
Reserveer geheugen op de stack en roep constructor Breuk(5) aan
Roep destructor aan en geef geheugen van Breuk(5) weer vrij
39
Class Breuk class Breuk { public: void leesin(); void drukaf() const; int teller() const; int noemer() const; void plus(Breuk b); void abs(); private: int boven; int onder; void normaliseer(); };
int main() { Breuk a, b; a.drukaf(); a.leesin(); b = a; b.drukaf(); Breuk c(a); c.drukaf(); return 0; }
Werkt dit?
40
Gratis bij elke class! constructor zonder argument (default constructor).
Deze constructor roept de default constructor aan van alle data members. assignment operator (operator=). Deze assignment operator roept de assignment operator aan van alle data members. copy constructor. Deze constructor roept de copy constructor aan van alle data members. destructor. Deze destructor roept de destructor aan van alle data members. Je kunt al deze functies ook zelf definiëren!
41
const Breuk / const memberfuncties Je kunt een constante Breuk definiëren. const Breuk kwart(1, 4); cout << kwart.teller() << '/' << kwart.noemer() << endl; // mag dit ? kwart.plus(5); // mag dit ? Hoe weet de compiler dat?
42
Class Breuk (versie 1) class Breuk { public: Breuk(); Breuk(int t); Breuk(int t, int n); int teller() const; int noemer() const; void plus(Breuk b); void abs(); private: int boven; int onder; void normaliseer(); };
•Vraag-functies: •Return type •Geen argumenten •const •Doe-functies: •Geen return type (void) •Meestal argumenten
Breuk b(3, 4); const Breuk kwart(1, 4); cout << b.teller() << endl; b.plus(kwart); cout << kwart.teller() << endl; kwart.plus(b); 43
Huiswerk! Bestudeer dictaat 2.3 t/m 2.10. class Rechthoek { public: // ... (geen constructors) private: Breuk lengte; Breuk breedte; };
int main() { Rechthoek r1; // ... Rechthoek r2(r1); // ... }
Compileert dit programma bij gebruik van versie 1 van
Breuk? Wat is de lengte en breedte van r1 en r2? 44
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 5
Class Breuk Breuk a, b; a.leesin(); b.leesin(); a.plus(b); a.drukaf();
Stel hergebruik Breuk component is succes! Helpfiles FAQ Kan het beter? Gebruik Breuk is vergelijkbaar met int! Breuk a, b; cin >> a >> b; a += b; cout << a;
Veel werk! Moeite waard?
46
Operator overloading a.operator+=(b);
a += b;
De C++ compiler denkt met je mee! 47
Breuk in C++ class Breuk { public: void leesin(); void drukaf() const; void operator+=(Breuk b); private: int boven; int onder; Operator overloading is simpel! ? void normaliseer(); }; void Breuk::operator+=(Breuk b) { boven = boven * b.onder + onder * b.boven; onder *= b.onder; normaliseer(); } 48
Breuk is een succes? Mag
a += b += c; met int? Dit is een constructie die je allicht niet wilt gebruiken, maar die wel mogelijk is! Wat betekent dat voor onze Breuk klasse? binary ’+=’ : no operator found which takes a
right-hand operand of type ’void’
Oplossing? b += c; a += b; Kan dat niet beter?
49
Fix Breuk::operator+= class Breuk { public: void leesin(); void drukaf() const; Breuk operator+=(Breuk b); private: int boven; int onder; void normaliseer(); };
Pas op! Deze code is niet correct. Zie volgende sheet.
Breuk Breuk::operator+=(Breuk b) { boven = boven * b.onder + onder * b.boven; onder *= b.onder; normaliseer(); return *this; ?????; }
50
Breuk is geen succes! a += b += c;
werkt nu. Maarrr… Werkt dit ook: (a += b) += c; ? Oplossing? reference
51
Reference In C++ zijn er 3 “soorten” variabelen: “Gewone” variabelen. Pointers. References. Een reference is een andere naam voor een variabele die al bestaat. int i; int& j = i; //initialisatie is verplicht!
Is dit goed voor de onderhoudbaarheid?
i = 3; cout << j << endl; // een reference is een "pseudoniem"
52
Gebruik reference Je kunt een reference gebruiken als: Globale variabele. Lokale variabele. Parameter. Return type.
53
Call by value void swap(int p, int q) { int t = p; p = q; q = t; } //
//
... int i = 3; int j = 4; swap(i, j); ...
Deze code werkt niet goed! Weet je nog waarom? Oplossing?
54
Call by reference in C void swap(int* p, int* q) { int t = *p; *p = *q; *q = t; } //
//
p wijst naar i q wijst naar j
... int i = 3; int j = 4; swap(&i, &j); ...
55
Call by reference in C++ void swap(int& p, int& q) { int t = p; p = q; q = t; } //
//
... int i = 3; int j = 4; swap(i, j); ...
p is andere naam voor i q is andere naam voor j
Onder de “motorkap” wordt een reference geïmplementeerd met een pointer. 56
Reference return Je kunt een reference ook teruggeven vanuit een
functie. int& max(int& a, int& b) { if (a > b) return a; else return b; }
int main() { int x = 2, y = 7, z; max(x, y) = 0; z = max(x, y); // ... }
Een functie die een reference teruggeeft kan ook links van een = teken gebruikt worden (is een lvalue). 57
Fix Breuk::operator+= class Breuk { public: void leesin(); void drukaf() const; Breuk& operator+=(Breuk b); private: int boven; Met behulp van een reference int onder; kunnen we ook onnodige void normaliseer(); kopietjes voorkomen. }; Breuk& Breuk::operator+=(Breuk b) { boven = boven * b.onder + onder * b.boven; onder *= b.onder; normaliseer(); return *this; }
58
Fix Breuk::operator+= class Breuk { public: Waarom const? void leesin(); void drukaf() const; Breuk& operator+=(const Breuk& b); private: int boven; int onder; void normaliseer(); }; Breuk& Breuk::operator+=(const Breuk& b) { boven = boven * b.onder + onder * b.boven; onder *= b.onder; normaliseer(); return *this; }
59
Huiswerk! Bestudeer dictaat 2.11 t/m 2.20. Een reference lijkt op een pointer. Wat zijn de verschillen? Noem 3 situaties waar een copy constructor nodig is.
Waarom zijn de parameters van max (zie boven) geen int of
const int& ?
60
Objectgeoriënteerd Programmeren in C++
OGOPRG Les 6
Operator+ overloading { Breuk a, b, c;
Gratis!
Zelf maken
c.operator=(a.operator+(b)); c = a + b; }
Wat moet operator+ eigenlijk doen? Wat moet het parametertype zijn? Breuk const Breuk&
Goed Beter! Voorkomt onnodig kopietje.
Wat moet het returntype zijn? Breuk Breuk& const Breuk
Goed Fout! Je hebt geen variabele die al bestaat. Beter! Geeft een fout bij: a + b = c;
62
Breuk::operator+ class Breuk { public: // ... Breuk& operator+=(const Breuk& rechts); const Breuk operator+(const Breuk& rechts) const; private: int boven; int onder; Waarom const? void normaliseer(); }; const Breuk Breuk::operator+(const Breuk& rechts) const { Breuk hulpje(*this); hulpje += rechts; return hulpje; } 63
Breuk::operator+ De volgende implementaties zijn niet juist! const Breuk Breuk::operator+(const Breuk& rechts) { Breuk hulpje(*this); // Hint: hulpje += rechts; vergeten! const Breuk const kwart(1, 4); return hulpje; Breuk b(2, 3), c; } c = kwart + b; const Breuk& Breuk::operator+(const Breuk& rechts) const { Breuk hulpje(*this); hulpje += rechts; Return andere naam voor return hulpje; een “dode” variabele! } const Breuk& Breuk::operator+(const Breuk& rechts) const { *this += rechts; return *this; Receiver mag niet } 64
veranderen!
Breuk::operator+ probleem! {
Breuk(5) Breuk a, b;
b.operator=(a.operator+(5)); b = a + 5; b.operator=(5.operator+(a)); b = 5 + a; } Microsoft error: binary '+' : no global operator found which takes type 'Breuk'. GNU gcc error: No match for 'operator+' in '5 + a'.
Oplossing? 65
Globale operator+ overloading Je kunt de globale operator+ overloaden. class Breuk { public: // ... Breuk& operator+=(const Breuk& rechts); const Breuk operator+(const Breuk& rechts) const; private: // ... }; const Breuk operator+(int links, const Breuk& rechts); const Breuk operator+(int links, const Breuk& rechts) { Breuk hulpje(links); hulpje += rechts; return hulpje; }
66
Fix: Breuk::operator+ {
Breuk(5) Breuk a, b;
b.operator=(a.operator+(5)); b = a + 5; b.operator=(operator+(5, a)); b = 5 + a; }
Kan dit niet eenvoudiger? 67
Globale operator+ overloading Alternatief: class Breuk { public: // ... Breuk& operator+=(const Breuk& rechts); private: // ... }; const Breuk operator+(const Breuk& links, const Breuk& rechts); const Breuk operator+(const Breuk& links, const Breuk& rechts) { Breuk hulpje(links); hulpje += rechts; return hulpje; } 68
Fix: Breuk::operator+ {
Breuk(5) Breuk a, b;
b.operator=(operator+(a, 5)); Breuk(5) b = a + 5; b.operator=(operator+(5, a)); b = 5 + a; }
69
Operator== overloading { Breuk a, b;
if (operator==(a, b))
if (a == b) /* ... */; }
Wat moet operator== eigenlijk doen? Waarom is de globale operator== gebruikt en niet
Breuk::operator== ? Wat moeten de parametertypes zijn? Breuk const Breuk&
Goed Beter! Voorkomt onnodig kopietje.
Wat moet het returntype zijn? bool
Goed
70
Operator== overloading Je kunt de globale operator== overloaden. class Breuk { public: // ... int teller() const; int noemer() const; private: // ... }; bool operator==(const Breuk& l, const Breuk& r);
bool operator==(const Breuk& l, const Breuk& r) { return l.teller() == r.teller() && l.noemer() == r.noemer(); } 71
Breuk::operator== Waarom is de volgende implementatie niet juist! class Breuk { public: // ... private: int boven; int onder; // ... };
Error: boven en onder zijn private!
bool operator==(const Breuk& l, const Breuk& r); bool operator==(const Breuk& l, const Breuk& r) { return l.boven == r.boven && l.onder == r.onder; }
Wat te doen als er geen teller() en noemer() memberfuncties zijn ?
72
Breuk::operator== Friend! class Breuk { public: // ... private: int boven; int onder; // ... friend bool operator==(const Breuk& l, const Breuk& r); }; bool operator==(const Breuk& l, const Breuk& r) { return l.boven == r.boven && l.onder == r.onder; } 73
Friend Vriendschap in C++ gaat wel erg ver ...
A friend is someone who may touch your private parts. Geldt bij het gebruik van friend nog steeds het principe
van information hiding? 74
Huiswerk! Bestudeer dictaat 2.21 t/m 2.27.
Let op! 2.24 en 2.25 zijn niet behandeld (mag je overslaan). Maak een operator*= voor Breuk. Maak een operator* voor Breuk. Maak een operator!= voor Breuk. Denk aan OO Maak een operator< voor Breuk. “afschuiven”! Maak een operator<= voor Breuk. Maak een operator> voor Breuk. Maak een operator>= voor Breuk.
75
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 7
Operator<< overloading We willen breuken op dezelfde manier afdrukken als
integers.
ostream aanpassen?
{ Breuk b(12, -9);
Bestaat al!
Zelf maken
Bestaat al!
cout.operator<<("b = ").operator<<(b). operator<<(endl);
// }
cout << "b = " << b << endl; ...
Object van de class ostream
77
Operator<< overloading We willen breuken op dezelfde manier afdrukken als
integers. { Breuk b(12, -9);
Zelf maken
Globale operator<< overloaden Bestaat al!
Bestaat al!
operator<<(cout.operator<<("b = "), b).operator<<(endl);
// }
cout << "b = " << b << endl; ...
Object van de class ostream
78
Operator<< overloading { Breuk a;
operator<<(cout, a). operator<<(endl);
cout << a << endl;
}
Wat moet het eerste parametertype zijn? ostream ostream&
De uitvoer moet niet naar een kopietje van het beeldschermgeheugen. Goed
Wat moet het tweede parametertype zijn? Breuk const Breuk&
Goed Beter! Voorkomt onnodig kopietje.
Wat moet het returntype zijn? ostream ostream&
De endl moet niet naar een kopietje van het beeldschermgeheugen. Goed
79
Operator<< class Breuk { public: // ... private: int boven; int onder; // ... friend ostream& operator<<(ostream& left, const Breuk& right); }; ostream& operator<<(ostream& left, const Breuk& right) { left << right.boven << '/' << right.onder; return left; } 80
Gemak van overerving Er zijn verschillende classes afgeleid van ostream. ostringstream Om te schrijven naar een string variabele. ofstream Om te schrijven naar een file. De overloaded operator<< kan ook met objecten van
deze afgeleide classes worden gebruikt. ostream ostringstream
ofstream 81
Gemak van overerving class Breuk { // ... friend ostream& operator<<(ostream& left, const Breuk& right); }; // ...
Gaat goed! Want int main() { ofstream “is een” Breuk b(12,-9); ofstream out("uitvoer.txt"); ostream. if (out) out << "b = " << b << endl; else cerr << "File uitvoer.txt kan niet geopend worden!" << endl; cin.get(); return 0; 82 }
Operator>> overloading We willen breuken op dezelfde manier inlezen als
integers.
istream aanpassen?
{ int i, j; Breuk b;
Bestaat al!
Zelf maken
Bestaat al!
cin.operator>>(i).operator>>(b). operator>>(j);
// }
cin >> i >> b >> j; ...
Object van de class istream
83
Operator>> overloading We willen breuken op dezelfde manier inlezen als
integers.
Globale operator>> overloaden
{ int i, j; Breuk b;
Zelf maken
Bestaat al!
Bestaat al!
operator>>(cin.operator>>(i), b). operator>>(j);
// }
cin >> i >> b >> j; ...
Object van de class istream
84
Operator>> overloading { Breuk a; int i;
operator>>(cin, a). operator>>(i);
cin >> a >> i;
}
Wat moet het eerste parametertype zijn? istream istream&
De invoer moet niet worden gelezen uit een kopietje van het toetsenbordbuffer. Goed
Wat moet het tweede parametertype zijn? Breuk Breuk&
De invoer moet niet naar een kopietje van a. Goed
Wat moet het returntype zijn? istream istream&
i moet niet worden gelezen uit een kopietje van het toetsenbordbuffer. Goed
85
Operator>> istream& operator>>(istream& left, Breuk& right) { int teller; if (left >> teller) if (left.peek() == '/') { left.get(); int noemer; if (left >> noemer) right = Breuk(teller, noemer); else right = Breuk(teller); } else right = Breuk(teller); Deze operator>> hoeft else geen friend te zijn van de right = Breuk(); class Breuk return left; } 86
Huiswerk! Bestudeer dictaat 2.28.
87
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 8
Seperate compilation Memcell.h
#ifndef _Memcell_ #define _Memcell_ class MemoryCell { public: int Read() const; void Write(int x); private: int StoredValue; }; #endif
Memcell.cpp
#include "Memcell.h" int MemoryCell::Read() const { return StoredValue; } void MemoryCell::Write(int x) { StoredValue = x; }
Memappl.cpp #include using namespace std; #include "Memcell.h" int main() { MemoryCell M; M.Write(5); 89 cout << "Cell contents are " << M.Read() << endl;
Seperate compilation pre-compiler compiler
Memcell.h class MemoryCell { // ... };
Memcell.cpp
linker Memcell.obj
#include "Memcell.h" // ... int MemoryCell::Read() {
Memappl.cpp
Memcell.exe
Memappl.obj
#include "Memcell.h" // ... MemoryCell M; 90
Project Microsoft Visual C++ 2013
91
Hergebruik void swap(int& p, int& q) { int t = p; p = q; q = t; } //
//
... int i = 3; int j = 4; swap(i, j); ...
Copy – Paste is slecht voor de onderhoudbaarheid!
Wat te doen als we twee variabelen van het type
double willen verwisselen? Wat te doen als we twee objecten van de class Breuk willen verwisselen?
92
Generieke functie Gebruik een template functie. template void swap(T& p, T& q) { T t = p; Een template is een “mal” p = q; waarmee verschillende functies q = t; “gemaakt” kunnen worden } // ... void swap(int& p, int& q) { int t = p; int i = 3; p = q; int j = 4; q = t; swap(i, j); } // ... Breuk b(1, 2); void swap(Breuk& p, Breuk& q) { Breuk c(3, 4); Breuk t = p; swap(b, c); p = q; q = t;
}
93
Class Dozijn In de class Dozijn kun je 12 integers opslaan. class Dozijn { public: void zetIn(int index, int waarde); int leesUit(int index) const; private: int data[12]; }; //
...
//
Dozijn d; d.zetIn(3, 13); ... cout << "De plaats nummer 3 in d bevat de waarde: " << d.leesUit(3) << endl;
94
Class Dozijn void Dozijn::zetIn(int index, int waarde) { if (index >= 0 && index < 12) data[index] = waarde; } int Dozijn::leesUit(int index) const { if (index >= 0 && index < 12) return data[index]; return 0; /* ik weet niets beters */ }
ostream& operator<<(ostream& o, const Dozijn& d) { o << d.leesUit(0); for (int i = 1; i < 12; ++i) o << ", " << d.leesUit(i); return o; }
95
Class Dozijn int main() { Dozijn d1; for (int j = 0; j < 12; ++j) d1.zetIn(j, j * j); // vul d1 met kwadraten cout << "d1 = " << d1 << endl; cin.get(); Copy – Paste is slecht voor de return 0; onderhoudbaarheid! } d1 = 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121
Wat te doen als we 12 variabelen van het type double
willen opslaan? Wat te doen als we 12 objecten van de class Breuk willen opslaan? 96
Generieke class Gebruik een template class. template class Dozijn { public: void zetIn(int index, const T& waarde); const T& leesUit(int index) const; private: T data[12]; Een template is een “mal” }; waarmee verschillende classes // ... “gemaakt” kunnen worden Dozijn di; class Dozijn { public: void zetIn(int index, const int& waarde); const int& leesUit(int index) const; private: int data[12]; };
97
Generieke class Dozijn template void Dozijn::zetIn(int index, const T& waarde) { if (index >= 0 && index < 12) data[index] = waarde; } template const T& Dozijn::leesUit(int index) const { if (index < 0) index = 0; if (index > 11) index = 11; return data[index]; } template ostream& operator<<(ostream& o, const Dozijn& d) { o << d.leesUit(0); for (int i = 1; i < 12; ++i) o << ", " << d.leesUit(i); return o; }
98
Generieke class Dozijn int main() { Dozijn d1; for (int j = 0; j < 12; ++j) d1.zetIn(j, j * j); // vul d1 met kwadraten cout << "d1 = " << d1 << endl;
Dozijn<string> d2; d2.zetIn(0, "Drenthe"); d2.zetIn(1, "Flevoland"); // ... d2.zetIn(10, "Zeeland"); d2.zetIn(11, "Zuid-Holland"); cout << "d2 = " << d2 << endl; cin.get(); return 0; }
d1 = 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 d2 = Drenthe, Flevoland, Friesland, Gelderland, Groningen, Limburg, Noord-Brabant, Noord-Holland, Overijssel, Utrecht, Zeeland, Zuid-Holland
99
Wat doen we als we meer/minder dan 12 elementen willen opslaan?
Generieke class Gebruik een tweede template parameter. template class Rij { public: void zetIn(int index, const T& waarde); const T& leesUit(int index) const; int aantalPlaatsen() const; private: Een template is een “mal” T data[N]; waarmee verschillende classes }; // ... “gemaakt” kunnen worden Rij alfabet; class Rij { public: void zetIn(int index, const char& waarde); const char& leesUit(int index) const; int aantalPlaatsen() const; private: 100 char data[26]; };
Generieke class Rij template void Rij::zetIn(int index, const T& waarde) { if (index >= 0 && index < N) data[index] = waarde; } template const T& Rij::leesUit(int index) const { if (index < 0) index = 0; if (index > N - 1) index = N - 1; return data[index]; } template int Rij::aantalPlaatsen() const { return N; } template ostream& operator<<(ostream& o, const Rij& r) { o << r.leesUit(0); for (int i = 1; i < N; ++i) o << ", " << r.leesUit(i); return o; 101 }
Generieke class Rij int main() { Rij kwad; for (int i = 0; i < kwad.aantalPlaatsen(); ++i) kwad.zetIn(i, i * i); cout << "kwad = " << kwad << endl; Rij alfabet; for (int i = 0; i < alfabet.aantalPlaatsen(); ++i) alfabet.zetIn(i, 'A' + i); cout << "alfabet = " << alfabet << endl; cout << "de derde letter van alfabet is " << alfabet.leesuit(2) << endl; cout << "de honderste letter van alfabet is " << alfabet.leesuit(99) << endl; cin.get(); return 0; }
kwad = 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 alfabet = A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z de derde letter van alfabet is C 102 de honderste letter van alfabet is Z
Template class std::array C++11
std::array in C++ vervangt de C array. Het aantal elementen N ligt vast na het compileren. Elementen kunnen worden opgevraagd met operator[]. Je kunt een std::array “gewoon” vergelijken, toekennen en kopiëren. Je kunt een std::array element voor element doorlopen met een range-based for. std::array heeft memberfuncties: size() geeft het aantal elementen (type: std::array::size_type). at(n) geeft reference naar element n. Geeft een fout (exception) als element n niet bestaat. 103
Template class std::array C++11 #include #include <array> using namespace std; int main() { // definieer array van 15 integers array a; // vul met kwadraten for (array::size_type i = 0; i < a.size(); ++i) { a[i] = i * i; } // druk af for (array::size_type i = 0; i < a.size(); ++i) { cout << a[i] << " "; } cout << endl; // ... 104
Template class std::array C++11 #include #include <array> using namespace std; int main() { // definieer array van 15 integers array a; // vul met kwadraten int i = 0; for (int& e: a) { e = i * i; ++i; } // druk af for (int e: a) { cout << e << " "; } cout << endl; // ...
106
Template class std::vector
std::vector in C++ is een dynamische array. De std::vector kan groeien en krimpen. Elementen kunnen worden opgevraagd met operator[]. Je kunt een std::vector “gewoon” vergelijken, toekennen en kopiëren. Je kunt een std::vector element voor element doorlopen met een range-based for. std::vector heeft memberfuncties: size() geeft het aantal elementen (type: std::vector::size_type). at(n) geeft reference naar element n. Geeft een fout (exception) als element n niet bestaat. push_back(e) voeg element e aan de vector toe. 107
Template class std::vector #include #include using namespace std; int main() { // definieer vector van integers vector v; // vul met kwadraten for (int i = 0; i < 15; ++i) { v.push_back(i * i); } // druk af for (vector::size_type i = 0; i < v.size(); ++i) { cout << v[i] << " "; } cout << endl; // ... 108
Template class std::vector C++11 #include #include using namespace std; int main() { // definieer vector van integers vector v; // vul met kwadraten for (int i = 0; i < 15; ++i) { v.push_back(i * i); } // druk af for (int e: v) { cout << e << " "; } cout << endl; // ...
110
Huiswerk! Bestudeer dictaat 2.29. Bestudeer dictaat (heel) hoofdstuk 3.
111
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 9
Hergebruik van classes Aggregatie (Aggregation) ... heeft een (of meer) ... Overerving (Inheritance) ... is een (speciaal soort) ...
113
Aggregatie Aggregatie (Aggregation) ... heeft een (of meer) ... class Rechthoek { public: // ... private: Breuk lengte; Breuk breedte; };
Een Rechthoek heeft een lengte en een breedte van het type Breuk.
114
Overerving Overerving (Inheritance) ... is een (speciaal soort) ... class Winkelier { // ... }; class Bloemiste: public Winkelier { // ... };
Een Bloemiste is een (speciaal soort) Winkelier. 115
ADT aanpak enum Soort {sintBernard, tekkel};
class Hond { private: Niet goed Soort s; uitbreidbaar! // ... public: Krant haalKrant(); void blaf(); void Hond::blaf() { // ... switch (s) { }; case sintBernard: cout << "WOEF WOEF"; Krant Hond::haalKrant() { break; // ... case tekkel: blaf(); cout << "kef kef"; return krant; break; } } 116 }
OO aanpak class Hond { Een Tekkel is een Hond. private: Een SintBernard is een Hond. // ... public: Krant haalKrant(); virtual void blaf(); // ... }; Message kan overridden worden. Krant Hond::haalKrant() { // ... blaf(); return krant; } void Hond::blaf() { cout << "blaf blaf"; }
117
OO aanpak class SintBernard: public Hond { private: Whisky vat; public: virtual void blaf(); }; void SintBernard::blaf() { cout << "WOEF WOEF"; } class Tekkel: public Hond { public: virtual void blaf(); }; void Tekkel::blaf() { cout << "kef kef"; }
118
Polymorfisme void doeJeWerk(Hond& h) { Krant k = h.haalKrant(); // ... h is een polymorfe parameter. } int main() {doeJeWerk is een polymorfe functie. SintBernard Boris; Tekkel Harry; Harry is van de class if (!weekend) doeJeWerk(Harry); else if (zaterdag) doeJeWerk(Boris); cin.get(); return 0; }
Tekkel maar een Tekkel is een Hond. Boris is van de class SintBernard maar een SintBernard is een Hond. 119
Uitbreidbaarheid Door de OO aanpak kan heel eenvoudig een nieuwe
soort hond worden toegevoegd. Voeg zelf de classes DuitseHerder en MechelseHerder toe. Welke code moet nu gewijzigd worden? Welke code moet nu opnieuw gecompileerd worden?
120
Huiswerk! Bestudeer dictaat hoofdstuk 4 t/m 4.2. Voeg zelf zelf de classes DuitseHerder en MechelseHerder
toe. Gegeven is dat beide rassen Herdershonden zijn die een schapenkudde kunnen drijven en hoeden. Elk ras heeft een eigen blaf. Beantwoord de vragen op de vorige sheet.
121
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 10
Uitwerking Huiswerk Voeg
DuitseHerder en MechelseHerder toe. Welke code moet gewijzigd worden? Welke code moet gecompileerd worden?
123
Tiroler Herder
© Toon van Driel
124
Tiroler Herder
© Toon van Driel 125
Inline memberfuncties class Hond { public: virtual void blaf() { cout << "blaf blaf"; } }; class SintBernard: public Hond { public: virtual void blaf() { cout << "WOEF"; } private: Whisky vat; };
Definitie van de
memberfuncties staat in de class declaratie. Nadeel: Component kan niet zonder
source geleverd worden.
Voordeel: Minder typen.
126
Boodschap sturen via pointer class Hond { public: virtual void blaf() { cout << "blaf blaf" << endl; } }; int main() { Hond fikkie; fikkie.blaf(); Hond* phond(&fikkie); *phond.blaf(); (*phond).blaf(); phond->blaf();
Error: left of '.blaf' must have class/struct Ok Handige afkorting
127
Slicing problem polymorfisme werkt alleen bij * en &. class Hond { public: virtual void blaf() { cout << "blaf" << endl; } Waarom geen: };
SintBernard& SintBernard*
?
class SintBernard: public Hond { public: virtual void blaf() { cout << "WOEF" <<endl; } private: Niet Whisky vat; Polymorf ! };
Waar blijft de whisky? int main() { SintBernard boris; Hond& rhond = boris; rhond.blaf(); Hond* phond = &boris; phond->blaf(); Hond hond = boris; 128 hond.blaf();
Abstract Base Class class Hond { private: Puur virtueel // ... public: Krant haalKrant(); virtual void blaf() = 0; // ... }; Krant Hond::haalKrant() { // ... blaf(); return krant; }
Er kunnen geen objecten
(variabelen) van een ABC gedefinieerd worden. Een class die overerft van Hond en blaf() override is geen ABC meer. Een class die overerft van Hond en blaf() niet override is een ABC.
Oplossing voor het Slicing Problem 129
Slicing problem polymorfisme werkt alleen bij * en &. Hond is een class Hond { ABC public: virtual void blaf() = 0; }; class SintBernard: public Hond { public: virtual void blaf() { cout << "WOEF" << endl; } Error: 'Hond' : private: cannot instantiate Whisky vat; abstract class }; due to following members:
'void Hond::blaf(void)' : is abstract
int main() { SintBernard boris; Hond& rhond = boris; rhond.blaf(); Hond* phond = &boris; phond->blaf(); Hond hond = boris; 130
Slicing Problem Maak alle base classes abstract. Compiler voorkomt slicing! Kan niet altijd: Hergebruik class van een andere programmeur (evt. zonder source code). Je moet nu zelf slicing voorkomen!
131
Huiswerk! Bestudeer dictaat paragraaf 4.3 t/m 4.5 en 4.11. 4.3: Memberfunctie overriding.
Vanuit de in de derived class overridden memberfunctie kun je de originele functie in de base class aanroepen. 4.4: Abstract base class. 4.5: Constructors bij inheritance. Vanuit de constructor van de derived wordt automatisch de constructor van de base class aangeroepen. Je kunt ook zelf vanuit de constructor van de derived class een constructor in de base class aanroepen.
Nodig bij practicumopdracht 3a 4.12: Slicing problem.
132
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 11
Overloading van memberfuncties class Class { public: void f() const { cout << "Ik ben f()" << endl; } void f(int i) const { // overload f() cout << "Ik ben f(int)" << endl; } }; int main() { Class object; object.f(); object.f(3); // ...
Uitvoer: Ik ben f() Ik ben f(int) 134
Overloading en overerving class Base { public: void f() const { Error: cout << "Ik ben f()" << endl; 'Derived::f' : } }; function does not
Hiding-rule
take 0 arguments
class Derived: public Base { public: void f(int i) const { // Verberg f() cout << "Ik ben f(int)" << endl; } };
Conclusie: Overloading en overerving gaan niet goed samen!
Uitvoer: Ik ben f() Ik ben f(int) Ik ben f()
int main() { Base b; Derived d; b.f(); d.f(3); d.f(); d.Base::f(); 135 }
Reden voor de hiding-rule // Code van Bas class Base { public: // geen f(...) }; // Code van Dewi class Derived: public Base { public: void f(double d) const { cout << "Ik ben f(double)" << endl; } }; int main() { Derived d; d.f(3); // ...
Uitvoer: Ik ben f(double)
136
Reden voor de hiding-rule // Aangepaste code van Bas class Base { public: // ... void f(int i) const { cout << "Ik ben f(int)" << endl; } }; // Code van Dewi niet gewijzigd
Conclusie: De hiding-rule verhoogt de onderhoudbaarheid!
int main() { Derived d; d.f(3); // Base::f(int) is hidden. Gelukkig maar! // ... Uitvoer: Ik ben f(double)
137
Explicit overriding C++11 Sinds C++11 kun je expliciet aangeven dat je een
memberfunctie wilt overridden. Dit voorkomt dat je een memberfunctie “per ongeluk” overload. Dit doe je door het woord override achter de memberfunctie te plaatsen. De compiler geeft nu een foutmelding als er geen overriding wordt gebruikt.
138
Explicit overriding C++11 class Base { OOPS: virtual vergeten! public: void f(int i) const { cout << "Base::f(int) called." << endl; } virtual void g(int i) const { cout << "Base::g(int) called." << endl; } Error: 'Derived::f' : method with }; override specifier 'override' did not class Derived: public Base { override any base class methods public: Error: 'Derived::g' : method with void f(int i) const override { OOPS: const vergeten! override specifier 'override' did not cout << "Derived::f(int) called." << endl; override any base class methods } virtual void g(int i) override { cout << "Derived::g(int) called." << endl; } };
139
Final overriding C++11 Je hebt blaf() in Herdershond overridden maar je wilt niet dat blaf() in classes die afgeleid zijn van Herdershond opnieuw overridden wordt. 140
Final overriding C++11 class Herdershond: public Hond { public: virtual void blaf() final; // ... }; class DuitseHerder: public Herdershond { public: virtual void blaf(); // ... };
Error: 'Herdershond::blaf()': declared as 'final' cannot be overridden by 'DuitseHerder::blaf' 141
Afscherming private: alleen bereikbaar in member functies van de class zelf protected: alleen bereikbaar in member functies van de class en in member functies van "nakomelingen" van de class public: altijd bereikbaar via object
Zie dictaat paragraaf 4.6.
142
Huiswerk! Bestudeer dictaat paragraaf 4.6 t/m 4.10, 4.12 en 4.13. 4.6: protected members. 4.7: Voorbeeld: ADC kaarten. 4.8: Overloading en overriding van memberfuncties. 4.9: Expliciet overridden van memberfuncties. 4.10: Final overridden van memberfuncties. 4.12 Voorbeeld: Opslaan van polymorfe objecten in een vector.
Nodig bij practicumopdracht 3c 4.13 Voorbeeld: Impedantie calculator.
144
Huiswerk! Maak het proeftentamen. Zie blackboard Of http://bd.eduweb.hhs.nl/ogoprg/pdf/proeftentamen_1.pdf
145
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 12
Huiswerk! Bestudeer boek:
Warmer & Kleppe, Praktisch UML, 5de editie. H1 en H2 H4 t/m 4.3
147
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 13
Software Engineering Fases Wat wil de klant? Analyse (find the requirements). Hoe kun je het maken? Design (structured or OO). Waarmee kun je het maken? Implement (write the code).
Werkt het? Test (verify and validate). Verification: Have we built the system right? Does the system satisfy its specification?
Validation: have we built the right system? Does the specification satisfy the customer's expectation?
149
Software Engineering Waterval methode Traditionele methode. Geschikt voor projecten met weinig risico. Past goed bij gestructureerde aanpak. Spiraal methode (Evolutionaire methode.) Moderne methode. Geschikt voor projecten met veel risico (onzekerheid). Past goed bij OO aanpak.
150
Waterval = sequentieel
Analyse Design Implement Test
151
Tijdsdruk... Versnellen door in elkaar schuiven van waterval model geeft problemen!
152
Spiraal = cyclisch Test
Analyse
Typische duur van één cyclus: 3 weken!
Implement
Design 153
Modelleren
Werkelijkheid
model 154
Wat modelleren we? Analyse Maak een model van de te automatiseren werkelijkheid of van het op te lossen probleem. Domeinkennis structuur. Functionele eisen gedrag.
Ontwerp Maak een model van de oplossing. Implementatie Maak een model van het programma. Bij OOA+OOD+OOP werken we steeds aan hetzelfde model (seamless development)
155
Unified Modeling Language UML inleiding. Hoofdstuk 1 en 2. UML Klasse- en objectdiagram (1). Paragraaf 4.1 t/m 4.3, 4.4.2, 4.4.8, 4.4.9 en 4.4.17. UML Use-case-diagram. Paragraaf 8.1 t/m 8.6. UML Sequentie- en communicatiediagram. Paragraaf 10.1 t/m 10.4. UML Toestands- en Activiteitsdiagram. Paragraaf 12.1 t/m 12.3 en H15.1 t/m 15.3. UML Klassediagram (2) Paragraaf 4.4.4, 4.4.10 en 4.4.12.
156
Visual Paradigm for UML Tekenen van UML diagrammen. Omzetten UML naar C++ (of Java, C#, Python enz). Omzetten C++ (of Java, C#, Python) naar UML. Beschikbaar in lokaal D1.049 (en ook thuis).
157
Visual Paradigm for UML SE
158
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 14
Klassendiagram
Attributes = data members Operations = member functions = messages Tijdens het ontwikkelen van het model voeg je steeds meer details toe. Deze details kun je ook weer verbergen, zie: Rechtermuisknop -> Presentation Options -> Operation/Attribute Display Options.
160
Objectdiagram
Object is een instantie van een Class Attributes = data members In Visual Paradigm for UML kunnen attributes met waarden “verborgen” worden. 161
Fases
Analysis
Design
Tijdens het ontwikkelen van het model voeg je steeds meer details toe. Deze details kun je ook weer verbergen, zie: Rechtermuisknop -> Presentation Options -> Operation/Attribute Display Options.
162
Code generatie Van je model kan C++ code worden gegenereerd:
Andersom is ook mogelijk: Van C++ code een model
genereren.
163
Implementation = Code generatie Rekening.h #ifndef __Rekening_h__ #define __Rekening_h__ class Rekening { public: bool open(); void stort(double bedrag); bool neemOp(double bedrag); private: double saldo; }; #endif
Code moet nog worden Rekening.cpp ingevuld!
#include "Rekening.h" bool Rekening::open() { } void Rekening::stort(double bedrag) { } bool Rekening::neemOp(double bedrag) { }
164
Implementation = Code generatie
165
Associatie
Labels: Altijd invullen! Multipliciteit: Officiëel altijd in te vullen, maar in praktijk wordt deze weggelaten (aangenomen multipliciteit: 1) Rol: eventueel invullen. Leesrichting: gebruiken om “onnatuurlijke” 166 leesrichting aan te geven.
Overerving (... is een ... ) Directeur.h #ifndef __Directeur_h__ #define __Directeur_h__ #include "Persoon.h" class Directeur: public Persoon { }; #endif
167
Aggregatie (... heeft een ...) Fiets heeft: 2 Wielen 1 Frame
168
Associatie Overerving Aggregatie Compositie
Aggregatie “heeft een” relatie. vaag gedefinieerd
Beginnende UML modelleerders gebruiken vaak ten onrechte compositie! Een compositie is een speciaal soort aggregatie.
Compositie “bevat een” relatie. deel behoort maar bij 1 geheel levensduur deel <= levensduur geheel 169
Compositie (... bevat een ...) Welke aggregaties zijn
composities? Mens --- Nier
Mens --- Hersenen PC --- Hard Disk PC --- CPU
CPU --- Transistor Radio --- Transistor
Let op! Het goede antwoord is afhankelijk van de applicatie. 170
Compositie (... bevat een ...) #ifndef __Fiets_h__ #define __Fiets_h__ #include "Frame.h" class Fiets { public: Frame Unnamed_Frame_; }; #endif
171
Compositie (... bevat een ...)
172
Compositie (... bevat een ...) #ifndef __Fiets_h__ #define __Fiets_h__ #include "Frame.h" class Fiets { private: Frame hetFrame; }; #endif
173
Compositie (... bevat een ...) #ifndef __Frame_h__ #define __Frame_h__ #include "Fiets.h" class Frame { public: Fiets* Unnamed_Fiets_; }; #endif
174
Compositie (... bevat een ...)
175
Compositie (... bevat een ...) #ifndef __Fiets_h__ #define __Fiets_h__ #include "Frame.h"
class Fiets { private: Frame hetFrame; }; #endif
#ifndef __Frame_h__ #define __Frame_h__ class Frame { }; #endif
176
Commentaar
177
Huiswerk! Bestudeer boek:
Warmer & Kleppe, Praktisch UML, 5de editie. H1 en H2
H4 t/m 4.3, 4.4.2 en 4.4.8.
Bedenk hoe je een aggregatie
(geen compositie) kunt implementeren. Zie dictaat: paragraaf 4.13.
178
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 15
Voorbeeld Waarom? We willen van Hond graag een Abstract Base Class maken!
180
Pure virtual member function
181
Pure virtual member function
Pure virtual memberfunctie wordt in UML cursief weergegeven. 182
Abstract Base Class
183
Abstract Base Class
ABC wordt in UML cursief weergegeven. 184
Aggregatie implementatie
Hoe kunnen we een 0..1 aggregatie implementeren in
class SintBernard? WhiskeyVat data member? WhiskeyVat& data member? WhiskeyVat* data member?
Andere naam voor een WhiskeyVat Niet 0..1 maar 1. dat al bestaat. Onzin! 185
Aggregatie implementatie #ifndef __SintBernard_h__ #define __SintBernard_h__ class WhiskeyVat; class SintBernard { public: WhiskeyVat* heeft_om_zijn_nek; void blaf(); }; #endif 186
Aggregatie implementatie #ifndef __WhiskeyVat_h__ #define __WhiskeyVat_h__ class SintBernard; class WhiskeyVat { public: SintBernard* heeft_om_zijn_nek; }; #endif 187
Aggregatie implementatie
188
Aggregatie implementatie
189
Aggregatie implementatie #ifndef __SintBernard_h__ #define __SintBernard_h__ class WhiskeyVat; class SintBernard { public: void blaf(); private: WhiskeyVat* vat; }; #endif
190
Aggregatie implementatie
Hoe kunnen we een 0..5 aggregatie implementeren in
class Kennel? array met Hond* -ers array vector
Beter! Maar niet beschikbaar in Visual Paradigm
Goed! Wel beschikbaar in Visual Paradigm, ook bruikbaar voor 0..*
191
Aggregatie implementatie #include using namespace std; #ifndef __Kennel_h__ #define __Kennel_h__
Liever niet!
class Hond; class Kennel { private: std::vector inwoners; }; #endif 192
Opdracht Schrijf een testprogramma voor de hiervoor gemaakte
class Kennel. Zet een SintBernard genaamd boris in de Kennel
genaamd k. Zet een Tekkel genaamd harry in de Kennel k. Zet een SintBernard genaamd felix in de Kennel k. Laat alle honden in de Kennel k blaffen. Haal harry uit de Kennel k. Laat alle honden in de Kennel k blaffen. Bedenk eerst welke messages je naar een Kennel moet kunnen sturen. 193
Uitwerking #include #ifndef __Kennel_h__ #define __Kennel_h__ class Hond;
Waarom Hond& als parameter?
class Kennel { private: std::vector inwoners; public: void zetIn(Hond& h); void haalUit(Hond& h); void blafAllemaal(); }; #endif 194
Uitwerking Schrijf altijd eerst het testprogramma! #include Test Driven Development using namespace std; #include #include #include #include
"Hond.h" "SintBernard.h" "Tekkel.h" "Kennel.h"
Gewenste uitvoer:
int main() { SintBernard boris; Kennel k; k.zetIn(boris); Tekkel harry; k.zetIn(harry); SintBernard felix; k.zetIn(felix); cout << "Alle honden in de kennel blaffen:" << endl; k.blafAllemaal(); k.haalUit(harry); cout << "Alle honden in de kennel blaffen:" << endl; k.blafAllemaal(); cin.get(); return 0; }
195
Kennel implementatie #include #include using namespace std; #include "Kennel.h" #include "Hond.h" void Kennel::zetIn(Hond& h) { if (inwoners.size() < 5) inwoners.push_back(&h); else cout << "Kennel is al vol!" << endl; } void Kennel::haalUit(Hond& h) { inwoners.erase(find(inwoners.begin(), inwoners.end(), &h)); } void Kennel::blafAllemaal() { for (auto hp: inwoners) hp->blaf(); }
196
Huiswerk! Bestudeer boek:
Warmer & Kleppe, Praktisch UML, 5de editie. 4.4.8 en 4.4.9.
Bestudeer dictaat: paragraaf 4.12.
197
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 16
UML diagrammen Statische structuur van programma. UML Klassediagram. Dynamisch gedrag van programma. UML Objectdiagram. Use-case-diagram UML Use-case-diagram. beschrijft het gedrag UML Sequentiediagram. van het programma gezien vanuit de UML Communicatiediagram. gebruikers van het UML Toestandsdiagram. programma. UML Activiteitsdiagram. 199
UML Use-case-diagram Wordt gebruikt voor vastleggen van de functionele
eisen.
Use-case Actor
Systeemgrens 200
Use-case beschrijving Naam
Rekening openen
Actor
Baliemedewerker
Aannamen
Baliemedewerker heeft beschikking over de NAW-gegevens van de Klant. De Klant kan zich legitimeren.
Beschrijving
1. De baliemedewerker maakt aan het systeem bekend dat een nieuwe rekening aangemaakt moet worden en voert de NAW-gegevens van de Klant in. 2. Als de klant een bedrijf is wordt het KvK nummer ingevoerd. 3. Het systeem controleert of de Klant al rekeningen heeft en of de Klant rood staat op een van deze rekening. In dat geval treedt een uitzondering [rood staan] op. 4. Het systeem maakt het nummer van de nieuwe rekening bekend aan de baliemedewerker.
Uitzonderingen
[rood staan] De baliemedewerker kan naar de use-case Geld storten overgaan om de Klant de gelegenheid te geven het tekort aan te vullen. Als het tekort is aangevuld wordt de use-case vervolgd bij stap 3.
Resultaat
De Klant heeft minstens 1 rekening. 201
Use-case beschrijving
202
UML diagrammen Statische structuur van programma. UML Klassediagram. Dynamisch gedrag van programma. Sequentiediagram laat zien in welke volgorde UML Use-case-diagram. objecten elkaar UML Sequentiediagram. berichten sturen. UML Communicatiediagram. Communicatiediagram UML Toestandsdiagram. laat zien welke objecten UML Activiteitsdiagram. elkaar berichten sturen.
203
Sequentiediagram Boek (p.119): Actor
Tijd
Boodschap
Object
wekker stuurt boodschap zoem naar :Gebruiker
Actor kan boodschap sturen naar object. Object kan boodschap sturen naar actor, andere objecten en naar zichzelf.
204
Sequentiediagram Verbeterde versie:
205
Sequentiediagram Of eigenlijk nog beter:
206
Communicatiediagram Bevat dezelfde informatie als een sequentiediagram.
207
Conditionele boodschappen
208
Conditionele boodschappen
209
Iteratie van boodschappen
210
Iteratie van boodschappen void Kennel::blafAllemaal() { for (auto hp: inwoners) hp->blaf(); }
Visual Paradigm kan geen Sequence Diagram tekenen vanuit C++ code. Visual Paradigm kan geen code (ook geen Java) genereren vanuit een Sequence Diagram 211
Boodschap aan jezelf
212
Constructor
213
Voorbeeld (vervolg...)
214
Opgave Maak een sequentiediagram waarin een
testprogramma de volgende acties uitvoert: Zet een SintBernard genaamd boris in de Kennel
genaamd k. Zet een Tekkel genaamd harry in de Kennel k. Laat alle honden in de Kennel k blaffen.
215
Uitwerking
216
Huiswerk! Bestudeer boek:
Warmer & Kleppe, Praktisch UML, 5de editie. Hoofdstuk 8 (behalve 8.5.3 en 8.7.6).
Hoofdstuk 10 (behalve 10.7). Opgaven bij H8, H10 (zie BB).
Maak opgaven van voorgaande sheets: Sequentiediagram.
217
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 17
UML diagrammen Statische structuur van programma. UML Klassediagram. Dynamisch gedrag van programma. UML Use-case-diagram. UML Sequentiediagram. UML Communicatiediagram. Toestandsdiagram laat de toestanden en UML Toestandsdiagram. toestandsovergangen UML Activiteitsdiagram. van een klasse zien. 219
Toestandsdiagram Aanmaken van een toestandsdiagram van een klasse in
Visual Paradigm:
220
Toestandsdiagram Toestand van een WhiskeyVat Worden we hier veel wijzer van?
221
Toestandsdiagram
222
Toestandsdiagram #include "WhiskyVat_sm.h" class WhiskyVat { private: WhiskyVatContext _fsm; public: WhiskyVat(): _fsm(*this) { } WhiskyVatContext& getContext() { return _fsm; } void maakVol() { _fsm.maakVol(); } void maakLeeg() { _fsm.maakLeeg(); } };
223
Toestandsdiagram #include "WhiskyVat.h" void state_Leeg(WhiskyVat *aWhiskyVat) { printf("Please select transition:\n"); printf("1. maakVol\n"); printf("0. quit\n"); int choice; scanf("%d", &choice); switch (choice) { case 1: aWhiskyVat->maakVol(); break; case 0: exit(0); } } void state_Vol(WhiskyVat *aWhiskyVat) { /* idem */
224
Toestandsdiagram int main(int argc, char **argv) { WhiskyVat lWhiskyVat; while (true) { printf("Current state: %s\n", lWhiskyVat.getContext().getState().getName()); if (&lWhiskyVat.getContext().getState() == &WhiskyVatFSM::Leeg) { state_Leeg(&lWhiskyVat); } else if (&lWhiskyVat.getContext().getState() == &WhiskyVatFSM::Vol) { state_Vol(&lWhiskyVat); } } 225 }
Toestandsdiagram Current state: WhiskyVatFSM::Leeg Please select transition: 1. maakVol 0. quit 1 Current state: WhiskyVatFSM::Vol Please select transition: 1. maakLeeg 0. quit 1 Current state: WhiskyVatFSM::Leeg Please select transition: 1. maakVol 0. quit 0 226
UML diagrammen Statische structuur van programma. UML Klassediagram. Dynamisch gedrag van programma. UML Use-case-diagram. UML Sequentiediagram. UML Communicatiediagram. UML Toestandsdiagram. UML Activiteitsdiagram.
Een Activiteitsdiagram laat een stroom van activiteiten zien.
229
Activiteitsdiagram Start
Swimlane
Activiteit
Guard
Beslispunt Splitsing Synchronisatie
Einde
Samenkomst 230
Huiswerk! Bestudeer boek:
Warmer & Kleppe, Praktisch UML, 5de editie. Hoofdstuk 12.
Hoofdstuk 15. H12 en H15 (zie BB).
Maak opgaven van voorgaande sheets: Toestandsdiagram.
231
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 18
Globaal en lokaal geheugen int global; void f(int parameter) { int local1; // ... } void main() { int local2; // ... f(local2); }
De compiler bepaalt wanneer variabelen worden aangemaakt en opgeruimd.
233
Dynamisch geheugen Je kunt ook zelf beslissen wanneer een variabele
(object) wordt aangemaakt en opgeruimd.
Tekkel* hp = new Tekkel; hp->blaf(); delete hp;
Reserveer geheugenruimte (bij Operating System)
Geef geheugenruimte vrij (aan Operating System) 234
Dynamische array Het geheugen wordt aangevraagd als het programma
runt grootte hoeft niet bij compileren bekend te zijn. Statisch: const size_t s = 5; Tekkel a[s]; for (auto t: a) t.blaf();
Dynamisch:
size_t s; cout << "Hoeveel Tekkels wil je? "; cin >> s; Tekkel a[s]; Error: expected for (auto t: a) constant t.blaf(); expression
size_t s; cout << "Hoeveel Tekkels wil je? "; cin >> s; Range-based for Tekkel* c = new Tekkel[s]; werkt hier niet. for (size_t i = 0; i < s; ++i) c[i].blaf(); delete[] c;
235
Dynamisch geheugen Krachtig: Je bepaalt zelf wanneer geheugen wordt aangemaakt of vrijgegeven. Gevaarlijk: Memory corruption. delete te veel of delete van verkeerde pointer. Memory leak. delete vergeten. Undefined behaviour. gebruik van een deleted variabele. Gebruik zoveel mogelijk standaard componenten zoals std::vector
236
Dynamische std::vector Het geheugen wordt aangevraagd als het programma
runt grootte hoeft niet bij compileren bekend te zijn. Dynamisch: #include using namespace std;
vector::size_type s; cout << "Hoeveel Tekkels wil je? "; cin >> s; vector v(s); for (Tekkel t: v) t.blaf(); 237
Constructor Wordt door de compiler aangeroepen
als een variabele gemaakt wordt. class Breuk { public: Breuk(); Breuk(int t); Breuk(int t, int n); int teller() const; int noemer() const; void plus(Breuk b); private: int boven; int onder; void normaliseer(); };
void main() { Breuk a, b(-2), c(21,-9); // ... } 238
Destructor Wordt door de compiler aangeroepen als een variabele
verwijderd wordt. class Breuk { public: Breuk(); Breuk(int t); Breuk(int t, int n); ~Breuk(); int teller() const; int noemer() const; void plus(Breuk b); private: int boven; int onder; void normaliseer(); };
Breuk::~Breuk() { cout << "De breuk met teller " << boven << " en noemer " << onder << " is overleden." << endl; } void main() { Breuk a, b(-2), c(21,-9); // ... } 239
Constructor en destructor Compiler roept de constructor en destructor
automatisch aan. Reserveer geheugen op de stack voor a en roep constructor a.Breuk(21,-9) aan
{
Breuk a(21,-9); // ... // ... // ... }
Roep destructor a.~Breuk() aan en geef geheugen van a weer vrij 240
Constructor en destructor Compiler roept de constructor en destructor
automatisch aan. Reserveer geheugen op de heap en roep constructor Breuk(21,-9) aan
{
}
Breuk* bp = new Breuk(21,-9); // ... Roep destructor delete bp; ~Breuk() aan en geef // ... geheugen weer vrij 241
Gratis bij elke class! constructor zonder argument (default constructor).
Deze constructor roept de default constructor aan van alle data members. copy constructor. Deze constructor roept de copy constructor aan van alle data members. assignment operator (operator=). Deze assignment operator roept de assignment operator aan van alle data members. destructor. Deze destructor roept de destructor aan van alle data members. Je kunt al deze functies ook zelf definiëren!
242
Default destructor probleem De automatisch door de compiler aangemaakte default
destructor is niet virtual. class Hond { ~SB() wordt niet int main() { Hond* Boris(new SB); public: aangeroepen! Boris->blaf(); virtual void blaf() = 0; delete Boris; }; } class SB: public Hond { private: Whisky vat; public: SB() { vat.maakVol(); } virtual ~SB () { vat.maakLeeg(); } virtual void blaf() override { cout << "Woef woef" << endl; } 243 };
Virtual destructor Als een class nu of in de toekomst als basis class
gebruikt wordt dan moet de destructor virtual zijn zodat van deze class afgeleide classes via een polymorphic pointer gedelete kunnen worden. C++11 class Hond { public: virtual ~Hond() = default; virtual void blaf() = 0; };
class SintBernard: public Hond { public: virtual ~SintBernard(); };
244
Huiswerk! Voeg in practicum opgave 2b een destructor toe die
meldt dat een Tijdsduur is overleden. Verklaar de uitvoer.
Verander nu in opgave 2b elke const Tijdsduur&
door een Tijdsduur. Verklaar de wijzigingen in de uitvoer.
Bestudeer dictaat: Hoofdstuk 5 t/m 5.4.
245
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 19
Aggregatie implementatie
class SintBernard: public Hond { public: SintBernard(); virtual ~SintBernard(); virtual void blaf() override; private: WhiskeyVat* vat; };
Geef de implementatie van de constructor en de destructor. 247
Implementatie SintBernard::SintBernard(): vat(new WhiskeyVat) { vat->maakVol(); } virtual ~ SintBernard::SintBernard() { vat->maakLeeg(); delete vat; } virtual void SintBernard::blaf() { cout << "WOEF WOEF" << endl; }
:SintBernard vat
:WhiskeyVat Aangemaakt met new
248
Copy constructor SintBernard a; SintBernard b(a);
Default copy constructor: kopieert alle data members
SintBernard::SintBernard(): vat(new WhiskeyVat) { vat->maakVol(); }
a b
vat
vat
?
?
Dit is niet goed! We moeten zelf een copy constructor definiëren. 249
Copy constructor Gewenst resultaat:
a
vat
b
vat
250
Copy constructor SintBernard::SintBernard(const SintBernard& r): Hond(r), vat(0) { if (r.vat != 0) vat = new WhiskeyVat(*(r.vat)); }
Kun je ook een SintBernard in plaats van een const
SintBernard& als parameter gebruiken? Nee! Want dan moet er een kopietje worden gemaakt en dan
wordt de copy constructor aangeroepen, maar dan moet er een kopietje worden gemaakt en dan wordt de copy constructor aangeroepen, maar dan moet er een kopietje worden gemaakt en dan wordt de copy constructor aangeroepen maar, dan moet er een kopietje worden gemaakt en dan wordt de copy constructor aangeroepen maar, dan ...
251
operator= SintBernard a; SintBernard b; b = a;
Default assignment operator: assigned alle data members
b.vat = a.vat;
Dit is niet goed! We moeten zelf een operator= definiëren.
a
vat
b
vat
252
operator= Gewenst resultaat:
a
vat
b
vat
253
operator= SintBernard& SintBernard::operator=(const SintBernard::operator=(SintBernard SintBernard& r) { r) { std::swap(vat, SintBernard t(r); r.vat); return *this; t.vat); std::swap(vat, } return *this; }
Kun je ook een SintBernard in plaats van een const
SintBernard& als parameter gebruiken? Ja! Er wordt dan een overbodig kopietje gemaakt.
Ja!!! Het maken van het kopietje t is dan overbodig!
Wat is nut van SintBernard& return type en return
*this? a = b = c;
254
Wanneer zelf definiëren? Een class moet een zelf gedefinieerde copy constructor,
operator= en destructor bevatten als: die class een pointer bevat en
als bij het kopiëren van een object van de class niet de
pointer, maar de data waar de pointer naar wijst moet worden gekopieerd en als bij een toekenning aan een object van de class niet de pointer, maar de data waar de pointer naar wijst moet worden toegekend en als bij het opruimen van een object van de class niet alleen de pointer, maar ook de data waar de pointer naar wijst moet worden opgeruimd. 255
Huiswerk! Bestudeer dictaat: Paragraaf 5.5 t/m 5.10.
256
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 20
Memberfuncties en data members Elk object heeft zijn eigen data members terwijl de
memberfuncties door alle objecten van een bepaalde class "gedeeld" worden. class Hond { public: Hond(const string& n); void blaf(); private: string naam; }; Hond::Hond(const string& n): naam(n) { } void Hond::blaf() { cout << naam << " zegt: WOEF" << endl; }
258
Memberfuncties en data members We willen bijhouden hoeveel objecten van de class
Hond er op een bepaald moment bestaan. int aantalHonden = 0; //dit is een globale variabele class Hond { public: Hond(const string& n); ~Hond(); void blaf(); private: string naam; }; Hond::Hond(const string n): naam(n) { ++aantalHonden; } Hond::~Hond() { --aantalHonden; } 259
static Een static data member is een onderdeel van de
class en wordt door alle objecten van de class gedeeld. class Hond { public: Hond(const string& n); ~Hond(); void blaf(); static int aantal(); private: string naam; static int aantalHonden; };
260
static Een static data member is een onderdeel van de
class en wordt door alle objecten van de class gedeeld. int Hond::aantalHonden = 0; Hond::Hond(const string& n): naam(n) { ++aantalHonden; } Hond::~Hond() { --aantalHonden; } int Hond::aantal() { return aantalHonden; } void Hond::blaf() { cout << naam << " zegt: WOEF" << endl; }
261
static memberfuncties Twee manieren van aanroepen: direct via de classnaam: class_naam::member_functie_naam(parameters) Voorbeeld: cout << Hond::aantal() << endl; via een object van de class: object_naam.member_functie_naam(parameters) Voorbeeld: cout << h1.aantal() << endl; Beperkingen t.o.v. een gewone memberfunctie: Een static memberfunctie heeft geen receiver (ook niet als hij via een object aangeroepen wordt). Een static memberfunctie heeft dus geen this pointer. Een static memberfunctie kan dus geen "gewone" memberfuncties aanroepen en ook geen "gewone" data members gebruiken. 262
static Een static data member is een onderdeel van de
class en wordt door alle objecten van de class gedeeld. class Hond { public: Hond(const string& n); ~Hond(); void blaf(); static int aantal(); private: string naam; static int aantalHonden; };
UML boek: paragraaf 4.4.4.
263
static static in Visual Paradigm:
264
Huiswerk! Bestudeer dictaat: Paragraaf 6.1. Bestudeer extra voorbeelden over inheritance en
polymorphisme: http://bd.eduweb.hhs.nl/ogoprg/extra.htm
Bestudeer boek: 4.5 en 4.6 (exclusief 4.6.8 en 4.6.9)
265
Overzicht stof OGOPRG Dictaat: Hoofstuk 1 t/m 6 (alleen 6.1, de rest van hoofdstuk 6 is voor de “liefhebbers”). Boek: Hoofdstuk 1 en 2. Hoofdstuk 4 (behalve 4.4.1, 4.4.3 t/m 4.4.7, 4.4.10 t/m 4.4.18, 4.6.8 en 4.6.9). Hoofdstuk 8 (behalve 8.5.3 en 8.7.6). Hoofdstuk 10 (behalve 10.7). Hoofdstuk 12. Hoofdstuk 15. Opgaven! Zie slides en BB.
266
Afronden practicum Deze week klaar! Anders onvoldoende.
Herkansing: Mail naar [email protected] als je deel wilt nemen aan de herkansing. Er zal vervolgens een rooster worden gemaakt voor op maandag 29 juni (duaal) en voltijd 30 juni.
267
Objectgeoriënteerd Programmeren in C++ OGOPRG Les 21
ADT Array De grootte van een Array kan tijdens run-time bepaald
worden. Bij het gebruik van [] wordt gecontroleerd of de index binnen de grenzen van de Array ligt. Implementatie van Array: size data
4 0
1
2
3
0
1
4
9
Aangemaakt met new 269
ADT Array class Array { public: explicit Array(int s); Array(const Array& r); Array& operator=(const Array& r); ~Array(); int& operator[](int index); const int& operator[](int index) const; int length() const; bool operator==(const Array& r) const; bool operator!=(const Array& r) const; // ... // Er zijn vele uitbreidingen mogelijk. private: int size; int* data; };
270
ADT Array Array::Array(int s): size(s), data(new int[s]) { }
Array::~Array() { delete[] data; }
size data
4 0
1
2
3
?
?
?
?
Aangemaakt met new 271
Copy constructor Array a(4); for (int j = 0; j < a.length(); ++j) { a[j] = j * j; // vul a met kwadraten } Default copy constructor: Array b(a); kopieert alle data members Array::Array(int s): size(s), data(new int[s]) { } size 4 ? 0 1 2 3
a
data
b
?
size
? 4
data
?
0 ?
1 ?
4 ?
9 ?
Dit is niet goed! We moeten zelf een copy constructor definiëren. 272
Copy constructor Gewenst resultaat:
a
size
4
data
b
size data
0
1
2
3
0
1
4
9
0
1
2
3
0
1
4
9
4
273
Copy constructor Array::Array(const Array& r): size(r.size), data(new int[r.size]) { for (int i = 0; i < size; ++i) data[i] = r.data[i]; }
Kun je ook een Array in plaats van een const
Array& als parameter gebruiken? Nee! Want dan moet er een kopietje worden gemaakt en dan
wordt de copy constructor aangeroepen, maar dan moet er een kopietje worden gemaakt en dan wordt de copy constructor aangeroepen, maar dan moet er een kopietje worden gemaakt en dan wordt de copy constructor aangeroepen maar, dan moet er een kopietje worden gemaakt en dan wordt de copy constructor aangeroepen maar, dan moet er een kopietje worden gemaakt en dan ...
274
operator= Dit is niet goed! We Array a(4); moeten zelf een Array b(a.length()); operator= definiëren. for (int j = 0; j < a.length(); ++j) { a[j] = j * j; // vul a met kwadraten b[j] = j * a[j]; // vul b met derde machten } Default assignment b = a; operator: size 4 assigned alle data 0 1 2 3 members
a
data
b.size = a.size; b.data = a.data;
b
size data
0
1
4
9
0
1
2
3
0
1
8
4 27 275
operator= Gewenst resultaat:
a
size
4
data
b
0
1
2
3
0
1
4
9
0
1
2
3
0
1
4
9
size 4 data
276
operator= Array& Array::operator=(const Array::operator=(Array r) Array& { r) { std::swap(data, Array t(r); r.data); std::swap(size, t.data); std::swap(data, r.size); return *this; std::swap(size, t.size); } return *this; }
Kun je ook een Array in plaats van een const
Array& als parameter gebruiken? Ja! Er wordt dan een overbodig kopietje gemaakt.
Ja!!! Het maken van het kopietje t is dan overbodig!
Wat is nut van Array& return type en return
*this? a = b = c;
277
Wanneer zelf definiëren? Een class moet een zelf gedefinieerde copy constructor,
operator= en destructor bevatten als: die class een pointer bevat en
als bij het kopiëren van een object van de class niet de
pointer, maar de data waar de pointer naar wijst moet worden gekopieerd en als bij een toekenning aan een object van de class niet de pointer, maar de data waar de pointer naar wijst moet worden toegekend en als bij het opruimen van een object van de class niet alleen de pointer, maar ook de data waar de pointer naar wijst moet worden opgeruimd. 278
ADT Array versus C array Voordelen ADT Array: Bij aanmaken mag de grootte een variabele zijn. Bij gebruik [] wordt de index gecontroleerd. Nadelen ADT Array: Je kunt er alleen integers in opslaan!
Oplossing?
279
Template class Array template class Array { public: explicit Array(int s); Array(const Array& v); Array& operator=(const Array& r); ~Array(); T& operator[](int index); const T& operator[](int index) const; int length() const; bool operator==(const Array& r) const; bool operator!=(const Array& r) const; // ... // Er zijn vele uitbreidingen mogelijk. private: int size; template T* data; Array::Array(int s): }; size(s), data(new T[s]) { }
280
Template class Array // ... int i; cout << "Hoe groot wil je de Array's "; cin >> i; Array<double> a(i); for (int j = 0; j < v.length(); ++j) a[j] = sqrt(j); // Vul a met wortels cout << "a[12] = " << a[12] << endl; Array b(i); for (int j = 0; j < b.length(); ++j) b[j] = j * j; // Vul b met kwadraten cout << "b[12] = " << b[12] << endl; // ...
281
ADT Array versus std::Array Voordelen ADT Array: Bij aanmaken mag de grootte een variabele zijn. Bij gebruik [] wordt de index gecontroleerd. Voordelen std::Array: Grootte is een template parameter en hoeft dus niet opgeslagen te worden. Je kunt range-based for gebruiken om door de std::Array te lopen. Je kunt een std::Array initializeren met { … }
Oplossing? 282
ADT Array uitbreidingen C++11 #include template class Array { public: // ... // Support for initializer list Array(initializer_list list); // Support for range-based for T* begin(); const T* begin() const; T* end(); const T* end() const; private: int size; T* data; }; 283
ADT Array uitbreidingen C++11 template Array::Array(initializer_list l): size(list.size()), data(new T[size]) { auto listIter = list.begin(); for (int i = 0; i < size; ++i) { data[i] = *listIter++; } } template T* Array::begin() { return data; } template const T* Array::begin() const { return data; } template T* Array::end() { return data + size; } template const T* Array::end() const { return data + size; 284 }
ADT Array uitbreidingen C++11 // ... Array v = {1, 2, 3, 4}; for (auto e: v) { cout << e << " "; } cout << endl;
// ...
285
Objectgeoriënteerd Programmeren in C++ Terugblik practicum
Inhoud Objectgeoriënteerd Programmeren in C++. responsibility driven design (ontwerpen uitgaande van
verantwoordelijkheden). information hiding (het afschermen van informatie door middel van het scheiden van interface en implementatie). abstraction (het afschermen van complexiteit door middel van het scheiden van interface en implementatie). inheritance (het mogelijk maken van een nieuwe vorm van hergebruik, ... is een ... in plaats van ... heeft een ...). polymorphism (veelvormigheid mogelijk gemaakt door dynamic binding).
Objectgeoriënteerd Ontwerpen met UML.
klasse- en objectdiagrammen. use-case-diagram. sequence- en collaborationdiagrammen. toestands- en activiteitendiagrammen. 287
Herbruikbaarheid Practicumopdracht 1 Hier heb je gezien dat de class std::string een eenvoudig te (her)gebruiken component is. Een object van het type std::string: Slaat een character string op (private data members). Heeft interface (public memberfuncties) waarmee: Informatie over de opgeslagen string kan worden opgevraagd (find). De opgeslagen string kan worden gewijzigd (erase).
Practicumopdracht 2a Hier heb je zelf een ADT (Abstract DataType = herbruikbare component) Tijdsduur gemaakt. Practicumopdracht 2b Hier heb je door operator overloading toe te passen de herbruikbaarheid vergroot.
288
Aanpasbaarheid Practicumopdracht 2c Hier heb je de implementie van de classs Tijdsduur gewijzigd (slaat alleen nog minuten op) zonder de interface aan te passen. Alle code die gebruik maakt van de component Tijdsduur merkt hier niets
van en hoeft niet aangepast te worden! Beide implementaties hebben hun sterkte: Implementatie van opdracht 2b kan sneller printen. Implementatie van opdracht 2c kan sneller rekenen en gebruikt minder geheugen.
Doordat beide implementaties dezelfde interface hebben kan een gebruiker
van de component (een programmeur) altijd nog wisselen van implementatie.
289
Herbruikbaarheid Practicumopdracht 2d Hier heb je de code van de component Tijdsduur opgenomen in twee aparte files Tijdsduur.h en Tijdsduur.cpp De herbruikbaarheid is hierdoor vergroot (component kan gebruikt worden door Tijdsduur.h te includen en Tijdsduur.cpp mee te linken). De aanpasbaarheid is hierdoor vergroot (wisselen van implementatie kan nu door wisselen van files). Je kan de component nu verspreiden zonder de source code vrij te geven. Door alleen Tijdsduur.h en een gecompileerde versie van Tijdsduur.cpp te verspreiden. 290
Uitbreidbaarheid Practicumopdracht 3b en 3c Hier heb je gezien dat door het gebruik van overerving het in opdracht 3a gemaakte programma eenvoudig is uit te breiden met een nieuwe soorten werknemers: Stukwerker en Manager. De polymorphic functie printMaandSalaris(const Werknemer& w)
hoeft niet aangepast te worden. Je hoeft alleen de “verschillen” te programmeren. Het “gemeenschappelijke” erf je over vanuit de basisklasse Werknemer. Als het goed is heb je de basisklasse Werknemer abstract gemaakt om slicing te voorkomen.
291