destructors are not called in the right order

Pages: 12
May 9, 2025 at 9:41am
Hi! I've a bug with mingw, I've downloaded the msvcrt runtime mingw-w64 12.0 on this website (the lasted doesn't work for me, DllMain entry point missing...) :

https://winlibs.com

But when I use std::unique_ptr the destructor of the object is not called.

Thanks.
Last edited on May 11, 2025 at 12:25am
May 9, 2025 at 1:34pm
Are you sure you're using std::unique_ptr correctly?
Last edited on May 9, 2025 at 1:34pm
May 9, 2025 at 3:04pm
Can you post test code that demonstrates the issue.
May 9, 2025 at 6:15pm
When I do tests in the main, the destructor is called, but not in my project. I think I made something wrong in my project..., but I don't call release and I initialize the unique_ptr so...I don't see what's wrong.

EDIT : I need to call reset to delete the pointer otherwise the descructor is not called.
Last edited on May 9, 2025 at 6:38pm
May 9, 2025 at 7:15pm
Erf...I've other destructors which are not called and not with object pointer this time, I need to call the cleanup function manually.
May 9, 2025 at 8:39pm
How are you creating your objects that don't have their destructors called?
May 9, 2025 at 9:51pm
I create them in the constructor like this :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PerPixelLinkedListRenderComponent::PerPixelLinkedListRenderComponent(RenderWindow& window, int layer, std::string expression, window::ContextSettings settings) :
            HeavyComponent(window, math::Vec3f(window.getView().getPosition().x, window.getView().getPosition().y, layer),
                          math::Vec3f(window.getView().getSize().x, window.getView().getSize().y, 0),
                          math::Vec3f(window.getView().getSize().x + window.getView().getSize().x * 0.5f, window.getView().getPosition().y + window.getView().getSize().y * 0.5f, layer)),
            view(window.getView()),
            expression(expression),
            quad(math::Vec3f(window.getView().getSize().x, window.getView().getSize().y, window.getSize().y * 0.5f)),
            layer(layer),
            frameBuffer(window.getDevice()),
            vkDevice(window.getDevice()),
            indirectRenderingShader(window.getDevice()),
            perPixelLinkedListP2(window.getDevice()),
            vb(window.getDevice()),
            vbBindlessTex {VertexBuffer(window.getDevice()), VertexBuffer(window.getDevice()), VertexBuffer(window.getDevice()), VertexBuffer(window.getDevice()), VertexBuffer(window.getDevice()), VertexBuffer(window.getDevice()), VertexBuffer(window.getDevice())},
            descriptorPool(window.getDescriptorPool()),
            descriptorSetLayout(window.getDescriptorSetLayout()),
            descriptorSets(window.getDescriptorSet()),
            vboIndirect(nullptr) {


And for unique_ptr I do this :

componentManager = std::make_unique<graphic::RenderComponentManager>(*window);

The destructor of components is not called and if I call them manually, it's the destructor of the frameBuffer object which is not called. (if I don't call it manually)

EDIT : I don't think the constructor fails to create objets..., but if it does, desctructors are not called so ...
Last edited on May 9, 2025 at 10:43pm
May 10, 2025 at 7:15am
I take it you have placed print statements in the destructors to see if they run.

Are you sure that the object that contains these objects has its destructor called? For example, if componentManager is a member of an object that is not destructed (e.g. maybe because you created it using new but forgot to use delete) then componentManager (and *componentManager) will of course not be destructed either.
Last edited on May 10, 2025 at 7:17am
May 10, 2025 at 7:33am
The RenderComponentManager object is created in the Application class, the destructor of the application class is called, but not the destructor of the RenderComponentManager class and it's components...

This is really strange.

Last edited on May 10, 2025 at 7:33am
May 10, 2025 at 7:39am
Is componentManager a member of the Application class? If it's just a local variable that is created inside one of the functions then it should be destructed much earlier, when it goes out of scope (i.e. when the function ends at the latest). If it's a global variable then it won't get destructed until the program ends.
Last edited on May 10, 2025 at 7:39am
May 10, 2025 at 8:19am
componentManager is a member of the application class, so it should be destroyed at the end of the program, but it's not destroyed...

EDIT : I printed the adress of the componentManager object in it's the same in the constructor and in the desctructor so I don't understand why it's not destroyed...
Last edited on May 10, 2025 at 8:22am
May 10, 2025 at 8:58am
What if you also print componentManager.get() from the Application destructor? Does it return a non-null pointer?

I think it's time that you listen to seeplus' advice. Create a minimal reproducible example. That'll make it much easier for us to help you. If you make it truly minimal it's a big chance that you'll find the issue yourself in the process (usually the problem turns out to be something unexpected, that's why you don't find it).

https://en.wikipedia.org/wiki/Minimal_reproducible_example
Last edited on May 10, 2025 at 8:58am
May 10, 2025 at 9:24am
Yes the pointer is not null when I print it in the destructor, yes I need to investigate more and trying to make a minimal code which reproduce the bug...
May 10, 2025 at 11:23am
...I simplified the code and wrote it outside my lib and everything is fine the destructors are called :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class RenderTarget {
public :
    RenderTarget() {}
    virtual ~RenderTarget() {
        std::cout<<"destroy render target"<<std::endl;
    }
};
class RenderTexture : public RenderTarget{
public :
    RenderTexture() {}
    ~RenderTexture() {
        std::cout<<"destroy render texture"<<std::endl;
    }
};
class Component {
    public :
    Component() {}
    virtual ~Component() {
        std::cout<<"destroy component"<<std::endl;
    }
};
class PerPixelLinkedListRenderComponent : public Component {
    public :
    PerPixelLinkedListRenderComponent() {
    }
    ~PerPixelLinkedListRenderComponent() {
        std::cout<<"destroy per pixel linked list render component"<<std::endl;
    }
    private :
        RenderTarget rt;
};
class RenderComponentManager {
    public :
    RenderComponentManager () {}
    void addComponent(Component* component) {
        std::unique_ptr<Component> ptr;
        ptr.reset(component);
        components.push_back(std::move(ptr));
    }
    ~RenderComponentManager() {
        std::cout<<"destroy component manager"<<std::endl;
    }
    private :
    std::vector<std::unique_ptr<Component>> components;
};
class Application {
    public :
    Application() {
        componentManager = std::make_unique<RenderComponentManager>();
    }
    ~Application() {
        std::cout<<"destroy application"<<std::endl;
    }
    RenderComponentManager& getComponentManager() {
        return *componentManager;
    }
    private :
    std::unique_ptr<RenderComponentManager> componentManager;
};
class MyApp : public Application {
public :
    MyApp() : Application() {}
};
int main() {
    MyApp app;
    PerPixelLinkedListRenderComponent* ppllrc = new PerPixelLinkedListRenderComponent();
    app.getComponentManager().addComponent(ppllrc);

   
}


So I guess the problem is when linking my static library that the destructors are not called...the classes are the same than in my lib but with more code in the body...and I don't think this is the code in the body who make it bug...
Last edited on May 10, 2025 at 11:24am
May 10, 2025 at 8:34pm
A "static" library is really just an archive that bundles a bunch of .obj files. So it really shouldn't make any difference whether you link those classes from a "static" library or whether you compile+link the .obj files directly.

I suspect you have some other problem in your project. But it's really hard to tell without a (minimal) full example that can be used to demonstrate the problem. In this case, the code excerpt above isn't sufficient, since we need to know how you build the project.

One thing to take care of is that, if you are working with "libraries", be sure that the exactly same header files are used when building the library and when building the "main" application that uses the library. Any divergence of the class' declaration between the "main" application (where the class is used) and the library (where the class is implemented) can result in undefined behavior.

I have experienced (seemingly) "mysterious" crashes or bugs in real-world projects turning out to be caused by diverging class' declarations many times. Like somebody threw in an updated version of the library but forgot to update the corresponding headers too 🙄
Last edited on May 10, 2025 at 8:42pm
May 10, 2025 at 10:25pm
Hi! I've searching for the issue and it's when I add a window to the application class, the destructor of the RenderComponentManager class is not called.

But when I don't add a window, the destructor is called.

Here's the code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class RenderComponentManager {
    public :
        RenderComponentManager() {

        }
        ~RenderComponentManager() {
            std::cout<<"rcm destructor"<<std::endl;
            system("PAUSE");
        }
};
class Application {
        public :
        Application(sf::VideoMode vm, std::string title, sf::Uint32 style=sf::Style::Default, odfaeg::window::ContextSettings settings = odfaeg::window::ContextSettings()) : vkDevice(vkSettup)

