为啥在调用它的析构函数后我可以访问这个堆栈分配的对象? [复制]

Posted

技术标签:

【中文标题】为啥在调用它的析构函数后我可以访问这个堆栈分配的对象? [复制]【英文标题】:Why can I access this stack-allocated object after its destructor has been called? [duplicate]为什么在调用它的析构函数后我可以访问这个堆栈分配的对象? [复制] 【发布时间】:2021-09-09 04:32:35 【问题描述】:

我正在开发一款游戏,但遇到了一些我 不能理解。为什么我可以在之后访问这个堆栈分配的对象 它的析构函数被调用了吗?

TextRes 这个类用于保存纹理的“通用名称” 以及磁盘上纹理资源的路径。它还拥有一个 SDL_Texture* 变量,Texture_。

class TextRes

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

另一个类,TextResMgr,负责这个中的数据 纹理_变量;加载和删除纹理资源, 主要是。 MVC 设计。 TextResMgr 有一个 std::vector, TRvec,其中保存了指向 TextRes 实例的指针。 TextResMgr::push_tr(TextRes&) 采用 TextRes 的一个实例 引用并将其添加到 TextResMgr::TRvec 对象并加载 纹理从磁盘到 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;    
;

TextRes 的实际实例驻留在游戏的每个“关卡”中 classes”在结构中,TextResMgr 将它们推送/加载到 TextResMgr::TRvec 当关卡激活时。

// 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");
    
;

现在,如果您以前没有使用过 SDL,请不要担心 SDL_Texture*, 您唯一需要知道的是您必须使用 SDL 创建和删除它们指向的对象的函数,而不是 标准 c++ alloc/malloc 过程,如 new 和 delete。什么时候 ~TextResMgr() 被调用,它会遍历每个 TextRes* 中的 TextResMgr::TRvec 并在其上调用 SDL_DestroyTexture() TextRes::Texture_。

我制作了一个小日志宏实用程序,可以帮助我跟踪周围的对象 通过向控制台报告信息来执行我的程序。一世 从中获取此输出:

[日志行号] [原始文件] [文件行号] [日志消息]

> 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() 在第 95、97 和 99 行调用,这些行也显示 每个对象的地址。 ~TextResMgr() 被调用 之后在第 102 行和第 104 行调用一个函数来删除所有 TextResMgr::TRvec 中每个 TextRes 的 TextRes::Texture_。 107 号线 显示删除 TextRes::Texture_ 以及纹理的地址 第 108 行显示了纹理所在的 TextRes 的地址 成员。

显然,在那些完全相同的 TextRes 对象上调用了析构函数 已经,但在这里我仍然可以访问其成员。我有 整个过程中的错误检查,并且可以验证这些纹理是 当时删了。

我的想法是这些 TextRes 对象进入范围 TRL_MainMenu 并且当 TRL_MainMenu 超出范围时, 无论我是否在其他地方引用它。一世 了解 SDL_Texture* 成员在堆上,并且 如果我之前有它的地址,访问它应该不是问题 时间,但删除 TextRes::Texture_ 的 TextResMgr 函数得到 通过 TextRes 非静态成员对该纹理的引用 函数,TextRes::text()。

此外,在任何时候都不会再次调用 ~TextRes()。

这里发生了什么?我是金发女郎,我只是有时间吗??? 这一切都很容易解决,我可以控制流量 程序,这样这种情况甚至不会出现,但我只是卡住了 关于如何在 TextRes 对象上调用 TextRes::text() 超出范围。

使用 Microsoft Visual Studio Community 2019 16.10.2

【问题讨论】:

未定义行为是未定义。访问已删除的对象是未定义的行为,您实际上是在问“为什么我的未定义行为会以某种方式表现?” 是的,那是一个金发碧眼的时刻!哈哈。谢谢! 【参考方案1】:

显然,已经在那些完全相同的 TextRes 对象上调用了析构函数,但在这里我仍然可以访问它的成员

你猜错了。销毁后不允许访问其成员。

这是怎么回事?

程序的行为未定义。

【讨论】:

金发时刻。谢谢!

以上是关于为啥在调用它的析构函数后我可以访问这个堆栈分配的对象? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

C++ 堆栈分配对象,显式析构函数调用

为啥自动对象的析构函数被调用两次?

为啥C++里面,析构函数会被调用两次

在 C++ 中抛出后会调用析构函数吗?

返回一个对象会调用它的析构函数吗?

访问冲突 - 为啥基类析构函数被调用两次? [关闭]