La programmation orientée objet en C++ repose sur trois concepts clés : l'encapsulation, l'héritage et le polymorphisme. Le C++ considère que tout peut être modélisé comme un objet, chaque objet possédant des propriétés et des comportements.
Principes de l'encapsulation
L'encapsulation combine les attributs et les méthodes en une entité cohérente représentant un concept réel, tout en contrôlant l'accès via des niveaux de visibilité.
Exemple : Classe cercle avec calcul de périmètre
#include <iostream>
using namespace std;
const double PI_VALUE = 3.14159;
class Cercle {
private:
int rayon;
public:
void definirRayon(int r) {
rayon = r;
}
double obtenirPerimetre() {
return 2 * PI_VALUE * rayon;
}
};
int main() {
Cercle monCercle;
monCercle.definirRayon(10);
cout << "Périmètre : " << monCercle.obtenirPerimetre() << endl;
return 0;
}
Contrôle d'accès
Trois niveaux de visibilité existent :
- public : accessible depuis l'intérieur et l'extérieur de la classe
- protected : accessible dans la classe et ses classes dérivées
- private : accessible uniquement dans la classe elle-même
#include <iostream>
#include <string>
using namespace std;
class Personne {
public:
string nom;
protected:
string vehicule;
private:
int motDePasse;
public:
void afficher() {
cout << "Nom: " << nom << endl;
}
};
int main() {
Personne p;
p.nom = "Alice"; // Valide
// p.vehicule = "Voiture"; // Erreur : protected
// p.motDePasse = 123; // Erreur : private
return 0;
}
Différence entre struct et class
La seule différence réside dans la visibilité par défaut :
struct: membres publics par défautclass: membres privés par défaut
Avantages des attributs privés
#include <iostream>
using namespace std;
class Cube {
private:
int longueur, largeur, hauteur;
public:
void definirDimensions(int l, int w, int h) {
longueur = l;
largeur = w;
hauteur = h;
}
int calculerVolume() {
return longueur * largeur * hauteur;
}
int calculerSurface() {
return 2 * (longueur*largeur + largeur*hauteur + longueur*hauteur);
}
bool egal(const Cube& autre) {
return (longueur == autre.longueur) &&
(largeur == autre.largeur) &&
(hauteur == autre.hauteur);
}
};
int main() {
Cube cube1, cube2;
cube1.definirDimensions(10, 10, 10);
cube2.definirDimensions(10, 10, 10);
cout << "Volume: " << cube1.calculerVolume() << endl;
cout << "Égaux: " << (cube1.egal(cube2) ? "Oui" : "Non") << endl;
return 0;
}
Initialisation et nettoyage des objets
Constructeurs et destructeurs
Les constructeurs initialisent les objets lors de leur création, tandis que les destructeurs libèrent les ressources à la destruction.
#include <iostream>
#include <string>
using namespace std;
class Etudiant {
public:
Etudiant(string nom, int age) : nom_(nom), age_(age) {
cout << "Objet créé pour " << nom_ << endl;
}
~Etudiant() {
cout << "Objet détruit pour " << nom_ << endl;
}
void presenter() {
cout << "Je suis " << nom_ << ", j'ai " << age_ << " ans." << endl;
}
private:
string nom_;
int age_;
};
int main() {
Etudiant etud1("Marie", 20);
etud1.presenter();
return 0;
}
Constructeur de copie et copie profonde
#include <iostream>
using namespace std;
class Tableau {
private:
int* donnees;
int taille;
public:
Tableau(int n) : taille(n) {
donnees = new int[n];
for (int i = 0; i < n; i++) {
donnees[i] = 0;
}
}
// Copie profonde
Tableau(const Tableau& autre) {
taille = autre.taille;
donnees = new int[taille];
for (int i = 0; i < taille; i++) {
donnees[i] = autre.donnees[i];
}
}
~Tableau() {
delete[] donnees;
}
void definirValeur(int index, int valeur) {
if (index >= 0 && index < taille) {
donnees[index] = valeur;
}
}
int obtenirValeur(int index) {
return donnees[index];
}
};
int main() {
Tableau tab1(5);
tab1.definirValeur(0, 42);
Tableau tab2(tab1); // Copie profonde
cout << tab2.obtenirValeur(0) << endl; // 42
return 0;
}
Modèle d'objet et pointeur this
Stockage séparé des membres
Seuls les attributs non sattiques font partie de la représentation mémoire de l'objet. Les fonctions membres sont partagées entre toutes les instances.
#include <iostream>
using namespace std;
class Point {
private:
int x_, y_;
public:
Point(int x, int y) : x_(x), y_(y) {}
void deplacer(int dx, int dy) {
x_ += dx;
y_ += dy;
}
Point& translater(int dx, int dy) {
x_ += dx;
y_ += dy;
return *this;
}
void afficher() {
cout << "(" << x_ << ", " << y_ << ")" << endl;
}
};
int main() {
Point p1(0, 0);
p1.translater(3, 4).translater(1, 2); // Chaînage
p1.afficher(); // (4, 6)
return 0;
}
Membres statiques
#include <iostream>
using namespace std;
class Compteur {
private:
static int instances;
int valeur;
public:
Compteur() : valeur(0) {
instances++;
}
static int obtenirNombreInstances() {
return instances;
}
};
int Compteur::instances = 0;
int main() {
Compteur c1, c2;
cout << "Instances : " << Compteur::obtenirNombreInstances() << endl; // 2
return 0;
}
Fonctiosn amies
Les fonctions amies permettent à des fonctions externes d'accéder aux membres privés d'une classe.
#include <iostream>
#include <string>
using namespace std;
class Maison;
class Ami {
public:
void visiter(Maison* m);
};
class Maison {
friend void Ami::visiter(Maison* m);
private:
string salleSecrete;
public:
Maison() : salleSecrete("Trésor caché") {}
};
void Ami::visiter(Maison* m) {
cout << "Accès à : " << m->salleSecrete << endl;
}
int main() {
Maison m;
Ami a;
a.visiter(&m);
return 0;
}
Surcharge d'opérateurs
#include <iostream>
#include <string>
using namespace std;
class Vecteur {
private:
double x_, y_;
public:
Vecteur(double x = 0, double y = 0) : x_(x), y_(y) {}
Vecteur operator+(const Vecteur& autre) const {
return Vecteur(x_ + autre.x_, y_ + autre.y_);
}
friend ostream& operator<<(ostream& os, const Vecteur& v) {
os << "(" << v.x_ << ", " << v.y_ << ")";
return os;
}
};
int main() {
Vecteur v1(1, 2), v2(3, 4);
Vecteur v3 = v1 + v2;
cout << "v3 = " << v3 << endl; // (4, 6)
return 0;
}
Héritage
Héritage de base
#include <iostream>
#include <string>
using namespace std;
class Vehicule {
protected:
string marque;
int annee;
public:
Vehicule(string m, int a) : marque(m), annee(a) {}
virtual void afficher() {
cout << marque << " (" << annee << ")" << endl;
}
};
class Voiture : public Vehicule {
private:
int portes;
public:
Voiture(string m, int a, int p) : Vehicule(m, a), portes(p) {}
void afficher() override {
Vehicule::afficher();
cout << "Portes : " << portes << endl;
}
};
int main() {
Voiture v("Renault", 2020, 5);
v.afficher();
return 0;
}
Polymorphisme
Fonctions virtuelles
#include <iostream>
#include <string>
using namespace std;
class Animal {
public:
virtual void parler() = 0; // Fonction virtuelle pure
virtual ~Animal() {}
};
class Chien : public Animal {
public:
void parler() override {
cout << "Woof!" << endl;
}
};
class Chat : public Animal {
public:
void parler() override {
cout << "Miaou!" << endl;
}
};
void faireParler(Animal* a) {
a->parler();
}
int main() {
Animal* animaux[] = {new Chien(), new Chat()};
for (Animal* a : animaux) {
faireParler(a);
delete a;
}
return 0;
}
Classe abstraite
#include <iostream>
using namespace std;
class Forme {
public:
virtual double aire() const = 0;
virtual void dessiner() const = 0;
virtual ~Forme() {}
};
class Rectangle : public Forme {
private:
double largeur, hauteur;
public:
Rectangle(double l, double h) : largeur(l), hauteur(h) {}
double aire() const override {
return largeur * hauteur;
}
void dessiner() const override {
cout << "Rectangle " << largeur << "x" << hauteur << endl;
}
};
int main() {
Rectangle r(5, 3);
r.dessiner();
cout << "Aire : " << r.aire() << endl;
return 0;
}
Ces concepts fondamentaux permettent de créer des modèles orientés objet robustes et maintenables en C++, facilitant la réutilisation du code et la gestion de la complexité.