        {
            running = false;
            odfaeg::graphic::RenderWindow* window = new odfaeg::graphic::RenderWindow (vm, title, vkDevice, style, settings);
            windows.push_back(std::make_pair(window, true));
            componentManager = std::make_unique<RenderComponentManager>();


        }
        Application() : vkDevice(vkSettup) {
            running = false;
            componentManager = std::make_unique<RenderComponentManager>();
        }
        odfaeg::graphic::RenderWindow& getRenderWindow(unsigned int i=0) {            
            return *windows[i].first;
        }
        void stop() {
            running = false;
        }
       
        int exec() {
            load();
            init();
            running = true;            
            return EXIT_SUCCESS;
        }
       

        
        void load() {
            onLoad();
        }
        
        void init() {
            onInit();
        }
        
        void render() {             
        }

        
        void update() {   
        }
        
        virtual void onLoad (){}
       
        virtual void onInit() {}
        
        virtual void onRender (RenderComponentManager* cm){}
        
        virtual void onDisplay(odfaeg::graphic::RenderWindow *rw){}
        
        virtual void onUpdate (odfaeg::graphic::RenderWindow* window, odfaeg::window::IEvent& event) {}
        
        virtual void onExec() {}

        ~Application() {
            std::cout<<"destroy appli : "<<componentManager.get()<<std::endl;
            for (unsigned int i = 0; i < windows.size(); i++) {
                delete windows[i].first;
            }

        }
        private :
            std::vector<std::pair<odfaeg::graphic::RenderWindow*, bool>> windows; /** > the render window*/
            std::unique_ptr<RenderComponentManager> componentManager; /** > the render component manager which draw components on the window*/
            bool running; /** > determine if the application running or not.*/
            std::multimap<odfaeg::graphic::RenderWindow*, odfaeg::window::IEvent> events; /** > store the windows events generated by the application*/
            std::multimap<odfaeg::graphic::RenderWindow*, odfaeg::window::IEvent>::iterator it; /** > an iterator to the window::IEvents generated by the application*/
            odfaeg::window::Device vkDevice;
            odfaeg::window::VkSettup vkSettup;
};
class TestAppli : public Application {
public :
    TestAppli(sf::VideoMode wm, std::string title) : Application (sf::VideoMode(800, 600), "test") {



    }

