1. Introduction au C++
sommaire chapitre 1
1.8. Méthodes virtuelles : classes abstraites, interfaces
1.8.1. Méthode virtuelle
Une méthode virtuelle est une méthode dont l'implantation peut
être adaptée/redéfinie dans une classe héritée.
Elle est déclarée en préfixant la méthode par le mot clé virtual
Sur l'exemple suivant on déclare un classe Person, puis une classe Student qui
hérite de Person. La méthode d'affichage est virtuelle car elle va dépendre de
la classe. Note : on réutilise la partie liée à l'affichage de Person.
#include <iostream>
#include <string>
using namespace std;
/**
* Base class
*/
class Person {
protected:
string name;
int age;
public:
// default constructor
Person() {
name = "";
age = 0;
}
// constructor with 2 arguments
Person(string n, int a) {
name = n;
age = a;
}
// display class
virtual ostream& print(ostream& out) {
out << name << ", " << age;
return out;
}
friend ostream& operator<<(ostream& out, Person& p) {
return p.print(out);
}
};
/**
* Inherited class with redefinition of virtual method print
*/
class Student : public Person {
protected:
string level;
public:
Student(string n, int a, string l) : Person(n, a), level(l) {
}
ostream& print(ostream& out) {
Person::print(out);
out << ", level = " << level;
return out;
}
};
int main() {
Person p("toto", 10);
Student s("john", 20, "l3 informatique");
cout << p << endl;
cout << s << endl;
return 0;
}
toto, 10
john, 20, level = l3 informatique
1.8.2. Méthode virtuelle pure : classe abstraite
Une méthode virtuelle pure est une méthode virtuelle sans corps (code).
Elle est déclarée en préfixant la méthode par le mot clé virtual
et en la suffixant par = 0;
Une classe qui possède une méthode virtuelle pure ne peut pas être instanciée ce qui permet
de déclarer une classe mère dite abstraite dont le but sera de définir les méthodes à
implanter dans les classes filles.
class Abstraite {
public:
Abstraite() { }
virtual void methode() = 0;
};
int main() {
// not possible
//Abstraite a;
// possible
Abstraite *a;
// not possible
//a = new Abstraite();
return 0;
}
1.8.3. Interface
Une interface représente un ensemble de méthodes dont dispose une classe.
Par définition : une interface dispose uniquement de méthodes et pas de données membres, mais en C++ on peut inclure des données membres. Remarque : ce n'est pas le cas en Java.
On dit qu'une classe implémente/implante une interface.
En C++, la notion d'interface n'existe pas de manière native puisque seule la classe permet
de déclarer des méthodes héritées mais également des données membres. C'est donc
au programmeur qu'est laissée la responsabilité de créer des interfaces en s'assurant
de ne pas ajouter de données membres à la classe qu'il veut faire apparaître comme
interface.
Deux classes qui implantent la même interface peuvent donc être manipulées de la
même manière.
Sur l'exemple suivant on crée une interface pour une pile et deux implémentations différentes :
- une implémentation basée sur un tableau d'entiers
- une implémentation basée sur un vector<int>
On montre alors comment manipuler les deux piles au travers de l'interface pour s'affranchir de l'implémentation.
#include <iostream>
#include <vector>
using namespace std;
/**
* interface of a stack
*/
class IntegerStackInterface {
public:
// add value on top
virtual void push(int value) = 0;
// remove value on top
virtual void pop() = 0;
// return value on top
virtual int top() = 0;
// tells whether stack is empty
virtual bool is_empty() = 0;
};
/**
* Implementation as a C array
*/
class IntegerStackAsArray : public IntegerStackInterface {
protected:
int max_elements;
int index;
int *elements;
public:
IntegerStackAsArray(int sz=10) {
index = -1;
elements = new int [max_elements = sz];
}
~IntegerStackAsArray() {
delete [] elements;
}
void push(int value) {
elements[++index] = value;
}
void pop() {
--index;
}
int top() {
return elements[index];
}
bool is_empty() {
return (index == -1) ? true : false;
}
};
/**
* Implementation as a STL vector
*/
class IntegerStackAsVector : public IntegerStackInterface {
protected:
int max_elements;
vector<int> v;
public:
IntegerStackAsVector(int sz=10) {
max_elements = sz;
}
~IntegerStackAsVector() {
}
void push(int value) {
v.push_back(value);
}
void pop() {
v.pop_back();
}
int top() {
return v[v.size()-1];
}
bool is_empty() {
return (v.size() == 0) ? true : false;
}
};
/**
* treatment using pointer access
* add values 1, 2, ..., 10
* then compute sum by poping them
*/
void process_ptr(IntegerStackInterface *s) {
for (int i=1; i<=10; ++i) {
s->push(i);
}
int sum = 0;
while (!s->is_empty()) {
sum += s->top();
s->pop();
}
cout << "sum = " << sum << endl;
}
/**
* treatment using reference access
* add values 1, 2, ..., 10
* then compute sum by poping them
*/
void process_ref(IntegerStackInterface& s) {
for (int i=1; i<=10; ++i) {
s.push(i);
}
int sum = 0;
while (!s.is_empty()) {
sum += s.top();
s.pop();
}
cout << "sum = " << sum << endl;
}
int main() {
IntegerStackAsArray s1;
IntegerStackAsVector s2;
process_ptr(&s1);
process_ptr(&s2);
process_ref(s1);
process_ref(s2);
}