Destructor wird nicht gerufen bei lazy-init Code-Beispiel - und Lösung



  • Hi,

    zunächst einmal der Code infrage:

    // wer damit experimentieren möchte: https://onlinegdb.com/UZV0zfRdCT
    
    #include <iostream>
    #include <list>
    
    class App {
    public:
        virtual const char* getName() = 0;
    };
    
    class AppWatch: public App {
    public:
        AppWatch() {
            std::cout << "created " << this->getName() << std::endl;
        }
        virtual ~AppWatch() {
            std::cout << "destroyed " << this->getName() << std::endl;
        }
        const char* getName() override { return "watch"; };
    };
    
    class AppTool: public App {
    public:
        AppTool() {
            std::cout << "created " << this->getName() << std::endl;
        }
        virtual ~AppTool() {
            std::cout << "destroyed " << this->getName() << std::endl;
        }
        const char* getName() override { return "tool"; };
    };
    
    class LazyInit {
    public:
        LazyInit() {
            std::cout << "lazy yay" << std::endl;
        }
    
        template<typename T>
        void set() {
            this->init = []() -> App* {
                return new T();
            };
        }
    
        App* operator->() {
            if(this->ptr == nullptr)
                this->ptr = this->init();
            return this->ptr;
        }
    
        virtual ~LazyInit() {
            if(this->ptr) {
                std::cout << "lazy death with " << (size_t) this->ptr << std::endl;
                delete this->ptr;
            } else
                std::cout << "lazy death" << std::endl;
        }
    private:
        App* (*init)() = nullptr;
        App* ptr = nullptr;
    };
    
    std::list<LazyInit> test;
    int main()
    {
        std::cout << "** Test start" << std::endl;
        {
            AppTool at;
        }
        std::cout << "** Test done" << std::endl;
        std::cout << "** Test start" << std::endl;
        {
            LazyInit lazy;
            lazy.set<AppWatch>();
            std::cout << "**Test prepared" << std::endl;
            lazy->getName(); // do smth with lazy object
        }
        std::cout << "** Test done" << std::endl;
        std::cout << "** Test start" << std::endl;
        {
            LazyInit lazy;
            lazy.set<AppWatch>();
            test.emplace_back(lazy);
            std::cout << "**Test prepared" << std::endl;
    
            test.back()->getName(); // do smth with lazy object
            
            test.clear();
        }
        std::cout << "** Test done" << std::endl;
        return 0;
    }
    

    Das Ziel soll es sein, dass das "LazyInit-Object" erst bei Dereferenzierung die entsprechende Klasse erzeugt. Das funktioniert auch so weit - das Problem ist, dass der Destructor der AppTool/AppWatch nicht korrekt ausgeführt wird, wenn "delete this->ptr;" erreicht wird (obwohl die Destructoren der Child-Klassen "virtual" sind).

    Lösung
    Tjaa, manchmal muss man das Problem nur Aufschreiben - die Lösung ist es ebenfalls den Destructor der "App"-Klasse auf "virtual" zu setzen, da ansonsten nicht in der vtable nachgeprüft wird, ob weitere Destructoren fehlen. 🤦🏻♂

    Ich lasse den Beitrag mal online - ggf. stolpert wer anderes ebenfalls über diese Feinheit 😉



  • @Simonmicro sagte in Destructor wird nicht gerufen bei lazy-init Code-Beispiel - und Lösung:

    die Lösung ist es ebenfalls den Constructor der "App"-Klasse auf "virtual" zu setzen

    Destructor



  • @hustbaer Well, you've got me there - fixed it 👌


Anmelden zum Antworten