Cover
Zacznij teraz za darmo H5_overerving.pdf
Summary
# Overerving in C++
Overerving in C++ maakt het mogelijk om bestaande klassen uit te breiden met nieuwe functionaliteit, wat resulteert in een hiërarchische structuur van klassen.
### 1.1 Soorten overerving
In C++ zijn er drie hoofdtypen overerving die bepalen hoe leden van een basisklasse toegankelijk zijn in een afgeleide klasse [3](#page=3):
* **Publieke overerving (`public`)**: Dit is de meest gebruikte en aanbevolen vorm van overerving. Het behoudt de "is een"-relatie tussen de basisklasse en de afgeleide klasse, wat betekent dat een object van de afgeleide klasse functioneel uitwisselbaar is met een object van de basisklasse. Publieke leden van de basisklasse blijven publiek in de afgeleide klasse, en protected leden blijven protected. Private leden van de basisklasse zijn ontoegankelijk voor de afgeleide klasse [5](#page=5).
* **Private overerving (`private`)**: Bij private overerving worden publieke en protected leden van de basisklasse private in de afgeleide klasse. Hoewel de afgeleide klasse deze leden kan gebruiken, zijn ze niet toegankelijk voor gebruikers van de afgeleide klasse. Dit wordt niet beschouwd als een echte "is een"-relatie in objectgeoriënteerd programmeren (OGP). Als `public` of `private` niet expliciet wordt vermeld na de dubbele punt (`:`), wordt private overerving verondersteld [4](#page=4).
* **Protected overerving (`protected`)**: Dit type overerving wordt niet gedetailleerd besproken op de opgegeven pagina's. In het algemeen maken protected leden van de basisklasse protected leden in de afgeleide klasse, terwijl publieke leden private worden [3](#page=3).
#### 1.1.1 Publieke overerving in detail
Bij publieke overerving kunnen objecten van de afgeleide klasse de publieke lidfuncties van de basisklasse gebruiken. Een belangrijk aspect is dat de gebruiker van een afgeleid object mag doen wat deze ook met een basisklasseobject zou mogen doen [5](#page=5).
> **Tip:** Gebruik publieke overerving wanneer een "is een"-relatie duidelijk aanwezig is. Vergeet niet `public` expliciet te vermelden na de dubbele punt (`:`) bij het definiëren van de afgeleide klasse [5](#page=5).
#### 1.1.2 Private overerving in detail
Private overerving heeft als gevolg dat de programmeur van de afgeleide klasse wel gebruik kan maken van de publieke leden van de basisklasse, maar gebruikers van de afgeleide klasse deze niet kunnen aanroepen. Objecten van de afgeleide klasse kunnen de publieke lidfuncties van de basisklasse dus niet gebruiken [4](#page=4).
### 1.2 Overerving van leden
#### 1.2.1 Lidfuncties
Publieke en protected lidfuncties van de basisklasse zijn, afhankelijk van het overervingstype, beschikbaar in de afgeleide klasse. Private leden van de basisklasse zijn echter nooit direct toegankelijk voor de afgeleide klasse [12](#page=12) [4](#page=4) [5](#page=5).
#### 1.2.2 Attributen
Net als lidfuncties, worden attributen van de basisklasse ook overgeërfd. Hun toegankelijkheid in de afgeleide klasse hangt af van het overervingstype. Private attributen van de basisklasse zijn niet direct toegankelijk in de afgeleide klasse [12](#page=12).
### 1.3 Constructoren en destructoren in afgeleide klassen
#### 1.3.1 Constructoren
* Constructoren van de basisklasse worden **niet** automatisch overgeërfd [8](#page=8).
* Als de afgeleide klasse zelf geen constructor declareert, wordt er een automatische default-constructor aangemaakt die de default-constructor van de basisklasse aanroept [8](#page=8).
* Hetzelfde geldt voor de copy-constructor: indien deze niet gedeclareerd is in de afgeleide klasse, wordt een default copy-constructor aangemaakt die die van de basisklasse aanroept [8](#page=8).
* De `using`-declaratie (`using Base::Base;`) kan gebruikt worden om constructoren van de basisklasse te "erven". Dit maakt het mogelijk om objecten van de afgeleide klasse te initialiseren met argumenten die bedoeld zijn voor de basisklasse-constructoren [9](#page=9).
* Als de constructor van de afgeleide klasse geen expliciete constructor van de basisklasse aanroept, wordt automatisch de default constructor van de basisklasse aangeroepen [11](#page=11).
* De constructor van de afgeleide klasse kan de constructor van de basisklasse **alleen** aanroepen via de initializer list [11](#page=11).
> **Voorbeeld:**
> ```cpp
> class A {
> public:
> A(int vA = 5);
> int getVarA() const;
> private:
> int varA;
> };
>
> class B : public A {
> public:
> B(int vB = 9);
> B(int vA, int vB);
> int getVarB() const;
> private:
> int varB;
> };
>
> // Implementatie:
> A::A(int vA) : varA(vA) {}
> B::B(int vB) : varB(vB) {}
> // De constructor van B roept expliciet de constructor van A aan
> B::B(int vA, int vB) :
> A(vA), varB(vB) {}
> ```
> Hier is `varA(vA)` in de initializer list van `B::B(int vA, int vB)` cruciaal omdat `varA` een private lid is van klasse `A` en dus niet direct geconfigureerd kan worden vanuit `B` [12](#page=12).
* `using` kan niet gebruikt worden om een specifieke constructor te erven, bijvoorbeeld `using A::A(int);`. Constructoren kunnen echter wel gedelete worden [10](#page=10).
#### 1.3.2 Destructoren
* Wanneer de destructor van de afgeleide klasse wordt aangeroepen, roept deze **automatisch** de destructor van de basisklasse aan [13](#page=13).
* Het is daarom niet nodig om de destructor van de basisklasse expliciet aan te roepen [13](#page=13).
* De destructor in de afgeleide klasse hoeft alleen aandacht te hebben voor extra attributen die specifiek zijn voor de afgeleide klasse. Men kan erop vertrouwen dat de destructor van de basisklasse correct werkt [13](#page=13).
#### 1.3.3 Copy-constructor en toekenningsoperator
* Bij het overschrijven van de copy-constructor in een afgeleide klasse, moet de copy-constructor van de basisklasse ook aangeroepen worden, typisch in de initializer list, gevolgd door het kopiëren van de extra attributen van de afgeleide klasse [14](#page=14).
* De toekenningsoperator (`=`) wordt standaard aangemaakt als deze niet gedeclareerd is. Deze maakt een ondiepe kopie van het rechterlid [15](#page=15).
* Bij het overschrijven van de toekenningsoperator in de afgeleide klasse, dient eerst de toekenningsoperator van de basisklasse aangeroepen te worden (bijvoorbeeld `A::operator=(b);`) om de basisklasse-leden correct toe te kennen, voordat de extra leden van de afgeleide klasse worden toegewezen. De implementatie moet ook controleren op zelf-toekenning (`if (this!= &b)`) [15](#page=15).
### 1.4 Voorbeelden
#### 1.4.1 Voorbeeld van publieke overerving
```cpp
class A {
public:
void setVarA(int i);
int getVarA() const;
private:
int varA;
};
class B : public A { // Publieke overerving
public:
void setVarB(int i);
int getVarB() const;
private:
int varB;
};
// Gebruik:
B b;
b.setVarA; // OK: setVarA is geërfd van A en is publiek toegankelijk [8](#page=8).
b.setVarB; // OK: setVarB is eigen aan klasse B [11](#page=11).
```
Als hier private overerving zou zijn gebruikt (dus `class B: A {... }` of `class B: private A {... }`), zou `b.setVarA;` een compileerfout geven omdat `setVarA` dan niet publiek toegankelijk zou zijn vanuit `B` [6](#page=6) [8](#page=8).
#### 1.4.2 Expliciet aanroepen van basisklasse lidfuncties
In een afgeleide klasse kan het nodig zijn om een lidfunctie uit een bovenliggende klasse expliciet aan te roepen. Dit kan door de naam van de basisklasse gevolgd door de scope-resolutie operator (`::`) te gebruiken [14](#page=14).
```cpp
void B::print() const {
A::print(); // Roept de print() functie van de basisklasse A aan
// ... verdere logica voor B ...
}
```
Dit is anders dan het aanroepen van een methode op een object van de basisklasse, wat zou kunnen met `b.A::print();` als `b` een object van type `B` was en er een publieke functie `print` in `A` zou bestaan [14](#page=14).
---
# Keyword protected en multiple inheritance
Dit onderdeel behandelt het gebruik van het `protected` keyword voor toegangsbeheer in afgeleide klassen en introduceert het concept van multiple inheritance, inclusief de bijbehorende uitdagingen.
### 2.1 Het `protected` keyword
Het `protected` keyword biedt een manier om leden (attributen en lidfuncties) van een klasse toegankelijk te maken binnen de klasse zelf en in afgeleide klassen, zonder deze volledig publiek te maken [17](#page=17).
#### 2.1.1 Functionaliteit van `protected`
* **Binnen de eigen klasse:** Leden gemarkeerd als `protected` hebben dezelfde toegankelijkheid als `private` leden; ze zijn alleen toegankelijk binnen de klasse waarin ze zijn gedefinieerd [17](#page=17).
* **In afgeleide klassen:** `protected` leden van een basisklasse blijven `protected` in een afgeleide klasse. Dit betekent dat ze toegankelijk zijn voor de afgeleide klasse zelf en ook voor klassen die verder van deze afgeleide klasse erven, wat zorgt voor een gecontroleerde doorstroming van leden in de overervingshiërarchie [17](#page=17).
#### 2.1.2 Protected overerving
Protected overerving is een mechanisme waarbij publieke leden van een basisklasse `protected` worden in de afgeleide klasse. Dit wordt zelden gebruikt [17](#page=17).
### 2.2 Multiple inheritance
Multiple inheritance stelt een klasse in staat om te erven van meerdere basisklassen [19](#page=19).
#### 2.2.1 Uitdagingen bij multiple inheritance
Een belangrijk aandachtspunt bij multiple inheritance zijn potentiële dubbelzinnigheden die kunnen ontstaan, met name wanneer klassen een gemeenschappelijke basisklasse delen.
##### 2.2.1.1 Het diamond problem
Het "diamond problem" doet zich voor wanneer twee klassen (A en B) een gemeenschappelijke basisklasse hebben, en een derde klasse (C) tegelijkertijd van zowel A als B erf. In dit scenario kunnen de attributen van de gemeenschappelijke basisklasse dubbel aanwezig zijn in klasse C. Dit leidt tot ambiguïteit over welk attribuut precies bedoeld wordt bij toegang. Om deze ambiguïteit op te lossen, moet expliciet worden aangegeven welke basisklasse het attribuut levert, door gebruik te maken van de scope resolution operator (`::`) [19](#page=19).
**Voorbeeld:**
Stel dat klasse `Basis` gemeenschappelijk is voor `A` en `B`, en `C` ervt van zowel `A` als `B`. Als `Basis` een attribuut `data` heeft, dan kan in `C` toegang tot `Basis::data` nodig zijn om te specificeren of het gaat om het `data` attribuut via `A` of via `B` [19](#page=19).
##### 2.2.1.2 Ambiguïteit bij methodes
Vergelijkbare ambiguïteit kan optreden wanneer twee basisklassen dezelfde methode definiëren, en deze methode niet wordt overschreven in de afgeleide klasse. Bij het aanroepen van zo'n methode op een object van de afgeleide klasse, is het noodzakelijk om met behulp van de scope resolution operator (`::`) te specificeren welke van de twee methoden uit de basisklassen bedoeld wordt [19](#page=19).
**Voorbeeld:**
Als zowel klasse `A` als klasse `B` een methode `toon()` hebben, en klasse `C` erft van `A` en `B` zonder `toon()` te overschrijven, dan moet bij het aanroepen van `toon()` op een `C`-object duidelijk gemaakt worden of `A::toon()` of `B::toon()` wordt bedoeld [19](#page=19).
---
# Polymorfisme en dynamic binding
Dit onderwerp verkent de principes van polymorfisme en dynamic binding in C++, met de nadruk op de vereisten voor publieke overerving en het gebruik van pointers of referenties, en benadrukt het belang van virtuele functies en destructoren [21](#page=21).
### 3.1 Wat is polymorfisme?
Polymorfisme, wat "veel vormen" betekent, stelt ons in staat om objecten van verschillende, maar gerelateerde klassen op een uniforme manier te behandelen. Dit wordt bereikt door gebruik te maken van objecten van de basisklasse die verwijzen naar objecten van afgeleide klassen [21](#page=21).
### 3.2 Vereisten voor polymorfisme
Om polymorfisme te realiseren, zijn de volgende voorwaarden cruciaal:
* **Publieke overerving:** Een afgeleide klasse moet publiek overerven van een basisklasse. Dit zorgt voor een "is-een" relatie [21](#page=21).
* **Pointers of referenties:** Polymorfisme is alleen mogelijk wanneer er gebruik wordt gemaakt van pointers of referenties naar objecten van de basisklasse die wijzen naar objecten van afgeleide klassen. Een direct object van de basisklasse kan niet op deze manier worden behandeld [21](#page=21).
#### 3.2.1 Voorbeeld: Klassehiërarchie
Beschouw de volgende klassehiërarchie:
* **`persoon`**: De basisklasse.
* **`student`**: Erft publiek van `persoon`.
* **`leerkracht`**: Erft publiek van `persoon`.
**Klasse `persoon` (voorbeeld):**
```cpp
class persoon {
private:
string naam;
public:
persoon(const string & = "?");
void print() const; // Wordt later virtueel gemaakt
};
persoon::persoon(const string &nm) : naam(nm) {}
void persoon::print() const {
cout << naam << " ";
}
```
**Klasse `student` (voorbeeld):**
```cpp
class student : public persoon {
private:
string klascode;
public:
student(const string & = "?", const string & = "?");
void print() const; // Overschrijft persoon::print
};
student::student(const string &nm, const string &kl) :
persoon(nm), klascode(kl) {}
void student::print() const {
persoon::print();
cout << "zit in klas " << klascode << endl;
}
```
**Klasse `leerkracht` (voorbeeld):**
```cpp
class leerkracht : public persoon {
private:
string vakgroep;
public:
leerkracht(const string & = "?", const string & = "?");
void print() const; // Overschrijft persoon::print
};
leerkracht::leerkracht(const string &nm, const string &vg)
: persoon(nm),vakgroep(vg) {}
void leerkracht::print() const {
persoon::print();
cout << "uit vakgroep " << vakgroep << endl;
}
```
#### 3.2.2 Demonstatie van NIET-polymorfisme met objecten
Wanneer we directe objecten gebruiken en toewijzen, treedt er geen polymorfisme op. De compiler handelt de toewijzing op basis van het type van het object.
```cpp
int main() {
persoon p("Peter");
p.print(); cout << endl; // Output: Peter
student s("Silke","1Ba3");
s.print(); // Output: Silke zit in klas 1Ba3
leerkracht l("Marc","EA20");
l.print(); // Output: Marc uit vakgroep EA20
// Toewijzing van een leerkracht object aan een persoon object
p = l;
p.print(); // Output: Marc (alleen het persoon-deel wordt behouden)
// De volgende toewijzingen resulteren in compileerfouten of
// ongewenst gedrag omdat het type niet overeenkomt.
// l = p;
// l = (leerkracht) p;
// Conclusie: hier is GEEN sprake van polymorfisme!
return 0;
}
```
#### 3.2.3 Demonstratie van polymorfisme met pointers en referenties
Met pointers en referenties gedraagt het systeem zich anders:
```cpp
int main() {
// Referentie naar een student object
student s("Silke","1Ba3");
persoon &rp = s;
rp.print(); // Output: Silke zit in klas 1Ba3
// Pointer naar een leerkracht object
persoon *pp = new leerkracht("Els","Talen");
(*pp).print(); // Output: Els uit vakgroep Talen
// Unique pointer naar een student object
unique_ptr upp = make_unique("An","Ma");
upp->print(); // Output: An zit in klas Ma
delete pp;
// Conclusie: er is GEEN dynamic binding, de lidfuncties van de basisklasse worden gebruikt.
// Dit is een cruciaal punt voor het begrip van dynamic binding.
return 0;
}
```
### 3.3 Dynamic binding
Dynamic binding (ook wel late binding genoemd) is het mechanisme dat ervoor zorgt dat de juiste (overschreven) lidfunctie van een afgeleide klasse wordt aangeroepen wanneer een functie wordt aangeroepen via een pointer of referentie naar de basisklasse [27](#page=27).
#### 3.3.1 Vereisten voor dynamic binding
Voor succesvolle dynamic binding moet aan de volgende voorwaarden worden voldaan:
* **Virtuele functies in de basisklasse:** De lidfunctie in de basisklasse moet gedeclareerd zijn als `virtual`. Dit is de sleutel tot dynamic binding [27](#page=27).
* **Overschrijven in de afgeleide klasse:** De corresponderende lidfunctie in de afgeleide klasse kan, maar hoeft niet, ook als `virtual` gemarkeerd te worden. Het is voldoende dat de functie correct wordt overschreven [27](#page=27).
**Toepassing van `virtual`:**
```cpp
class persoon {
private:
string naam;
public:
persoon(const string & = "?");
virtual void print() const; // Declaratie als virtual
};
```
#### 3.3.2 Belang van virtual functies en destructoren
* **Polymorfisme en dynamic binding:** Gebruik pointers (vooral `unique_ptr` voor geheugenbeheer) om polymorfisme te realiseren. Maak lidfuncties in de basisklasse `virtual` om dynamic binding te ondersteunen en overschrijf deze in afgeleide klassen [28](#page=28).
* **Operator `<<`:** Om de `<<`-operator dynamisch te laten binden, kan een `protected virtual print`-methode met een `ostream&` parameter in de basisklasse worden voorzien, samen met een `friend operator<<` die deze methode aanroept. De `print`-methode in afgeleide klassen moet dan worden overschreven en als `private` gemarkeerd [28](#page=28).
#### 3.3.3 Nadelen van virtual functies
Hoewel virtuele functies cruciaal zijn voor dynamic binding, hebben ze een prestatie-impact. Er is een overhead omdat het runtime moet worden onderzocht of de virtuele lidfunctie is overschreven in de afgeleide klasse. Niet-virtuele lidfuncties kunnen vaak inline worden gecompileerd en zijn daardoor sneller [29](#page=29).
#### 3.3.4 Het belang van virtual destructoren
Een cruciaal aspect van polymorfisme is het correct beheren van geheugen, met name bij het verwijderen van objecten via pointers naar de basisklasse.
* **Probleem zonder virtual destructor:** Als de destructor van de basisklasse niet virtueel is, wordt bij het verwijderen van een afgeleid object via een pointer naar de basisklasse, **enkel de destructor van de basisklasse opgeroepen**. Dit kan leiden tot geheugenlekken als de afgeleide klasse resources beheert [30](#page=30).
* **Oplossing:** Maak de destructor van de basisklasse `virtual`. Dit zorgt ervoor dat de destructoren van zowel de afgeleide klasse als de basisklasse correct worden aangeroepen in de juiste volgorde [30](#page=30).
* **Goede methodologie:** Het wordt sterk aanbevolen om de destructor altijd als `virtual` te declareren, zelfs als deze leeg is. Dit is een gangbare en goede praktijk, vooral op examens [30](#page=30).
**Voorbeeld (conceptueel):**
Stel dat je een `persoon* ptr` hebt die naar een `student` object wijst.
* **Zonder `virtual` destructor:** `delete ptr;` roept alleen `~persoon()` aan.
* **Met `virtual` destructor:** `delete ptr;` roept eerst `~student()` en daarna `~persoon()` aan.
---
# Abstracte klassen
Abstracte klassen zijn een fundamenteel concept in objectgeoriënteerd programmeren dat de implementatie van interfaces definieert zonder deze volledig uit te werken, waardoor ze dienen als blauwdrukken voor afgeleide klassen [32](#page=32).
### 4.1 Definitie van abstracte klassen
Een klasse wordt geclassificeerd als abstract onder de volgende voorwaarden [32](#page=32):
* De klasse bevat minstens één **puur virtuele lidfunctie**.
* Een puur virtuele lidfunctie wordt gekenmerkt door het feit dat deze geen body heeft en expliciet wordt gedeclareerd met een gelijkteken gevolgd door nul (`= 0`) in de klassedefinitie. De syntax hiervoor is `virtual declaratie_lidfunctie = 0;` [32](#page=32).
### 4.2 Kenmerken en beperkingen
Een cruciale eigenschap van abstracte klassen is dat er **geen objecten van aangemaakt kunnen worden**. Dit betekent dat men geen instanties van een abstracte klasse kan creëren, omdat de klasse niet volledig geïmplementeerd is (vanwege de puur virtuele functies die een body vereisen in een concrete afgeleide klasse) [32](#page=32).
> **Tip:** Abstracte klassen worden voornamelijk gebruikt om een gemeenschappelijke interface te specificeren voor een set gerelateerde klassen. Afgeleide klassen moeten alle puur virtuele functies implementeren om concreet te worden en om objecten van te kunnen maken.
### 4.3 Voorbeeld van een abstracte klasse
Een illustratief voorbeeld van een abstracte klasse is de `figuur` klasse:
> **Example:**
> ```cpp
> class figuur {
> public:
> virtual void draw() const = 0;
> // ... andere leden ...
> };
>
> figuur f; // Dit zal een compilatie-error veroorzaken
> ```
In dit voorbeeld is `draw()` een puur virtuele lidfunctie (`= 0`). Hierdoor is `figuur` een abstracte klasse. Pogingen om een object `f` van het type `figuur` aan te maken, zoals getoond, zullen resulteren in een compileerfout, omdat abstracte klassen niet geïnstantieerd kunnen worden. Afgeleide klassen, zoals `Cirkel` of `Rechthoek`, zouden deze `draw()` functie moeten implementeren om zelf concreet te worden [32](#page=32).
---
## 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 |
|------|------------|
| Overerving | Een mechanisme waarbij een nieuwe klasse (afgeleide klasse) eigenschappen en gedrag overneemt van een bestaande klasse (basisklasse). Dit bevordert codehergebruik en het modelleren van hiërarchische relaties. |
| Publieke overerving | Een vorm van overerving waarbij publieke leden van de basisklasse publiek blijven in de afgeleide klasse, en protected leden protected blijven. Dit behoudt de is-een relatie en maakt de functionaliteit van de basisklasse breed toegankelijk voor gebruikers van de afgeleide klasse. |
| Private overerving | Een vorm van overerving waarbij publieke en protected leden van de basisklasse private worden in de afgeleide klasse. Dit beperkt de toegang tot de leden van de basisklasse, waardoor de is-een relatie minder direct wordt benut door externe gebruikers. |
| Protected | Een toegangsmodificator die leden binnen de klasse zelf en in afgeleide klassen toegankelijk maakt, maar niet voor externe gebruikers. Dit helpt bij het beheren van toegang voor verdere extensies van de klassehiërarchie. |
| Constructor | Een speciale lidfunctie die automatisch wordt aangeroepen bij het creëren van een object van een klasse. Deze functie initialiseert de attributen van het object. |
| Destructor | Een speciale lidfunctie die automatisch wordt aangeroepen wanneer een object van een klasse wordt vernietigd. Deze functie voert opruimacties uit, zoals het vrijgeven van gereserveerd geheugen. |
| Multiple inheritance | Het vermogen van een klasse om eigenschappen en gedrag van twee of meer basisklassen te erven. Dit kan leiden tot complexiteit, zoals dubbelzinnigheden in de naamgeving van leden. |
| Polymorfisme | Het vermogen van objecten van verschillende klassen om te reageren op dezelfde methode-aanroep op een manier die specifiek is voor hun type. Dit wordt vaak bereikt met behulp van virtuele functies en pointers of referenties. |
| Dynamic binding | Een mechanisme waarbij de specifieke implementatie van een methode die wordt aangeroepen, pas tijdens runtime wordt bepaald. Dit is essentieel voor polymorfisme en wordt doorgaans geactiveerd door virtuele functies. |
| Abstracte klasse | Een klasse die niet direct geïnstantieerd kan worden omdat deze een of meer puur virtuele functies bevat. Abstracte klassen dienen als basis voor andere klassen en definiëren een interface. |
| Virtual functie | Een lidfunctie in een basisklasse die is gedeclareerd met het keyword `virtual`. Dit maakt dynamische binding mogelijk, zodat de correcte implementatie van de functie tijdens runtime wordt aangeroepen, afhankelijk van het objecttype. |
| Diamond problem | Een ambiguïiteitsprobleem dat optreedt bij multiple inheritance wanneer twee klassen een gemeenschappelijke basisklasse hebben. Als een afgeleide klasse beide klassen erft, kunnen de leden van de gemeenschappelijke basisklasse dubbel aanwezig zijn. |