Metode virtuale

In ceea ce priveste mostenirea trasaturilor, pana acum numai obiectele de tip Dog mosteneau atributete (starea) si capabilitatile (metodele) clasei de baza. In C++ relatia is (este din categorie sau de tipul) merge mult mai departe.

C++ is extinde polimorfismul pentru a permite pointerilor catre clasele de baza sa pointeze obiecte din clasele derivate. Astfel, se poate scrie

Mammal* pMammal = new Dog;

Acest cod creaza pe heap un nou obiect de tip Dog si intoarce o valoare de tip pointer catre un obiect din clasa de baza (aici mammal). Aceasta este corect, deoarece cainele este un mamifer (mai precis clasa Dog este derivata din clasa Mammal).

Nota. In aceasta consta esenta polimorfismului. Spre exemplu, se pot crea multe tipuri de ferestre, inclusiv ferestre de dialog, ferestre ce permit defilarea dotand pe fiecare dintre ele cu o metoda draw(). Creand un pointer catre o fereastra si atribuind valori ce pointeaza catre ferestre de dialog sau alt tip derivat, sepoate apoi apela functia draw() asupra acestui obiect fara a face referinta la tipul exact al obiectului implicat. Intotdeauna functia corecta va fi apelata.

Se poate folosi acest pointer pentru a invoca orice metoda a obiectului Mammal. Dar cum putem sa apelam acele metode care sunt suprapuse in clasa derivata Dog? Functiile virtuale permit acest lucru. Programul de mai jos ilustreaza ce se intampla cu metodele nevirtuale in acest context.

//Folosirea metodelor virtuale
#include <iostream.h>

class Mammal
{
public:
Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }
~Mammal() { cout << "Mammal destructor...\n"; }
void Move() const { cout << "Mammal move one step\n"; }
virtual void Speak() const { cout << "Mammal speak!\n"; }
protected:
int itsAge;
};

class Dog : public Mammal
{
public:
Dog() { cout << "Dog Constructor...\n"; }
~Dog() { cout << "Dog destructor...\n"; }
void WagTail() { cout << "Wagging Tail...\n"; }
void Speak()const { cout << "Woof!\n"; }
void Move()const { cout << "Dog moves 5 steps...\n"; }
};

int main()
{

Mammal *pDog = new Dog;
pDog->Move();
pDog->Speak();
return 0;
}

In urma rularii acestui cod pe ecranul terminalului obtinem

Mammal constructor...
Dog Constructor...
Mammal move one step
Woof!

Un exemplu in care se exemplifica folosirea destructorilor virtuali este dat in continuare (virtmet2.cpp)

//metode virtuale 2
#include <iostream.h>

class Mammal
{
public:
Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }
virtual ~Mammal() { cout << "Mammal destructor...\n"; }
virtual void Move() const { cout << "Mammal move one step\n"; }
//virtual
void Speak() const { cout << "Mammal speak!\n"; }
protected:
int itsAge;
};

class Dog : public Mammal
{
public:
Dog() { cout << "Dog Constructor...\n"; }
~Dog() { cout << "Dog destructor...\n"; }
void WagTail() { cout << "Wagging Tail...\n"; }
void Speak()const { cout << "Ham!\n"; }
void Move()const { cout << "Dog moves 5 steps...\n"; }
};

class CatDog : public Dog
{
public:
CatDog() { cout << "pisi constructor...\n"; }
~CatDog() { cout << "pisi destructor...\n"; }
void WagTail() { cout << "Wagging Tail...\n"; }
void Speak()const { cout << "Miau!\n"; }
void Move()const { cout << "Pisica sare...\n"; }
};

int main()
{

Mammal *pDog = new Dog;
CatDog pisi;
Mammal *pPisi = new CatDog;
pDog->Move();
pDog->Speak();
pisi.Speak();
pPisi->Speak();
delete pPisi;
return 0;
}

care genereaza la iesire urmatorul rezultat

Mammal constructor...
Dog Constructor...
Mammal constructor...
Dog Constructor...
pisi constructor...
Mammal constructor...
Dog Constructor...
pisi constructor...
Dog moves 5 steps...
Mammal speak!
Miau!
Mammal speak!

dupa delete pPisi
pisi destructor...
Dog destructor...
Mammal destructor...
pisi destructor...
Dog destructor...
Mammal destructor...

Cum se modifica rezultatul daca se face virtuala si metoda Speak() a obiectului Mammal?

Cornel Mironel Niculae, 2003-2004

11-Mar-2004