Sobes.tech
Middle+

Ինչու՞ վեկտորը վատ կաշխատի, եթե նրանից ժառանգվի։

sobes.tech AI

Պատասխան AI-ից

Նվագելությունը 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;
    
        // Օրինակ with unique_ptr
        // std::unique_ptr<std::vector<int>> up = std::make_unique<DerivedVector>();
        // up->push_back(5);
        // Երբ unique_ptr- ի տիրույթից դուրս է գալիս, նա կանչում է delete- ը 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-ի գործառույթները սովորական ֆունկցիաներով կամ օգտագործել ստանդարտ ալգորիթմները:

Այս մոտեցումները ավելի ճկուն, անվտանգ և համապատասխանում են OOP և C++ գրադարանների նախագծման սկզբունքներին: Ստանդարտ կոնտեյներները նախատեսված չեն որպես բազային դասեր օգտագործելու համար։