Pourquoi le vecteur fonctionnera-t-il mal s'il en hérite?
sobes.tech IA
Réponse de l'IA
L’héritage de std::vector n’est pas recommandé en raison de l’absence de destructeurs virtuels et d’autres fonctions virtuelles, ce qui viole le principe de substitution de Liskov et entraîne des problèmes lors de l’utilisation polymorphe.
Exemples de problèmes:
-
Problème de slicing: Lors du passage d’un objet de classe dérivée par valeur ou référence à une classe de base
std::vector, les données et comportements spécifiques à la classe dérivée seront perdus.#include <vector> #include <iostream> class MyVector : public std::vector<int> { public: int my_data = 100; // Sans destructeur virtuel ~MyVector() { std::cout << "Destructeur de MyVector" << std::endl; } }; void process_vector(std::vector<int> vec) { // Passage par valeur - slicing // Le destructeur de MyVector ne sera pas appelé std::cout << "Traitement de std::vector" << std::endl; } int main() { MyVector mv; mv.push_back(1); process_vector(mv); // Slicing de l’objet MyVector return 0; } // Seul le destructeur de std::vector sera appelé -
Absence de destructeur virtuel: Si vous supprimez un objet de classe dérivée via un pointeur vers la classe de base
std::vector, le destructeur de la classe dérivée ne sera pas appelé, ce qui peut entraîner des fuites de ressources.#include <vector> #include <iostream> #include <memory> // Pour unique_ptr class DerivedVector : public std::vector<int> { public: int* resource; DerivedVector() : std::vector<int>(), resource(new int) { std::cout << "Constructeur de DerivedVector" << std::endl; } // Sans destructeur virtuel ~DerivedVector() { std::cout << "Destructeur de DerivedVector" << std::endl; delete resource; // Peut ne pas être appelé } }; int main() { // Suppression via un pointeur vers la classe de base std::vector<int>* base_ptr = new DerivedVector(); // Lors du delete de base_ptr, seul le destructeur de std::vector sera appelé, // le destructeur de DerivedVector ne sera pas appelé, causant une fuite de resource delete base_ptr; // Exemple avec unique_ptr // std::unique_ptr<std::vector<int>> up = std::make_unique<DerivedVector>(); // up->push_back(5); // À la sortie du scope, unique_ptr appellera delete sur le pointeur brut base_ptr. // C’est équivalent à delete base_ptr; et causera le même problème si le vecteur n’a pas de destructeur virtuel. return 0; } // La mémoire allouée pour resource ne sera pas libérée -
Constructeurs: Le comportement des constructeurs de
std::vector(par exemple, copie, déplacement) peut ne pas correspondre aux attentes pour une classe dérivée si elle ajoute son propre état ou logique.
Au lieu d’hériter de std::vector, il est préférable d’utiliser:
- Composition: Utiliser
std::vectorcomme membre de la classe. Cela permet de contrôler l’interface et le comportement de la nouvelle classe, en utilisantstd::vectoren interne.#include <vector> #include <iostream> class MyContainer { private: std::vector<int> data; int my_extra_data = 100; public: void add(int val) { data.push_back(val); } const std::vector<int>& get_data() const { // Fournir l’accès au vecteur si nécessaire return data; } // Le destructeur de MyContainer appellera correctement le destructeur de data ~MyContainer() { std::cout << "Destructeur de MyContainer" << std::endl; } }; int main() { MyContainer mc; mc.add(1); std::cout << "Taille des données: " << mc.get_data().size() << std::endl; return 0; } // Le destructeur de MyContainer est appelé, qui appelle à son tour le destructeur de std::vector - Fonctions libres et algorithmes: Étendre la fonctionnalité de
std::vectoravec des fonctions ordinaires ou utiliser des algorithmes standards.
Ces approches sont plus flexibles, sûres et respectent les principes de la POO et de la conception de bibliothèques en C++. Les conteneurs standards ne sont pas destinés à être utilisés comme classes de base.