C++-Programmeren Sheet 5 Virtuele methoden



Dovnload 53.3 Kb.
Datum27.08.2016
Grootte53.3 Kb.

C++-Programmeren Sheet 5-

Virtuele methoden

Zowel in Java als C++ kunnen methoden in een basis klasse worden overschreven in een afgeleide klasse.

Er bestaat echter een groot verschil tussen de manier waarop in Java en C++ wordt bepaald welke van deze methoden moet worden aangeroepen. Dit heet binding.
In Java wordt dit altijd tijdens de uitvoering van het programma bepaald (dynamic binding).
In C++ gebeurt het standaard door de compiler (static binding). Het gevolg is dat het programma efficiënter werkt.

Helaas werkt static binding niet altijd correct.


class Person {

public:

// Constructor.

Person (const char * name);
// Destructor.

~Person (void);
// I/O friend.

friend ostream &

operator<< (ostream & os, const Person & p);
protected:

char * name;

};
De klasse Person heeft een afgeleide klasse Student.


class Student : public Person {

public:

// Constructor.

Student (const char * n, const float * m, int nr);
// Destructor

~Student (void);
// I/O friend.

friend ostream &

operator<< (ostream & os, const Student & s);
private:

// Private data.

int nrMarks;

float * marks;

};

Een eenvoudig testprogramma:


int main (void)

{

Person m ("Marten Wensink");

float cijfers [5] = {6.4f, 7.5f, 4.2f};

Student s ("D.E.Student", cijfers, 3);

cout << m << '\n' << s << '\n' << endl;
cout << "********* Testing references. ********\n";

Person & r0 = m;

Person & r1 = s;
// Display using references:

cout << r0 << endl;

cout << r1 << endl;

cout << endl;
cout << "********* Testing pointers. **********\n";

Person * p[2];

p[0] = new Person ("Marten Wensink");

p[1] = new Student ("D.E.Student", cijfers, 3);
// Display using pointers:

for (int i = 0; i < 2; i++)

cout << *p[i] << endl;

cout << endl;
cout << "*** Testing destructors for array. ***\n";

for (i = 0; i < 2; i++)

{ delete p[i];

cout << endl;

}
cout << "Finalising program...\n";

return 0;

}

Het resultaat is als volgt:


Person: Marten Wensink

Student: D.E.Student

Cijfers: 6.4 7.5 4.2
************ Testing references. ***********

Person: Marten Wensink

Person: D.E.Student
************* Testing pointers. ************

Person: Marten Wensink

Person: D.E.Student
****** Testing destructors for array. ******

Calling destructor for Person.


Calling destructor for Person.
Finalising program...

Calling destructor for Student.

Calling destructor for Person.

Calling destructor for Person.



Conclusie:
- bij ri en p[i] (i{0,1}) worden Person- en geen Student-objecten afgedrukt
- bij de vernietiging van het object aangewezen door p[i] wordt een Person- en geen Student-object vernietigd
m.a.w.:
1. er is een geheugenlek
2. de uitvoer is niet naar wens

Omdat de compiler bepaalt welke methoden worden aangeroepen, worden operator<< en de destructor van Person gekozen, want ri en p[i] zijn resp. een referentie en een pointer n een Person.



Oplossing:
Maak gebruik van dynamic binding. Daarvoor is (helaas) de programmeur verantwoordelijk.
Dynamic binding moet per methode worden afgedwongen.

Dit gebeurt door in de basis klasse voor de betreffende methode het woord virtual te zetten.


Methoden uit de afgeleide klassen met dezelfde signatuur en hetzelfde return-type zijn dan automatisch virtual.

In geval van het voorbeeld moet voor de destructor van Person het woord virtual staan. De destructoren van de afgeleide klassen zijn dan automatisch virtual.

Daarmee is het geheugenlek verholpen.

Voor de uitvoer-operator zit er een adder onder het gras:


Omdat een friend-functie geen methode van een klasse is, kan een friend-functie NIET virtual zijn !
In dit geval is er een andere oplossing:

- definieer alleen de uitvoer-operator als friend-functie in de

basis klasse

- laat deze een virtuele methode aanroepen die het feitelijke

werk doet (de methode kan private of protected zijn)

- de virtuele methode moet zonodig in een afgeleide klasse worden overschreven



Modificatie van klasse Person:

