Prečo bude vektor zle fungovať, ak z neho zdedíme?
sobes.tech AI
Odpoveď od AI
Dedičstvo od std::vector sa neodporúča kvôli absencii virtuálnych deštruktorov a iných virtuálnych funkcií, čo narúša LSP (princip náhrady Liskovovej podtriedy) a spôsobuje problémy pri polymorfnom použití.
Príklady problémov:
-
Problém s reznutím (slicing): Pri odovzdávaní objektu odvodeného triedy hodnotou alebo odkazom na základnú triedu
std::vector, budú stratené špecifické údaje a správanie odvodeného triedy.#include <vector> #include <iostream> class MyVector : public std::vector<int> { public: int my_data = 100; // Bez virtuálneho deštruktora ~MyVector() { std::cout << "MyVector deštruktor" << std::endl; } }; void process_vector(std::vector<int> vec) { // Prijíma hodnotou - dochádza k rezu // Deštruktor MyVector nebude volaný std::cout << "Spracovanie std::vector" << std::endl; } int main() { MyVector mv; mv.push_back(1); process_vector(mv); // Dochádza k rezu objektu MyVector return 0; } // Tu bude volaný iba deštruktor std::vector -
Absencia virtuálneho deštruktora: Ak odstránite objekt odvodeného typu cez ukazovateľ na základnú triedu
std::vector, deštruktor odvodeného typu nebude volaný, čo môže viesť k únikom zdrojov.#include <vector> #include <iostream> #include <memory> // Pre unique_ptr class DerivedVector : public std::vector<int> { public: int* resource; DerivedVector() : std::vector<int>(), resource(new int) { std::cout << "DerivedVector konštruktor" << std::endl; } // Chýba virtuálny deštruktor ~DerivedVector() { std::cout << "DerivedVector deštruktor" << std::endl; delete resource; // Nemusí byť volané } }; int main() { // Odstránenie cez ukazovateľ na základný typ std::vector<int>* base_ptr = new DerivedVector(); // Pri delete base_ptr sa volá iba deštruktor std::vector, // deštruktor DerivedVector sa nevolá, dochádza k úniku resource delete base_ptr; // Príklad s unique_ptr // std::unique_ptr<std::vector<int>> up = std::make_unique<DerivedVector>(); // up->push_back(5); // Pri ukončení platnosti unique_ptr zavolá delete na ukazovateli base_ptr. // To je ekvivalentné s delete base_ptr; a spôsobí rovnaký problém, ak vector nemá virtuálny deštruktor. return 0; } // Pamäť alokovaná pre resource nebude uvoľnená -
Konštruktory: Správanie konštruktorov
std::vector(napríklad kopírovací, presúvací konštruktor) nemusí zodpovedať očakávaniam pre odvodenú triedu, ak pridáva vlastný stav alebo logiku.
Namiesto dedenia od std::vector sú lepšie prístupy:
- Kompozícia: Použiť
std::vectorako člen triedy. To umožňuje kontrolovať rozhranie a správanie novej triedy pomocoustd::vectorvo vnútri.#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 { // Poskytujeme prístup k vektoru podľa potreby return data; } // Deštruktor MyContainer správne zavolá deštruktor data ~MyContainer() { std::cout << "MyContainer deštruktor" << std::endl; } }; int main() { MyContainer mc; mc.add(1); std::cout << "Veľkosť dát: " << mc.get_data().size() << std::endl; return 0; } // Deštruktor MyContainer je volaný, ktorý následne volá deštruktor std::vector - Voľné funkcie a algoritmy: Rozširovať funkcionalitu
std::vectorpomocou bežných funkcií alebo použiť štandardné algoritmy.
Tieto prístupy sú flexibilnejšie, bezpečnejšie a zodpovedajú princípom OOP a návrhu knižníc v C++. Štandardné kontajnery nie sú určené na použitie ako základné triedy.