Sobes.tech
Middle+

Kāpēc vektors slikti darbosies, ja no tā manto?

sobes.tech AI

Atbilde no AI

Mantošana no std::vector nav ieteicama, jo trūkstamu virtuālo destruktora un citām virtuālām funkcijām, kas pārkāpj LSP (Liskova aizvietošanas princips) un izraisa problēmas polimorfiskā lietošanā.

Piemēri:

  1. Sagriešanas (slicing) problēma: Kad pārsūtāt mantojošas klases objektu ar vērtības vai atsauces palīdzību uz bāzes klasi std::vector, mantojošās klases specifiskie dati un uzvedība tiks zaudēti.

    #include <vector>
    #include <iostream>
    
    class MyVector : public std::vector<int> {
    public:
        int my_data = 100;
        // Bez virtuāla destruktora
        ~MyVector() {
            std::cout << "MyVector destruktors" << std::endl;
        }
    };
    
    void process_vector(std::vector<int> vec) {
        // Pieņem ar vērtību - notiek sagriešana
        // MyVector destruktors netiks izsaukts
        std::cout << "std::vector apstrāde" << std::endl;
    }
    
    int main() {
        MyVector mv;
        mv.push_back(1);
        process_vector(mv); // Notiek MyVector objekta sagriešana
        return 0;
    } // Šeit tiks izsaukts tikai std::vector destruktors
    
  2. Trūkst virtuāla destruktora: Ja dzēšat mantojošas klases objektu ar atsauci uz bāzes klasi std::vector, mantojošā klases destruktors netiks izsaukts, kas var novest pie resursu noplūdes.

    #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 konstruktors" << std::endl;
        }
        // Bez virtuāla destruktora
        ~DerivedVector() {
            std::cout << "DerivedVector destruktors" << std::endl;
            delete resource; // Var netikt izsaukts
        }
    };
    
    int main() {
        // Dzēšana ar bāzes klases atsauci
        std::vector<int>* base_ptr = new DerivedVector();
        // delete base_ptr - tiks izsaukts tikai std::vector destruktors,
        // DerivedVector destruktors netiks izsaukts, notiks resursu noplūde
        delete base_ptr;
    
        // Piemērs ar unique_ptr
        // std::unique_ptr<std::vector<int>> up = std::make_unique<DerivedVector>();
        // up->push_back(5);
        // Kad unique_ptr iziet no darbības apgabala, tas izsauks delete uz pointer base_ptr.
        // Tas ir ekvivalents ar delete base_ptr; un var novest pie tā paša problēmas, ja vector nav virtuāls destruktors.
    
        return 0;
    } // Atbrīvotā resursa atmiņa netiks brīvi atbrīvota
    
  3. Konstruktori: std::vector konstruktori (piemēram, kopēšanas, pārvietošanas) var neatbilst gaidām mantojošā klases vajadzībām, ja tas pievieno savu stāvokli vai loģiku.

Vietā, lai mantošanas no std::vector, labāk ir:

  • Kompozīcija: Izmantot std::vector kā klases locekli. Tas ļauj kontrolēt saskarni un uzvedību, izmantojot std::vector iekšpusē.
    #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 { // Nodrošināt piekļuvi vektoram
            return data;
        }
        // MyContainer destruktors pareizi izsauks data destruktora
        ~MyContainer() {
             std::cout << "MyContainer destruktors" << std::endl;
        }
    };
    
    int main() {
        MyContainer mc;
        mc.add(1);
        std::cout << "Datu lielums: " << mc.get_data().size() << std::endl;
        return 0;
    } // MyContainer destruktors tiks izsaukts, kas savukārt izsauks std::vector destruktora
    
  • Brīvas funkcijas un algoritmi: Paplašināt std::vector funkcionalitāti ar parastām funkcijām vai izmantot standartus algoritmus.

Šie pieejas ir elastīgāki, drošāki un atbilst OOP un bibliotēku projektēšanas principiem. Standarta konteineri nav paredzēti kā bāzes klases.