Sobes.tech
Middle+

Miks vektor töötab halvasti, kui sellest pärida?

sobes.tech AI

Vastus AI-lt

Pärilitus std::vector-ist ei soovitata, kuna puuduvad virtuaalsed destruktorid ja teised virtuaalsed funktsioonid, mis rikuvad LSP-d (Liskovi asenduspõhimõtet) ning põhjustavad probleeme polümorfses kasutuses.

Näited probleemidest:

  1. Lõikamise (slicing) probleem: Kui edastate päriliku klassi objekti väärtuse või viitena baasklassile std::vector, kaovad päriliku klassi spetsiifilised andmed ja käitumine.

    #include <vector>
    #include <iostream>
    
    class MyVector : public std::vector<int> {
    public:
        int my_data = 100;
        // Puudub virtuaalne destruktor
        ~MyVector() {
            std::cout << "MyVector destruktor" << std::endl;
        }
    };
    
    void process_vector(std::vector<int> vec) {
        // Võtab väärtusena - toimub lõikamine
        // MyVector destruktor ei kutsuta
        std::cout << "std::vector töötlemine" << std::endl;
    }
    
    int main() {
        MyVector mv;
        mv.push_back(1);
        process_vector(mv); // Toimub MyVector objekti lõikamine
        return 0;
    } // Siin kutsutakse välja ainult std::vector destruktor
    
  2. Puudub virtuaalne destruktor: Kui kustutate päriliku klassi objekti baasklassi viitena std::vector, päriliku klassi destruktorit ei kutsuta, mis võib põhjustada ressursside lekkimist.

    #include <vector>
    #include <iostream>
    #include <memory> // unique_ptr jaoks
    
    class DerivedVector : public std::vector<int> {
    public:
        int* resource;
        DerivedVector() : std::vector<int>(), resource(new int) {
            std::cout << "DerivedVector konstruktor" << std::endl;
        }
        // Puudub virtuaalne destruktor
        ~DerivedVector() {
            std::cout << "DerivedVector destruktor" << std::endl;
            delete resource; // Võib jääda välja
        }
    };
    
    int main() {
        // Kustutamine baasklassi viitega
        std::vector<int>* base_ptr = new DerivedVector();
        // delete base_ptr - kutsutakse ainult std::vector destruktor,
        // DerivedVector destruktor ei kutsuta, toimub ressursside lekkimine
        delete base_ptr;
    
        // Näide unique_ptr-iga
        // std::unique_ptr<std::vector<int>> up = std::make_unique<DerivedVector>();
        // up->push_back(5);
        // Kui unique_ptr väljub ulatusest, kutsub ta delete- i peale base_ptr.
        // See on ekvivalent delete-ile ja võib põhjustada sama probleemi, kui vectoril puudub virtuaalne destruktor.
    
        return 0;
    } // Ressursi mälu ei vabastata
    
  3. Konstruktorid: std::vector konstruktorid (nt koopiakonstruktor, liigutuskonstruktor) võivad mitte vastata ootustele päriliku klassi puhul, kui see lisab oma oleku või loogika.

Asenduseks std::vector-ist pärimine on paremad:

  • Koostöö: Kasutada std::vector klassi liikmena. See võimaldab kontrollida liidest ja käitumist, kasutades std::vector-i sees.
    #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 { // Pääs andmete saamiseks
            return data;
        }
        // MyContainer destruktor kutsub õigesti välja data destruktor
        ~MyContainer() {
             std::cout << "MyContainer destruktor" << std::endl;
        }
    };
    
    int main() {
        MyContainer mc;
        mc.add(1);
        std::cout << "Andmete suurus: " << mc.get_data().size() << std::endl;
        return 0;
    } // MyContainer destruktor kutsutakse välja, mis omakorda kutsub välja std::vector destruktor
    
  • Vabad funktsioonid ja algoritmid: Laiendada std::vector funktsionaalsust tavaliste funktsioonide või standardsete algoritmide abil.

Need lähenemised on paindlikumad, turvalisemad ja vastavad OOP ning raamatukogude disaini põhimõtetele. Standardkonteinerid ei ole mõeldud alusklassideks.