    void onLoad() {

    }
    void onInit () {


    }
    void onRender(RenderComponentManager *cm) {


    }
    void onDisplay(odfaeg::graphic::RenderWindow* window) {

    }
    void onUpdate (odfaeg::graphic::RenderWindow* rw, odfaeg::window::IEvent& event) {
         /*if (rw == &getRenderWindow() && event.type == odfaeg::window::IEvent::WINDOW_EVENT && event.window.type == odfaeg::window::IEvent::WINDOW_EVENT_CLOSED) {
            std::cout<<"close"<<std::endl;
            stop();
         }*/
    }
    void onExec () {

    }

};
int main(int argc, char *argv[]) {
    TestAppli app(sf::VideoMode(800, 600), "Test odfaeg");
    return app.exec();




It'll be difficult to track the issue..., I don't know how it can bug by just adding a window...
Last edited on May 10, 2025 at 10:29pm
May 10, 2025 at 10:49pm
Ok I've found the issue, the problem is it destroyed the vulkan instance before destroying the vulkan device...

I've the same problem in my render window class, the destructor destroy the device before destroying command buffers, etc...

In which order the destructor destroy things ??? Because if I have an instance containing a device, the instance is destroying first...

And it seems when there is inheritance the variables of the child class are destroyed before the ones of the parent class...

Or I need to do the inverse...
Last edited on May 10, 2025 at 10:50pm
May 11, 2025 at 12:17am
In general, objects are destroyed in the reverse order that they have been created.

For local objects, the order of creation is kind of obvious. And they will be destroyed when they go out of scope (in reverse order).

It is global objects where things get nasty: They are created in order of appearance (top to bottom), but that is only guaranteed within a certain translation unit (i.e. source file). If you have several translation units (i.e. source files), then the order of creation (and thus also the order of destruction) of the global objects is undefined. And that is one reason why you generally want to avoid having global objects...
Last edited on May 11, 2025 at 12:26am
May 11, 2025 at 12:24am
Mmm...there is a thing I don't understand I declared my variables in this order :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
window::VkSettup vkSettup;
            window::Device vkDevice;
            std::vector<std::pair<graphic::RenderWindow*, bool>> windows; /** > the render window*/
            std::unique_ptr<graphic::RenderComponentManager> componentManager; /** > the render component manager which draw components on the window*/
            std::map<std::string, sf::Clock> clocks; /** > all the clocks used by the application to measure the time.*/
            bool running; /** > determine if the application running or not.*/
            sf::Color clearColor; /** > keep the clear color of the window*/
            std::multimap<graphic::RenderWindow*, window::IEvent> events; /** > store the windows events generated by the application*/
            std::multimap<graphic::RenderWindow*, window::IEvent>::iterator it; /** > an iterator to the window::IEvents generated by the application*/
            std::unique_ptr<Listener> listener;
            bool eventContextActivated;
            static sf::Clock timeClk;
            /*std::thread rendering_thread;
            std::recursive_mutex rec_mutex;*/
            graphic::World world;
            unsigned int nbEntities, nbEntitiesTypes, nbComponents, nbMaterials;
            std::vector<graphic::Material*> materials;
            std::vector<graphic::Material*> sameMaterials;
            std::map<int, std::string> types;


But the device is destroyed before the components...
Normally it should destroy the device last...
So I've this errors messages :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
destroy appli : 0x44beea0
destroy command buffers
rcm desrtructor
ppll destructor
destory device
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkCommandBuffer 0xa2aec90 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0x8fa81900000002af has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0xcc96650000001688 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0x4ab010000000169c has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0x438a4f00000002a8 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0xabab690000001684 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0x9aa7dc0000000293 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0xfbf8980000001666 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0xdc7e890000000291 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
validation layer: vkDestroyDevice(): Object Tracking - For VkDevice 0x4d3a6b0, VkBuffer 0xa41f190000001694 has not been destroyed.
The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.4.309.0/windows/antora/spec/latest/chapters/devsandqueues.html#VUID-vkDestroyDevice-device-05137)
destroy command buffers


May 11, 2025 at 6:32am
Ho I didn't declared device as a reference in my components so the device was destroyed...

Solved.
Pages: 12