L'utilisation de la composition permet de construire des objets complexes à partir d'objets plus simples. Dans cet exemple, une classe représentant une fenêtre contient une liste dynamique de boutons.
Composant Bouton (WidgetBouton.hpp)
#pragma once
#include <iostream>
#include <string>
class WidgetBouton {
public:
WidgetBouton(const std::string& texte) : label(texte) {}
const std::string& lireLabel() const { return label; }
void declencher() const {
std::cout << "[Action] Bouton '" << label << "' active." << std::endl;
}
private:
std::string label;
};
Gestionnaire de Fenêtre (CadreFenetre.hpp)
#pragma once
#include <vector>
#include <string>
#include <iostream>
#include "WidgetBouton.hpp"
class CadreFenetre {
public:
CadreFenetre(const std::string& nom) : titre(nom) {
elements.emplace_back("Quitter");
}
void ajouterBouton(const std::string& libelle) {
for (const auto& b : elements) {
if (b.lireLabel() == libelle) {
std::cout << "Erreur : Le bouton " << libelle << " existe deja.\n";
return;
}
}
elements.emplace_back(libelle);
}
void rendu() const {
std::string separateur(30, '-');
std::cout << separateur << "\nFenetre : " << titre << "\n";
for (size_t i = 0; i < elements.size(); ++i) {
std::cout << i + 1 << ". " << elements[i].lireLabel() << "\n";
}
std::cout << separateur << std::endl;
}
void cliquer(const std::string& libelle) {
for (auto& b : elements) {
if (b.lireLabel() == libelle) {
b.declencher();
return;
}
}
std::cout << "Bouton introuvable : " << libelle << std::endl;
}
private:
std::string titre;
std::vector<WidgetBouton> elements;
};
Le code suivant démontre comment le conteneur std::vector de la bibliothèque standard gère la copie prfoonde, assurant que les modifications sur une instance n'affectent pas sa copie.
#include <iostream>
#include <vector>
void demoCopie() {
std::vector<int> source = {10, 20, 30};
std::vector<int> copie = source; // Copie profonde
source[0] = 999;
std::cout << "Source[0]: " << source[0] << " (attendu: 999)\n";
std::cout << "Copie[0] : " << copie[0] << " (attendu: 10)\n";
}
void demoMatriceVector() {
std::vector<std::vector<int>> grille = {{1, 2}, {3, 4}};
auto duplication = grille;
grille[0].push_back(5);
std::cout << "Taille grille[0]: " << grille[0].size() << "\n";
std::cout << "Taille duplication[0]: " << duplication[0].size() << "\n";
}
Pour comprendre les mécanismes internes de C++, nous implémentons un gestionnaire de tableau dynamique respectant la règle des trois (constructeur de copie, destructeur, opérateur d'affectation).
#pragma once
#include <iostream>
#include <algorithm>
class TableauEntier {
public:
TableauEntier(int taille) : n(taille), donnees(new int[taille]()) {}
// Constructeur de copie (Deep Copy)
TableauEntier(const TableauEntier& autre) : n(autre.n), donnees(new int[autre.n]) {
std::copy(autre.donnees, autre.donnees + n, donnees);
}
~TableauEntier() { delete[] donnees; }
TableauEntier& operator=(const TableauEntier& autre) {
if (this != &autre) {
int* nouveau = new int[autre.n];
std::copy(autre.donnees, autre.donnees + autre.n, nouveau);
delete[] donnees;
donnees = nouveau;
n = autre.n;
}
return *this;
}
int& acceder(int index) {
if (index < 0 || index >= n) exit(1);
return donnees[index];
}
int taille() const { return n; }
private:
int n;
int* donnees;
};
Cette classe encapsule un tableau unidimensionnel pour simuler une structure bidimensionnelle, optimisant ainsi l'alocation mémoire.
#include <iostream>
#include <cstring>
class Matrice {
public:
Matrice(int l, int c, double val = 0.0) : lignes(l), colonnes(c) {
data = new double[l * c];
for (int i = 0; i < l * c; ++i) data[i] = val;
}
Matrice(const Matrice& m) : lignes(m.lignes), colonnes(m.colonnes) {
data = new double[lignes * colonnes];
std::memcpy(data, m.data, lignes * colonnes * sizeof(double));
}
~Matrice() { delete[] data; }
double& at(int i, int j) {
return data[i * colonnes + j];
}
void afficher() const {
for (int i = 0; i < lignes; ++i) {
for (int j = 0; j < colonnes; ++j) {
std::cout << data[i * colonnes + j] << " ";
}
std::cout << "\n";
}
}
private:
int lignes, colonnes;
double* data;
};
L'exemple suivant combine std::vector et l'algorithme std::sort avec une expression lambda pour maintenir une liste de contacts ordonnée.
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
struct Fiche {
std::string nom;
std::string tel;
};
class Carnet {
public:
void inserer(const std::string& n, const std::string& t) {
liste.push_back({n, t});
ordonner();
}
void supprimer(const std::string& n) {
auto it = std::remove_if(liste.begin(), liste.end(), [&](const Fiche& f) {
return f.nom == n;
});
liste.erase(it, liste.end());
}
void lister() const {
for (const auto& f : liste) {
std::cout << f.nom << " : " << f.tel << "\n";
}
}
private:
std::vector<Fiche> liste;
void ordonner() {
std::sort(liste.begin(), liste.end(), [](const Fiche& a, const Fiche& b) {
return a.nom < b.nom;
});
}
};