Sobes.tech
Middle+

Zašto će vektor loše raditi ako od njega naslijedimo?

sobes.tech АИ

Одговор од АИ

Наслеђивање од std::vector се не препоручује због недостатка виртуелних деструктора и других виртуелних функција, што крши Лисков принцип (LSP) и доводи до проблема при полиморфном коришћењу.

Примери проблема:

  1. Проблем са сечењем (slicing): Када преносите објекат одвојене класе кроз вредност или референцу на базну класу std::vector, подаци и понашање специфични за одвојену класу се губе.

    #include <vector>
    #include <iostream>
    
    class MyVector : public std::vector<int> {
    public:
        int my_data = 100;
        // Без виртуелног деструктора
        ~MyVector() {
            std::cout << "MyVector деструктор" << std::endl;
        }
    };
    
    void process_vector(std::vector<int> vec) {
        // Прима по вредности - долази до сечења
        // Деструктор MyVector неће бити позван
        std::cout << "Обрада std::vector" << std::endl;
    }
    
    int main() {
        MyVector mv;
        mv.push_back(1);
        process_vector(mv); // Долази до сечења објекта MyVector
        return 0;
    } // Овде ће бити позван само деструктор std::vector
    
  2. Недостајање виртуелног деструктора: Ако објекат одвојене класе бришете преко показивача на базну класу std::vector, деструктор одвојене класе неће бити позван, што може довести до цурења ресурса.

    #include <vector>
    #include <iostream>
    #include <memory> // За unique_ptr
    
    class DerivedVector : public std::vector<int> {
    public:
        int* resource;
        DerivedVector() : std::vector<int>(), resource(new int) {
            std::cout << "Конструктор DerivedVector" << std::endl;
        }
        // Недостаје виртуелни деструктор
        ~DerivedVector() {
            std::cout << "Деструктор DerivedVector" << std::endl;
            delete resource; // Може да не буде позван
        }
    };
    
    int main() {
        // Бришање преко показивача на базну класу
        std::vector<int>* base_ptr = new DerivedVector();
        // При delete base_ptr се позива само деструктор std::vector,
        // деструктор DerivedVector се не позива, долази до цурења ресурса
        delete base_ptr;
    
        // Пример са unique_ptr
        // std::unique_ptr<std::vector<int>> up = std::make_unique<DerivedVector>();
        // При изласку из scope, unique_ptr ће позвати delete на raw показивачу base_ptr.
        // Ово је еквивалентно delete base_ptr; и доводи до истог проблема ако vector нема виртуелни деструктор.
    
        return 0;
    } // Памћење додељено за resource неће бити ослобођено
    
  3. Конструктори: Понашање конструктора std::vector (нпр. конструктор копирања, преноса) можда неће одговарати очекивањима за одвојену класу ако она додаје своје стање или логику.

Уместо наследја од std::vector, најбоље приступе су:

  • Композиција: Користите std::vector као члан класе. Ово омогућава контролу интерфејса и понашања нове класе, користећи std::vector унутар ње.
    #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 { // Омогућава приступ вектору
            return data;
        }
        // Деструктор MyContainer ће исправно позвати деструктор data
        ~MyContainer() {
             std::cout << "Деструктор MyContainer" << std::endl;
        }
    };
    
    int main() {
        MyContainer mc;
        mc.add(1);
        std::cout << "Величина података: " << mc.get_data().size() << std::endl;
        return 0;
    } // Деструктор MyContainer се позива, који у својој функцији позива деструктор std::vector
    
  • Бесплатне функције и алгоритми: Проширите функционалност std::vector помоћу обичних функција или користите стандардне алгоритме.

Ови приступи су флексибилнији, безбеднији и у складу су са принципима ООП и дизајна библиотека у C++. Стандардни контејнери нису намењени за коришћење као основне класе.