0

I am working on developing a game and ran across something that I cannot understand. Why can I access this stack-allocated object after its destructor has been called?

This class, TextRes, is used to hold the "common name" of a texture and a path to the texture resource on disk. It also holds an SDL_Texture* variable, Texture_.

class TextRes
{
// class definition truncated
public:
    inline SDL_Texture* text() { return Texture_; }
private:
    char const*     Name_
    char const*     Path_
    SDL_Texture*    Texture_;
};

Another class, TextResMgr, is responsible for the data in this Texture_ variable; loading and deleting the texture resource, primarily. MVC design. TextResMgr has a std::vector<TextRes*>, TRvec, where pointers to instances of TextRes are held. TextResMgr::push_tr(TextRes&) takes an instance of TextRes by reference and adds it to the TextResMgr::TRvec object and loads the texture from disk into TextRes::Texture_.

class TextResMgr
{
// class definition truncated
    public:
    rt push_tr(TextRes&);    // rt is just a custom enum class used for return type 
    inline SDL_Texture* get_t(size_t _index) {return TRvec[_index].text();}

    private:
    std::vector<TextRes*> TRvec;    
};

The actual instances of TextRes reside in each of the game's "level classes" in a struct, and TextResMgr pushes/loads them to TextResMgr::TRvec when a level becomes active.

// ST_eng_menu.h (a "level"class)

#include "TRL_MainMenu.h"
class ST_eng_menu
{
// class definition truncated
public:
    TRL_MainMenu trl_main;
    char const* text_name = "menu-main";
};

// TRL_MainMenu.h

#include "TextRes.h"
struct TRL_MainMenu
{
    TextRes menu_bg;
    TextRes menu_opt_u;
    TextRes menu_opt_s;

    TRL_MainMenu()
    {
        menu_bg.name("menu-main");
        menu_bg.path("menu-main.png");

        menu_opt_u.name("menu_opt_u");
        menu_opt_u.path("menu_opt_u.png");

        menu_opt_s.name("menu_opt_s");
        menu_opt_s.path("menu_opt_s.png");
    }
};

Now, don't worry about SDL_Texture* if you havent used SDL before, the only thing you have to know about it is that you must use SDL functions to create and delete objects pointed to by them, not standard c++ alloc/malloc procedures like new and delete. When ~TextResMgr() is called, it will go through each TextRes* in TextResMgr::TRvec and calls SDL_DestroyTexture() on that TextRes::Texture_.

I made a little log macro utility that helps me follow objects around my program while executing by reporting information to the console. I get this output from it:

[log line number] [originating file] [file line number] [log message]

> 92 | STengMenu.h L : 35 ~ST_eng_menu() > 94 | TRL_MainMenu.h L : 29 ~TRL_MainMenu() > 95 | TextRes.h L : 19 ~TextRes() : 006FFA68 > 97 | TextRes.h L : 19 ~TextRes() : 006FFA5C > 99 | TextRes.h L : 19 ~TextRes() : 006FFA50 > 102 | TextResMgr.h L : 23 ~TextResMgr() > 104 | TextResMgr.cpp L : 122 TextResMgr::del_all_t() > 107 | SDLwrapper.h L : 336 destroy_text(862bb0) > 108 | TextResMgr.cpp L : 112 TextRes: 006FFA50 > 110 | SDLwrapper.h L : 336 destroy_text(86b6c0) > 111 | TextResMgr.cpp L : 112 TextRes: 006FFA5C > 113 | SDLwrapper.h L : 336 destroy_text(86b848) > 114 | TextResMgr.cpp L : 112 TextRes: 006FFA68

~TextRes() is called on lines 95, 97, and 99 and those lines also show the address to each of those objects. ~TextResMgr() is called afterward at line 102 and line 104 calls a function to delete all the TextRes::Texture_'s for each TextRes in TextResMgr::TRvec. Line 107 shows deleting a TextRes::Texture_ along with the texture's address and line 108 shows the address of the TextRes that texture was a member of.

Clearly, the destructor was called on those exact same TextRes objects already, but here I am allowed to access its members still. I have error checking throughout and can verify that those textures are deleted at that time.

My thinking is that these TextRes objects came into scope with TRL_MainMenu and should go out of scope when TRL_MainMenu does, regardless if I have a reference to it somewhere else or not. I understand that the SDL_Texture* member is on the heap and that accessing that should not be a problem if I had its address ahead of time, but the TextResMgr function that deletes TextRes::Texture_ gets a reference to that texture through a TextRes non-static member function, TextRes::text().

Furthermore, ~TextRes() is never called again at any point.

What is going on here? I am blonde, am I just having a moment??? This is all very easily fixed and I can control the flow of the program so that this situation doesn't even arise, but I am just stuck on how I can call TextRes::text() on a TextRes object after it has gone out of scope.

using Microsoft Visual Studio Community 2019 16.10.2

first last
  • 19
  • 4

1 Answers1

1

Clearly, the destructor was called on those exact same TextRes objects already, but here I am allowed to access its members still

You've assumed wrongly. You're not allowed to access its members after the destruction.

What is going on here?

The behaviour of the program is undefined.

eerorika
  • 232,697
  • 12
  • 197
  • 326