Professional Documents
Culture Documents
Obiective:
Suprascrierea metodelor ȋn clasele derivate
Apelul metodelor moştenite/suprascrise
Metode virtuale. Polimorfism
Conceptul de dinamic binding (late/runtime binding)
Apelul metodelor virtuale. Mecanismul v-table
Destructori virtuali
Metode virtuale pure
Clase abstracte
Clase de tip interfaţă
1
Mihai TOGAN
Suprascrierea metodelor ȋn clasele derivate.
Apelul metodelor moştenite/suprascrise (calling overrided methods)
#include <iostream>
using namespace std;
/*****************************************************************************/
class Vehicle
{
protected:
int m_Year;
string m_Color;
public :
Vehicle( const string &color, const int year)
: m_Year(year), m_Color(color) {}
return str;
}
/*****************************************************************************/
class Car : public Vehicle
{
string m_Model;
int m_Power;
public :
Car(const string &color, const int year,
const string &model, const int power)
: Vehicle(color, year), m_Model(model), m_Power (power) {}
return str;
}
//...
};
/*****************************************************************************/
void main ()
{
Car C ("Black", 2006, "Toyota Avensis", 100);
cout << C.getDesc().c_str() << endl;
/*****************************************************************************/
Explicaţie:
În cazul 1, obiectul C este de tip Car. Compilatorul generează cod de apel pentru metoda
din Car.
În cazul 2, pointerul p este de tip Vehicle. Compilatorul generează cod de apel pentru
metoda din Vehicle.
3
Mihai TOGAN
Observaţie: indiferent prin ce tip de date, Car sau Vehicle, accesăm obiectul, se apelează
metoda corectă specifică exact pentru obiectul instanţiat (!!)
Modificăm din nou programul: mai adaugam o clasa nouă Truck, derivată din clasa Vehicle
class Car : public Vehicle
{
//...
}
class Vehicle
{
//...
}
/*****************************************************************************/
void main ()
{
Vehicle *pVehicle;
bool flag;
//...
cin >> flag;
if (flag == true)
pVehicle = new Car ("Black", 2006, "Toyota Avensis", 100);
else
pVehicle = new Truck ("Green", 2009, 30);
//...
cin >> flag;
if (flag == true)
pVehicle = new Car ("Black", 2006, "Toyota Avensis", 100);
else
pVehicle = new Truck ("Green", 2009, 30);
Observaţii:
În acest caz, comportamentul codului este polimorfic: aceeaşi secvenţă de cod se execută
diferit.
Explicaţie: cele două secvenţe de cod executate sunt identice însă în fiecare secvenţă de
cod pointerul pVehicle conţine adrese la obiecte diferite.
5
Mihai TOGAN
Metode virtuale
/**************************************************************************/
class Baza {
//...
public:
void F1() { cout << "\n F1 din clasa baza";}
virtual void F2() { cout << "\n F2 din clasa baza";}
//...
};
pBaza = &B;
pBaza->F1(); // executa Baza::F1
pBaza->F2(); // executa Baza::F2
cout << endl;
pBaza = &D;
pBaza->F1(); // executa Baza::F1
pBaza->F2(); // executa Derivat::F2
cout << endl;
Baza &rBaza = D;
rBaza.F1(); // executa Baza::F1
rBaza.F2(); // executa Derivat::F2
cout << endl;
}
/***************************************************************************/
Observaţie: oriunde se poate folosi un pointer Vehicle * , se poate folosi și un pointer Car * .
6
Mihai TOGAN
Mecanismul v-table (tabela de funcții virtuale)
Fiecare clasă care are declarată o metodă virtuală (care nu era deja virtuală de la o clasă de
bază de mai jos) primeşte automat de la compilator o tabelă de pointeri la funcții: v-table
(__vfptr).
Pentru a inspecta uşor această tabelă am folosit modul de lucru debug din Visual Studio (vezi
figurile următoare).
Secvenţele de cod de mai jos sunt preluate folosind execuţia codului din exemplul anterior.
7
Mihai TOGAN
Secvenţa 2 : pBaza = &D
8
Mihai TOGAN
Secvenţa 3: Baza &rBaza = D;
9
Mihai TOGAN
Destructructori virtuali
void main ()
{
Derivat D;
}
Explicaţia: pointerul pB este de tip Baza, şi din această cauză destructorul clasei derivate nu
mai este apelat de compilator.
Dezavantajul care se crează: nu se realizează distrugerea corectă/completă a obiectului (nu se
face cleanup la nivelul componentei provenite din clasa Derivat).
Soluţia: este necesar ca și destructorul clasei Baza sa fie virtual (implică includerea acestuia
ȋn tabela de funcţii virtuale v-table).
class Baza {
//...
Baza();
virtual ~Baza();
};
10
Mihai TOGAN
class Derivat: public Baza {
//...
Derivat();
~Derivat();
};
void main ()
{
Baza *pB = new Derivat(); // instantierea unui obiect Derivat
//...
delete pB; // distrugerea obiectului Derivat
}
Ȋn plus, dacă se derivează și mai departe, trebuie ca și destructorul lui Derivat sa fie
virtual:
11
Mihai TOGAN
Metode virtuale pure și clase abstracte
class Vehicle
{
protected:
int m_Year;
string m_Color;
public :
Vehicle( const string &color, const int year)
: m_Year(year), m_Color(color) {}
Deşi există clasa Vehicle, să presupunem că ȋn aplicaţie nu vom instanţia obiecte Vehicle.
Presupunem că cerinţele aplicaţiei permit numai obiecte de tip Car, Truck (sau alte tipuri
concrete de vehicule, dacă mai există).
public :
Vehicle( const string &color, const int year)
: m_Year(year), m_Color(color) {}
Clasa Vehicle devine clasă abstractă: clasa nu poate fi instanţiată. Se poate lucra prin
intermediul ei doar cu obiecte ale unor clase concrete moştenite din Vehicle (Car, Truck).
void main ()
{
Vehicle V ("Black", 2006);
//...
};
13
Mihai TOGAN
Folosind acest design la nivelul claselor, cu metodele clasei abstracte din cadrul clasei
Vehicle se poate lucra numai pe obiecte ale unor clase concrete derivate din Vehicle:
void main ()
{
//Vehicle V("Black", 2006);
Vehicle *pVehicle;
bool flag;
//...
cin >> flag;
if (flag == true)
pVehicle = new Car ("Black", 2006, "Toyota Avensis", 100);
else
pVehicle = new Truck ("Green", 2009, 30);
//...
Car C ("White", 2008, "Dacia Logan", 85);
Vehicle &rV = C;
rV.getDesc ();
cout << rV.getColor().c_str();
//...
}
O clasă abstractă este folosită numai ca infrastructură pentru a permite apoi definirea
(prin derivare) a unor sub-clase concrete, sub-clase care au însă ceva ȋn comun.
Observaţie: se poate lucra cu instanţe (obiecte) ale sub-claselor folosind pointeri sau
referinţe declarate de tip clasa de bază abstractă.
O clasă abstractă poate avea una sau mai multe metode virtuale pure.
Dacă o clasă este derivată dintr-o clasă abstractă, ea poate ramâne ȋn continuare abstractă
daca nu implementează toate metodele virtuale pure moştenite din clasa de bază.
Definiție: dacă toate metodele sunt virtuale pure, clasa abstractă se numeşte clasă
abstractă pură. O astfel de clasă este considerată o clasă interfaţă (interface) pentru clasele
concrete derivate din ea.
14
Mihai TOGAN
Conceptul de clasă interfaţă (interface class)
Este un concept OOP foarte clar delimitat la nivelul unora din limbajele OOP existente
(Java, C#).
La nivelul limbajului C++ nu există o definiție clară, separată pentru o clasă de tip
interfaţă. O clasă abstractă având toate metodele virtuale pure poate fi considerată o
interfaţă.
Notă: Interfaţa este un concept OOP și el poate fi gestionat ȋn C++ folosind mecanismele
tehnice puse la dispozitie de limbaj prin intermediul claselor abstracte.
Conform [stackoverflow.com]: The interface were primarily made popular by Java. Below
are the nature of interface and its C++ equivalents:
2. In Java, the interface can contain only static final data members;
The C++ equivalent is static const data members which are compile time constants
3. Multiple interface can be implemented by a Java class, this facility is needed because a
Java class can inherit only 1 class.
The C++ supports multiple inheritance straight away with help of virtual keyword when
needed.
15
Mihai TOGAN
Exemplul 1
class DrawableObject //interface class
{
public:
//draw to GraphicalDrawingBoard
virtual void Draw(GraphicalDrawingBoard&) const = 0;
};
void main ()
{
DrawableList drawableList;
GraphicalDrawingBoard drawingBoard;
drawableList.pushback(new Triangle());
drawableList.pushback(new Rectangle());
drawableList.pushback(new Circle());
Observaţii:
Obiectele construite pot interfaţa cu restul aplicaţiei numai prin metodele expuse de
interfaţă.
Aplicaţia care lucreză cu aceste obiecte nu „cunoaşte” detaliile de implementare sau alte
informaţii specifice claselor care implementează obiectele respective, ci doar o clasă care
expune interfaţa de lucru.
16
Mihai TOGAN
Exemplul 2: refacem exemplul cu clasele Vehicle, astfel încât :
să generăm o clasă de interfaţă (abstractă) pentru orice tip de Vehicle;
să generam o clasa de tip Factory pentru a construi diverse tipuri de Vehicle;
[ivehicle.h]
pragma once
#include <iostream>
/*********************************************************************/
17
Mihai TOGAN
[_ivehicle_factory.cpp]
#include <iostream>
#include "_ivehicle_car_truck.h"
/****************************************************************************/
Vehicle* Factory_Vehicle::Create_CARInstance (const string &color,
const int year, const string &model, const int power)
{
return new _Car (color, year, model, power);
}
/****************************************************************************/
18
Mihai TOGAN
[_ivehicle_car_truck.h]
#pragma once
#include <iostream>
/****************************************************************************/
class _Vehicle : public Vehicle
{
protected:
int m_Year;
string m_Color;
public :
_Vehicle( const string &color, const int year);
/****************************************************************************/
class _Car : public _Vehicle
{
string m_Model;
int m_Power;
public :
_Car(const string &color, const int year,
const string &model, const int power);
/****************************************************************************/
class _Truck : public _Vehicle
{
int m_Tonnage;
public:
_Truck(const string &color, const int year, const int tonnage);
/****************************************************************************/
19
Mihai TOGAN
[_ivehicle_car_truck.cpp]
#pragma once
#include <iostream>
#include "_ivehicle_car_truck.h"
using namespace std;
/****************************************************************************/
_Vehicle::_Vehicle (const string &color, const int year)
: m_Year(year), m_Color(color) {}
/****************************************************************************/
_Car::_Car(const string &color, const int year,
const string &model, const int power)
: _Vehicle(color, year), m_Model(model), m_Power (power) {}
20
Mihai TOGAN
Pasul 2
Construim o aplicaţie care foloseşte biblioteca și instanţiază diverse obiecte de tip Vehicle:
[main.cpp]
#include <iostream>
#include "ivehicle.h"
Vehicle *pVehicle;
bool flag;
//...
cin >> flag;
if (flag == true)
pVehicle = Factory_Vehicle::Create_CARInstance("Black", 2006,
"Toyota Avensis", 100);
else
pVehicle = Factory_Vehicle::Create_TRUCKInstance("Green",
2009, 30);
//...
cin >> flag;
if (flag == true)
pVehicle = Factory_Vehicle::Create_CARInstance ("Black", 2006,
"Toyota Avensis", 100);
else
pVehicle = Factory_Vehicle::Create_TRUCKInstance ("Green",
2009, 30);
//...
Vehicle &rV = *pVehicle;
rV.getDesc ();
cout << rV.getColor().c_str();
}
Observaţii:
Aplicaţia foloseşte pentru compilare doar declaraţiile clasei Vehicle și Factory_Vahicle
(#include "ivehicle.h")