class Person {

public:

// Destructor (should always be virtual !!).

virtual ~Person (void);
// I/O friend.

friend ostream &

operator<< (ostream & os, const Person & p)

{ p.printInfo(os); return os; }
private:

// Private virtual function.

virtual void printInfo (ostream & os) const;

};

Abstracte klassen
- is bedoeld als basis voor een aantal afgeleide klassen, om zo gemeenschappelijke functionaliteit vast te leggen
- kan niet worden geïnstantieerd (er kunnen geen objecten van worden gemaakt)

C++ kent geen apart gereseveerd woord om een abstracte klasse te definiëren.


Iedere klasse waarin minimaal één methode voorkomt die virtual is en waarvan de body bestaat uit de tekst = 0; is abstract.
Een dergelijke methode heet pure virtual (puur virtueel).
In een afgeleide klasse moet deze methode worden overschreven, anders is de afgeleide klasse ook abstract.
Evenals in Java, kunnen abstracte klassen attributen bevatten.

C++ kent geen equivalent voor een interface zoals in Java.

Dit kan worden nagebootst door een klasse te definiëren met uitsluitend puur virtuele methoden.

Als voorbeeld een aantal klassen voor de representatie van vervoermiddelen.




class Vehicle {

public:

// Virtual destructor

virtual ~Vehicle (void) { }
// I/O-friend

friend ostream &

operator<< (ostream & os, const Vehicle & v)

{ v.printInfo (os); return os; }
private:

// Pure virtual operation

virtual void printInfo (ostream & os) const = 0;

};
De afgeleide klasse Bike is eveneens abstract omdat de methode printInfo() niet wordt overschreven.

class Bike : public Vehicle {
// No extra operations.
// No data.

};
class MotorVehicle : public Vehicle {

public:

// Constructor

MotorVehicle (const char * num)

{ regNum = new string (num); }
// Destructor

~MotorVehicle (void)

{ cout << "Destruction of " << *regNum << endl;

delete regNum;

}
// Public operations.

const string & number (void) const

{ return *regNum; }
protected:

string * regNum;

private:

void printInfo (ostream & os) const;

};
class PrivateCar : public MotorVehicle {

public:

// Constructor.

PrivateCar (const char * num, int n) :

MotorVehicle (num), numSeats (n) { }
protected:

int numSeats;

private:

void printInfo (ostream & os) const;

};
class RaceBike : public Bike {

public:

// Constructor.

RaceBike (int n) : numGears (n) { }
protected:

int numGears;

private:

void printInfo (ostream & os) const;

};

De implementatie van de uitvoer methoden:
void MotorVehicle::printInfo (ostream & os) const

{

os << "A motor vehicle with reg.no: " << number();

}
void PrivateCar::printInfo (ostream & os) const

{

os << "A private car with reg.no: " << number()

<< " and " << numSeats << " seats";

}
void RaceBike::printInfo (ostream & os) const

{

os << "A race bike with " << numGears << " gears";

}

Een simpel testje:
int main (void)

{

MotorVehicle m ("MW-100");

cout << m << endl;

Vehicle * pM = new MotorVehicle ("MW-101");

cout << *pM << endl;

Vehicle & vr = m;

cout << vr << endl << endl;
PrivateCar c ("MW-200", 4);

cout << c << endl;

Vehicle * pC = new PrivateCar ("MW-201", 5);

cout << *pC << endl;

MotorVehicle * pPC = new PrivateCar ("MW-202", 6);

cout << *pPC << endl << endl;

RaceBike rb (10);

cout << rb << endl;

Bike * pRB = new RaceBike (14);

cout << *pRB << endl;

Vehicle * pV = new RaceBike (16);

cout << *pV << endl;
cout << "\n********** Deleting objects. *******\n";

delete pM;

delete pC;

delete pPC;

delete pRB;

delete pV;
cout << "\n******** Finalising program. *******\n";

}
De uitvoer:
A motor vehicle with reg.no: MW-100

A motor vehicle with reg.no: MW-101

A motor vehicle with reg.no: MW-100
A private car with reg.no MW-200 and 4 seats

A private car with reg.no MW-201 and 5 seats

A private car with reg.no MW-202 and 6 seats
A race bike with 10 gears

A race bike with 14 gears

A race bike with 16 gears
************* Deleting objects. ************

Destruction of MW-101

Destruction of MW-201

Destruction of MW-202
************ Finalising program. ***********

Destruction of MW-200

Destruction of MW-100



De database wordt beschermd door het auteursrecht ©opleid.info 2017
stuur bericht

    Hoofdpagina