Cover
Empieza ahora gratis H4_OGP_C++.pdf
Summary
# Algemeen over klassen in C++
Klassen in C++ vormen de basis voor objectgeoriënteerd programmeren, waarbij ze fungeren als blauwdrukken voor het creëren van objecten met specifieke data (attributen) en gedragingen (lidfuncties) [3](#page=3).
### 1.1 Declaratie en definitie van klassen
Een klasse in C++ wordt onderscheiden in een **declaratie** en een **definitie** [3](#page=3).
* **Declaratie:** De declaratie van een klasse omvat de structuur van de klasse, inclusief de attributen (dataleden) en de declaraties van de lidfuncties. Het is cruciaal om de afsluitende puntkomma (`;`) niet te vergeten na de afsluitende accolades (`}`) van de klassedeclaratie [3](#page=3).
* **Definitie:** De definitie van een lidfunctie vindt plaats buiten de klassedeclaratie. Hierbij wordt de scope-operator (`::`) gebruikt, direct voorafgaand aan de naam van de lidfunctie, om aan te geven tot welke klasse de functie behoort [3](#page=3).
> **Tip:** De standaardwaarde voor parameters van lidfuncties mag slechts één keer worden gespecificeerd, doorgaans in de klassedeclaratie. Bij het definiëren van de lidfunctie buiten de klasse wordt deze standaardwaarde weggelaten [3](#page=3).
### 1.2 Lidfuncties en de `this` pointer
Lidfuncties zijn de functies die tot een klasse behoren en opereren op de data van een object van die klasse.
* **De `this` pointer:** Binnen een lidfunctie verwijst de speciale pointer `this` naar het huidige object waarop de functie wordt aangeroepen. Dit wordt vaak gebruikt om een onderscheid te maken tussen een klasse-attribuut en een parameter met dezelfde naam [4](#page=4).
> **Voorbeeld:**
> ```cpp
> void voorbeeld::set_a(int a) {
> this->a = a; // 'this->a' verwijst naar het attribuut van het object,
> // 'a' verwijst naar de parameter.
> }
> ```
### 1.3 Private en public secties
Klassen maken gebruik van toegangscontrolemechanismen om de zichtbaarheid en toegankelijkheid van hun leden te beheren.
* **`private` sectie:** Leden die in de `private` sectie worden gedeclareerd, zijn alleen toegankelijk vanuit lidfuncties van dezelfde klasse. Dit bevordert inkapseling door interne implementatiedetails te verbergen [3](#page=3).
* **`public` sectie:** Leden in de `public` sectie zijn toegankelijk vanuit zowel lidfuncties van de klasse als van buiten de klasse. Dit definieert de interface van de klasse, wat gebruikers van de klasse kunnen gebruiken [3](#page=3).
* **Default toegankelijkheid:** Indien er geen expliciete `public:` of `private:` specificatie is, worden alle leden standaard als `private` beschouwd [3](#page=3).
* **Enkele specificatie:** De sleutelwoorden `private:` en `public:` mogen elk slechts één keer voorkomen binnen een klassedeclaratie [3](#page=3).
> **Voorbeeld van een klassedeclaratie:**
> ```cpp
> class voorbeeld {
> public: // public-blok
> void set_a(int = -1); // Lidfunctie declaratie
> private: // private-blok
> int a, b; // Primitieve attributen worden NIET automatisch geïnitialiseerd
> }; // Afsluitende puntkomma is vereist
> ```
### 1.4 Mutator en Accessor lidfuncties
Lidfuncties kunnen grofweg worden ingedeeld naar hun functie:
* **Mutator (Mutator):** Dit zijn lidfuncties die de attributen van het huidige object wijzigen. Voorbeelden hiervan zijn "setter"-functies die een waarde instellen voor een attribuut [5](#page=5).
* **Accessor (Accessor):** Dit zijn lidfuncties die de attributen van het huidige object niet wijzigen. "Getter"-functies, die de waarde van een attribuut ophalen, vallen hieronder [5](#page=5).
#### 1.4.1 De `const` specificatie voor accessors
Om expliciet te garanderen dat een lidfunctie een attribuut niet wijzigt, kan het sleutelwoord `const` na de parameterlijst worden toegevoegd. De compiler controleert vervolgens of de functie zich aan deze garantie houdt [5](#page=5).
> **Belangrijk:**
> * De `const` specificatie moet worden herhaald bij de definitie van de lidfunctie buiten de klasse, omdat het deel uitmaakt van de functie-signatuur [5](#page=5).
> * Het toekennen van `const` aan een "setter" lidfunctie zal resulteren in een compilerfout, aangezien setters per definitie de objecttoestand wijzigen [6](#page=6).
> **Voorbeeld met `const` accessor:**
> ```cpp
> class voorbeeld {
> public:
> void set_a(int = -1);
> int get_a() const; // Accessor gemarkeerd als const
> private:
> int a, b;
> };
>
> int voorbeeld::get_a() const { // const moet hier herhaald worden
> return a;
> }
> ```
---
# Constructoren en destructoren
Dit onderwerp behandelt de automatische aanroep van constructoren en destructoren bij het creëren en vernietigen van objecten, inclusief de rol van destructoren bij dynamisch geheugenbeheer [10](#page=10) [8](#page=8).
### 2.1 Constructoren
Een constructor is een speciale methode binnen een klasse die automatisch wordt aangeroepen wanneer een object van die klasse wordt gedeclareerd. Het primaire doel van een constructor is om een correct geïnitialiseerd object te creëren [8](#page=8).
#### 2.1.1 Kenmerken van constructoren
* Een constructor heeft geen return-type, zelfs geen `void` [8](#page=8).
* Constructor overloading is mogelijk, wat betekent dat er meerdere constructoren met verschillende parameterslijsten binnen dezelfde klasse kunnen bestaan [8](#page=8).
* Default-parameters zijn ook toegestaan in constructoren [8](#page=8).
* Als een klasse geen enkele constructor bevat, wordt er automatisch een default constructor voorzien. Deze default constructor initialiseert primitieve typen met willekeurige waarden ("rommel") en klasse-attributen met hun eigen default constructoren [8](#page=8).
* Zodra een klasse ook maar één eigen constructor definieert, wordt de automatische default constructor niet meer verstrekt [8](#page=8).
#### 2.1.2 Declaratie en aanroep van constructoren
De syntaxis voor een constructor is de naam van de klasse, gevolgd door een optionele parameterlijst: `klassenaam([parameterlijst])` [8](#page=8).
Constructoren worden aangeroepen bij de declaratie van een object, zonder het gebruik van het `new` sleutelwoord [9](#page=9).
> **Voorbeeld:**
> ```cpp
> student std1; // Aangeroepen met default constructor (indien gedefinieerd)
> student std2("Tom"); // Aangeroepen met constructor die een string accepteert
> // NIET OK: student std1();
> // WEL OK (C++11): student std1{};
> ```
> [9](#page=9).
Een object kan ook achteraf worden hergeïnitialiseerd door toewijzing met een ander object van dezelfde klasse, wat impliciet een constructor of kopieeroperator kan aanroepen: `std1 = student("Jan");` [9](#page=9).
#### 2.1.3 Delegerende constructoren
Vanaf C++11 is het mogelijk voor een constructor om een andere constructor van dezelfde klasse aan te roepen via de initializer list. Dit wordt "delegerende constructoren" genoemd [13](#page=13).
> **Voorbeeld:**
> ```cpp
> class voorbeeld {
> public:
> voorbeeld(int _a, int _b) {
> a = _a; b = _b;
> }
> voorbeeld() : voorbeeld(1,2) {} // Roept de andere constructor aan
> private:
> int a, b;
> };
> ```
> [13](#page=13).
### 2.2 Destructoren
Een destructor is een speciaal soort lidfunctie dat automatisch wordt aangeroepen wanneer een object zijn levensduur beëindigt [10](#page=10).
#### 2.2.1 Kenmerken van destructoren
* Een destructor heeft geen return-type (ook geen `void`) en mag geen parameters hebben. Hierdoor is destructor overloading niet mogelijk [10](#page=10).
* De destructor wordt automatisch opgeroepen wanneer een object "out of scope" gaat [10](#page=10).
* Een destructor is alleen strikt noodzakelijk wanneer een klasse dynamisch aangemaakte attributen beheert, zoals datagebieden die zijn aangemaakt met `new` en worden bijgehouden via raw pointers [10](#page=10).
#### 2.2.2 Declaratie van destructoren
De syntaxis voor een destructor is een tilde (`~`) gevolgd door de naam van de klasse: `~klassenaam()` [10](#page=10).
#### 2.2.3 Werking en geheugenbeheer
Bij het verlaten van de scope van objecten die zich op de runtime stack bevinden, wordt de destructor automatisch aangeroepen en het geheugen op de stack vrijgegeven [12](#page=12).
> **Voorbeeld van constructor- en destructoraanroep:**
> ```cpp
> #include
>
> class myclass {
> int i;
> public:
> myclass(int i);
> ~myclass() {
> std::cout << "Destructor object " << i << std::endl;
> }
> };
>
> myclass::myclass(int i) {
> std::cout << "Constructor object " << i << std::endl;
> this->i = i;
> }
>
> int main() {
> myclass a [1](#page=1);
> for (int i = 2; i < 4; i++)
> myclass b(i);
> return 0;
> }
> ```
>
> **Output:**
> ```
> Constructor object 1
> Constructor object 2
> Destructor object 2
> Constructor object 3
> Destructor object 3
> Destructor object 1
> ```
> [11](#page=11) [12](#page=12).
### 2.3 Kopieerconstructoren
De kopieerconstructor wordt in C++ standaard gebruikt in twee specifieke situaties [14](#page=14):
1. Bij het initialiseren van een nieuw object met een bestaand object van dezelfde klasse: `student std2(std1);` [14](#page=14).
2. Bij het doorgeven van objecten als value-parameters aan functies: `void proc(student st)` waarbij `st` een kopie is van het origineel [14](#page=14).
De standaard kopieerconstructor is aanwezig, zelfs als er andere constructoren zijn gedefinieerd, tenzij de klasse attributen zoals `unique_ptr` bevat. De standaard kopieerconstructor kopieert alle attributen (passieve kopie, of "shallow copy"). Dit kan leiden tot problemen bij raw pointers, aangezien meerdere objecten dan naar dezelfde dynamisch toegewezen geheugenlocatie kunnen verwijzen, wat resulteert in een gedeelde structuur [14](#page=14).
Het zelf definiëren van een kopieerconstructor is slechts uitzonderlijk noodzakelijk. Dit is voornamelijk het geval bij het beheer van raw pointers, waarbij een "deep copy" (een nieuwe kopie van de data op de heap) gemaakt moet worden. Voor moderne C++-idiomen, zoals `unique_ptr`, is het gebruik van `make_unique` een betere praktijk [14](#page=14).
De syntaxis voor een kopieerconstructor is `A(const A &a)`, waarbij `A` de klassenaam is. Het is belangrijk om een constante referentie (`const A &a`) te gebruiken en niet een waarde-parameter (`A a`), om onnodige kopieën en potentiële recursie te vermijden [14](#page=14).
---
# Objecten als attributen
Dit gedeelte behandelt het concept van objecten die worden gebruikt als attributen binnen andere klassen, en het belang van initializer lists voor een correcte initialisatie.
### 3.1 Associatie, aggregatie en compositie
Wanneer een klasse een object van een andere klasse bevat als een van haar attributen, wordt dit een associatie, aggregatie of compositie genoemd [16](#page=16).
### 3.2 Initialisatie van attributen in constructors
Wanneer het hoofdblok van een constructor wordt betreden, zijn de attributen van het object al aanwezig [16](#page=16).
* **Primitieve attributen** bevatten bij aanvang willekeurige waarden ('rommel') [16](#page=16).
* **Objectattributen** worden geïnstantieerd met hun default constructor. Als een default constructor niet bestaat voor een dergelijk attribuut, leidt dit tot een fout [16](#page=16).
### 3.3 Het belang van initializer lists
Een initializer list biedt de mogelijkheid om de constructie en initialisatie van attributen te laten plaatsvinden *voordat* het hoofdblok van de constructor wordt betreden. Dit is cruciaal voor een correcte en gecontroleerde initialisatie van zowel primitieve als objectattributen [16](#page=16).
#### 3.3.1 Syntax van initializer lists
De syntax voor een initializer list is als volgt [17](#page=17):
```cpp
class ClassNaam {
public:
ClassNaam(const ParameterType1 ¶m1, ParameterType2 param2) : attribuut1(param1), attribuut2(param2) {}
private:
Type1 attribuut1;
Type2 attribuut2;
};
```
#### 3.3.2 Voorbeeld van een initializer list
Een concreet voorbeeld van het gebruik van een initializer list wordt getoond in de bestanden binnen de map `vbInitList`. Hier is een schematische weergave [17](#page=17):
```cpp
class A {
public:
A(const B &b1, int i1) : b(b1), i(i1) {}
private:
B b;
int i;
};
```
In dit voorbeeld wordt het attribuut `b` geïnitialiseerd met de meegegeven `b1` en het attribuut `i` met de meegegeven `i1`, nog voordat de code binnen het constructorblok van `A` wordt uitgevoerd.
> **Tip:** Het is sterk aan te raden om bij het schrijven van constructors zoveel mogelijk gebruik te maken van initializer lists om onnodige initialisaties te vermijden en de correctheid van de objecttoestand te garanderen [17](#page=17).
---
# Friend functies en klassen
Dit onderwerp behandelt het concept van friend functies en klassen, die directe toegang hebben tot private leden van een klasse, en de implicaties hiervan voor objectgeoriënteerde principes.
### 4.1 Friend functies
Een friend functie van een klasse is een externe functie die, hoewel geen lidfunctie van de klasse, directe toegang heeft tot de private attributen en lidfuncties van die klasse. Dit betekent dat een friend functie dezelfde rechten heeft als een lidfunctie wat betreft toegang tot private leden [19](#page=19).
#### 4.1.1 Declaratie van een friend functie
Om een functie als vriend van een klasse te declareren, wordt het sleutelwoord `friend` gebruikt binnen de klassedeclaratie. Als de friend functie buiten de klasse wordt gedefinieerd, mag het sleutelwoord `friend` niet worden herhaald en mag er geen `klassennaam::` voor de functienaam staan [19](#page=19).
> **Voorbeeld:**
>
> ```cpp
> class A {
> int i;
> public:
> A(int _i) : i(_i) {}
> friend int fr(const A &); // Declaratie van friend functie
> };
>
> // Definitie van de friend functie
> int fr(const A &a) {
> return a.i; // Directe toegang tot private lid 'i'
> }
>
> int main() {
> A a [7](#page=7);
> cout << fr(a); // Roepen van de friend functie
> return 0;
> }
> ```
> [20](#page=20).
#### 4.1.2 Implicaties voor OO-principes
Volgens strikte objectgeoriënteerde principes zouden enkel lidfuncties directe toegang moeten hebben tot private attributen. Het concept van een friend functie schendt deze basisprincipes enigszins, omdat een externe functie toegang krijgt tot private leden. Desondanks wordt het in C++ gebruikt, met name voor operator overloading en voor efficiëntieverhoging. Het biedt directe toegang tot private attributen van een klasse, wat overhead kan besparen vergeleken met het benaderen via publieke lidfuncties (getters) [21](#page=21).
### 4.2 Friend klassen
Een hele klasse kan ook als vriend van een andere klasse worden aangewezen. Wanneer klasse `Y` wordt gedeclareerd als friend van klasse `Z`, krijgt klasse `Y` directe toegang tot de private leden van klasse `Z`. Het omgekeerde is niet waar; klasse `Z` krijgt geen toegang tot de private leden van klasse `Y` tenzij dit expliciet wordt toegekend. Vriendschap wordt dus toegekend, niet aangenomen [22](#page=22).
> **Voorbeeld:**
>
> ```cpp
> class Z {
> friend class Y; // Klasse Y is friend van klasse Z
> private:
> int secret_z;
> public:
> Z(int s) : secret_z(s) {}
> };
>
> class Y {
> public:
> void access_z_data(const Z& z_obj) {
> // Klasse Y kan direct bij private lid van Z
> cout << "Accessing Z's secret: " << z_obj.secret_z << endl;
> }
> };
> ```
> [22](#page=22).
---
# Operator overloading
Operator overloading makes code more readable and expressive by allowing the redefinition of operators for custom classes [24](#page=24).
### 5.1 Introduction to operator overloading
In C++, certain operators are implicitly defined for objects, such as the assignment operator (`=`) and the address-of operator (`&`). However, most operators can be redefined to work with user-defined types. The general syntax for an overloaded operator, when defined as a member function, is `return_type operator_name([parameter_list]) [const]`. When an operator like `t1 + t2` is used, it is compiled as `t1.operator+(t2)` if it's a member function, or `operator+(t1, t2)` if it's an external function [24](#page=24).
#### 5.1.1 Commonly overloaded operators
A range of operators can be overloaded to enhance class functionality [25](#page=25):
* Arithmetic operators: `+`, `-`, `*`, `/`, `%`
* Comparison operators: `<`, `<=`, `>`, `>=`, `==`, `!=`
* Assignment operators: `=`, `+=`, `-=`, `*=`, `/=`, `%=`
* Increment/Decrement operators: `++`, `--`
* Stream insertion/extraction operators: `<<`, `>>`
* Array subscript operator: `[]`
> **Tip:** The compiler automatically generates the assignment operator (`=`) unless an attribute is a `unique_ptr` (in which case it's deleted). By default, this generated operator performs a shallow copy. If a deep copy is required, especially when a class contains raw pointers, the assignment operator often needs to be overridden [25](#page=25).
### 5.2 Examples of operator overloading
#### 5.2.1 Overloading arithmetic and comparison operators
Consider a `tijd` (time) class with attributes for hours, minutes, and seconds. To support operations like `t0 = t1 + t2`, `t3 = t2 * 2`, and `if (t1 < t2)`, specific operators need to be overloaded. Since the left-hand operand is always an object of the `tijd` class, these are typically implemented as member functions (#page=26, page=27) [26](#page=26) [27](#page=27).
The signatures for such member functions would be:
* `tijd operator+(const tijd &) const;` for addition [27](#page=27).
* `tijd operator*(int) const;` for multiplication by an integer [27](#page=27).
* `bool operator<(const tijd &) const;` for comparison [27](#page=27).
**Example Implementation:**
```cpp
// Addition operator
tijd tijd::operator+(const tijd &t) const {
tijd som(uur + t.uur, min + t.min, sec + t.sec);
return som;
}
// Multiplication operator
tijd tijd::operator*(int factor) const {
return tijd(uur * factor, min * factor, sec * factor);
}
// Less than operator
bool tijd::operator<(const tijd &t) const {
// Convert times to seconds for comparison
return uur * 3600 + min * 60 + sec <
t.uur * 3600 + t.min * 60 + t.sec;
}
```
#### 5.2.2 Overloading unary operators
Unary operators, which operate on a single operand, can also be overloaded. For instance, the unary minus operator (`-`) for the `tijd` class can be implemented as a member function [29](#page=29):
```cpp
tijd tijd::operator-() const {
return tijd(-uur, -min, -sec);
}
```
It's important to note that a unary minus operator can coexist with a binary subtraction operator if both are needed [29](#page=29).
#### 5.2.3 Overloading assignment and compound assignment operators
The assignment operator (`=`) and compound assignment operators (`+=`, `-=`, `*=`, `/=`, `%=`) are crucial for managing object states. The assignment operator is usually provided by default, but it performs a shallow copy. For correct deep copying, especially with raw pointers, overriding the assignment operator is necessary (#page=25, page=30) [25](#page=25) [30](#page=30).
A key characteristic of overloaded assignment and compound assignment operators is that they should return a reference to the object itself (`type&`) to support chaining operations like `a = b = 123;` or `(a = 5)++;`. The `return *this;` statement is used for this purpose (#page=30, page=31) [30](#page=30) [31](#page=31).
**Example for `+=` operator:**
```cpp
tijd& tijd::operator+=(const tijd &t) {
sec += t.sec;
min += t.min;
uur += t.uur;
herbereken(); // Assumed member function to handle overflow
return *this;
}
```
This allows for chained operations like `(nu += t) += t;` [31](#page=31).
#### 5.2.4 Overloading increment and decrement operators
Prefix (`++x`) and postfix (`x++`) increment/decrement operators require distinct implementations. The prefix operator typically returns a reference to the modified object (`tijd& operator++()`), while the postfix operator needs to return a copy of the object's state *before* modification to correctly handle cases like `tijd t2 = t++;` or the more complex `(t++)++;` (#page=32, page=33) [32](#page=32) [33](#page=33).
To distinguish between prefix and postfix versions in the function signature, the postfix operator accepts an `int` argument, which is not used but signals its postfix nature [33](#page=33).
**Example for `++` operator:**
```cpp
// Prefix increment
tijd& tijd::operator++() {
sec++;
herbereken(); // Handles time recalculation
return *this;
}
// Postfix increment
tijd tijd::operator++(int) { // int argument distinguishes it as postfix
tijd temp(*this); // Save current state
sec++;
herbereken();
return temp; // Return saved state
}
```
#### 5.2.5 Overloading the array subscript operator `[]`
The array subscript operator `[]` is often overloaded to provide array-like access to class members, especially for classes that manage collections of data or provide indexed access to internal attributes. It typically returns a reference to the element being accessed (`type&`), allowing direct modification of the element, such as `t1 = t1;` or `t1 ++;` [1](#page=1) [2](#page=2) [36](#page=36).
**Example for `[]` operator:**
```cpp
// In class definition
class tijd {
// ...
int& operator[](int);
};
// Implementation
int& tijd::operator[] (int i) {
if (i==0) return uur;
else if (i==1) return min;
else return sec; // Default to seconds if index is not 0 or 1
}
```
### 5.3 Friend operators
When an operator's left-hand operand is not an object of the class, it cannot be implemented as a member function. For example, in `t1 = 2 * t2;`, the integer `2` is the left-hand operand. There are two common approaches to handle this [38](#page=38):
1. **External operator function:** Define a non-member function that only has access to the class's public interface (e.g., getters and setters) [38](#page=38).
2. **Friend operator function:** Declare the operator as a `friend` within the class. This grants the operator direct access to the class's private members [38](#page=38).
**Example using a friend operator:**
```cpp
class tijd {
// ...
friend tijd operator*(int f, const tijd &t);
};
tijd operator*(int f, const tijd &t) {
return tijd(t.uur * f, t.min * f, t.sec * f);
}
```
A more concise approach, if the binary operator is already defined as `tijd operator*(int f) const;` (as `t * f`), is to define the external operator to simply call the member function: `return t * f;`. In this case, the external operator does not need to be a `friend` [39](#page=39).
### 5.4 Overloading iostream operators (`<<` and `>>`)
The stream insertion (`<<`) and extraction (`>>`) operators are used with `cin`, `cout`, `ifstream`, and `ofstream`, which are the left-hand operands. Consequently, they cannot be implemented as member functions of a user-defined class. They are typically implemented as `friend` functions of the class [40](#page=40).
The syntax for these friend functions is:
* `friend istream& operator>>(istream&, A &)` for input [40](#page=40).
* `friend ostream& operator<<(ostream&, const A &)` for output [40](#page=40).
**Example for `<<` and `>>` operators for `tijd`:**
```cpp
class tijd {
// ...
friend ostream& operator<<(ostream&, const tijd &);
friend istream& operator>>(istream&, tijd &);
};
// Output stream operator
ostream& operator<<(ostream &os, const tijd &t) {
os << setw << setfill('0') << t.uur << ':' [2](#page=2).
<< setw << setfill('0') << t.min << ':' [2](#page=2).
<< setw << setfill('0') << t.sec [2](#page=2);
return os;
}
// Input stream operator
istream& operator>>(istream &is, tijd &t) {
is >> t.uur >> t.min >> t.sec;
t.herbereken(); // Assumed member function to handle potential overflow
return is;
}
```
These operators facilitate easy input and output of custom objects to and from streams (#page=40, page=41) [40](#page=40) [41](#page=41).
### 5.5 Operator signatures overview
A table summarizing the return types and `const`-correctness for various overloaded operators:
| Operator | Return Type | Parameter List | Const (Y/N) |
| :-------------- | :--------------- | :------------- | :---------- |
| `+`, `-`, `*`, `/` | `A` | `const A &` | Yes |
| `-` (unary) | `A` | (none) | Yes |
| `==`, `!=`, `<`, `>`, `<=`, `>=` | `bool` | `const A &` | Yes |
| `=`, `+=`, `-=`, `*=`, `/=`, `%=` | `A&` | `const A &` | No |
| `++`, `--` (prefix) | `A&` | (none) | No |
| `++`, `--` (postfix) | `A` | `int` | No |
| `[]` | `type&` | `int` | No |
---
# Klasse-templates
Dit onderwerp introduceert klasse-templates, waarmee generieke klassen kunnen worden gemaakt die met verschillende gegevenstypen kunnen werken [43](#page=43).
### 6.1 Introductie tot klasse-templates
Klasse-templates maken het mogelijk om een blauwdruk voor een klasse te definiëren die kan werken met verschillende gegevenstypen, zonder dat voor elk type een aparte klasse geschreven hoeft te worden. Dit bevordert code-hergebruik en maakt programma's flexibeler [43](#page=43).
### 6.2 Definitie van een klasse-template
Een klasse-template wordt gedefinieerd met het sleutelwoord `template` gevolgd door de template-parameterlijst, meestal met `typename` of `class` om de type-parameter aan te duiden.
* **Template-declaratie:**
```c++
template
class koppel {
// ... klasse-inhoud ...
};
```
Hier is `T` de type-parameter die staat voor een willekeurig gegevenstype [43](#page=43).
* **Gebruik van de template-parameter in de klasse:**
Binnen de klasse-template worden leden zoals data-leden en functies gedefinieerd met behulp van de type-parameter `T`. Bijvoorbeeld, voor data-leden:
```c++
private:
T first, second;
```
Dit betekent dat de data-leden `first` en `second` van het type `T` zullen zijn, wat bepaald wordt bij het instantiëren van de template [43](#page=43).
* **Constructor en member-functies:**
Constructor-declaraties en member-functies binnen de klasse-template declaratie gebruiken ook de type-parameter. Het is belangrijk om **géén** `` toe te voegen na de klassenaam bij het declareren van leden binnen de klasse-declaratie zelf [43](#page=43).
```c++
public:
koppel(const T &, const T &); // géén toevoegen!!
T get_first() const;
koppel operator+(const koppel &) const;
```
### 6.3 Definitie van template-leden buiten de klasse
De definities van member-functies van een klasse-template worden ook gedefinieerd als templates. Elke definitie buiten de klasse-declaratie vereist opnieuw de `template` prefix en een scope resolutie operator (`::`) met de klasse-naam gevolgd door de type-parameters in accolades.
* **Voorbeeld van een constructor-definitie:**
```c++
template
koppel::koppel(const T &_first, const T &_second)
: first(_first), second(_second) {}
```
Merk op dat hier de scope resolutie `koppel::` correct wordt gebruikt [44](#page=44).
* **Voorbeeld van een member-functie-definitie:**
```c++
template
T koppel::get_first() const { return first; }
```
Ook hier is de `template` prefix en de scope resolutie `koppel::` essentieel [44](#page=44).
* **Essentie:** Elke definitie van een lid van een klasse-template is op zichzelf een template en vereist daarom de `template` prefix [44](#page=44).
### 6.4 Gebruik van klasse-templates
Om een object van een klasse-template te creëren, moet het specifieke gegevenstype tussen punthaken (`< >`) worden gespecificeerd bij de naam van de klasse. Dit proces wordt instantiëren genoemd.
* **Instantiëren van een klasse-template:**
```c++
koppel p(9,2);
```
Hier wordt een instantie van de `koppel` klasse gecreëerd waarbij `T` wordt vervangen door `int` [44](#page=44).
* **Gebruik van geïnstantieerde objecten:**
Na het instantiëren kunnen de member-functies op de gebruikelijke manier worden aangeroepen.
```c++
cout << p.get_first(); // Roept get_first() aan op koppel
```
Dit zal de `first` waarde van het `koppel` object `p` retourneren [44](#page=44).
### 6.5 Vriendfuncties met klasse-templates
Vriendfuncties, zoals de output operator `operator<<`, die toegang nodig hebben tot de private leden van een klasse-template, moeten ook als template worden gedeclareerd. De type-parameter voor de vriendfunctie kan dezelfde zijn als die van de klasse, of een andere type-parameter (bijvoorbeeld `U` in het voorbeeld).
* **Declaratie van een vriend-output operator:**
```c++
template
friend ostream& operator<<(ostream&, const koppel &);
```
Dit is **verplicht** voor niet-lidfuncties die als vriend zijn gedeclareerd in een klasse-template om correct te kunnen werken met de specifieke types [43](#page=43).
> **Tip:** Bij het definiëren van leden van een klasse-template buiten de klasse, onthoud dat de scope resolutie operator altijd de volledige template-naam met de type-parameter moet bevatten, zoals `koppel::`.
> **Voorbeeld:** Als je een `koppel` zou willen gebruiken, zou je dat als volgt instantiëren: `koppel d_pair(1.5, 2.7);`. De compiler genereert dan specifiek de code voor een `koppel` die `double` waarden opslaat.
---
## Veelgemaakte fouten om te vermijden
- Bestudeer alle onderwerpen grondig voor examens
- Let op formules en belangrijke definities
- Oefen met de voorbeelden in elke sectie
- Memoriseer niet zonder de onderliggende concepten te begrijpen
Glossary
| Term | Definition |
|------|------------|
| Klasse | Een blauwdruk voor het creëren van objecten, die datastructuren (attributen) en methoden (lidfuncties) combineert in één eenheid. |
| Attribuut | Een variabele die bij een klasse hoort en de staat van een object van die klasse beschrijft. |
| Lidfunctie | Een functie die bij een klasse hoort en de operaties definieert die op de attributen van een object kunnen worden uitgevoerd. |
| Constructor | Een speciale lidfunctie die automatisch wordt aangeroepen wanneer een object van een klasse wordt gecreëerd, met als doel het object correct te initialiseren. |
| Destructor | Een speciale lidfunctie die automatisch wordt aangeroepen wanneer een object wordt vernietigd, vaak gebruikt om resources vrij te geven die door de constructor zijn verkregen. |
| Mutator | Een lidfunctie die de attributen van een object wijzigt. |
| Accessor | Een lidfunctie die de attributen van een object leest zonder ze te wijzigen. |
| `const` | Een sleutelwoord dat aangeeft dat een lidfunctie de attributen van het object niet mag wijzigen. Dit wordt vaak gebruikt bij accessors. |
| Scope-operator (`::`) | Een operator die wordt gebruikt om aan te geven tot welke klasse een functie of variabele behoort, vooral bij het definiëren van lidfuncties buiten de klassedeclaratie. |
| `this` | Een pointer die in een lidfunctie verwijst naar het huidige object waarop de functie wordt aangeroepen. |
| Object | Een instantie van een klasse, met zijn eigen set attributen die zijn waarden bevatten. |
| Initializer List | Een lijst die na de parameterlijst van een constructor komt en wordt gebruikt om attributen te initialiseren voordat het lichaam van de constructor wordt uitgevoerd. |
| Copy-constructor | Een constructor die wordt aangeroepen wanneer een nieuw object wordt geïnitialiseerd als een kopie van een bestaand object. |
| `friend` functie | Een functie die niet een lidfunctie van een klasse is, maar wel directe toegang heeft tot de private en protected leden van die klasse. |
| `friend` klasse | Een klasse waarvan alle lidfuncties directe toegang hebben tot de private en protected leden van een andere klasse. |
| Operator overloading | Het herdefiniëren van bestaande operatoren zodat ze kunnen werken met objecten van zelfgedefinieerde klassen. |
| Unaire operator | Een operator die slechts één operand nodig heeft, zoals `-` (negatie) of `++` (increment). |
| Binaire operator | Een operator die twee operanden nodig heeft, zoals `+` (optelling) of `=` (toekenning). |
| Prefix operator | Een operator die vóór de operand wordt geplaatst (bv. `++x`). |
| Postfix operator | Een operator die na de operand wordt geplaatst (bv. `x++`). |
| Klasse-template | Een blauwdruk voor het creëren van klassen die generiek zijn voor datatypes, waardoor dezelfde klassestructuur kan worden gebruikt met verschillende typen gegevens. |
| `typename` | Een sleutelwoord dat in klasse-templates wordt gebruikt om een datatype aan te duiden. |