即使删除了调用它的对象,也能访问数据的c++成员函数

C++ Member function able to access data even it deleted the object that called it

本文关键字:访问 数据 c++ 函数 成员 对象 删除 调用      更新时间:2023-10-16

这是我遇到的一个场景:-

#include <iostream>
class Agent;
class State;
class OffState;
class OnState;
class State
{
public:
    virtual void execute(Agent * agent) = 0;
    virtual ~State() {std::cout << "removing Staten";}
};
class Agent{
    State * currentState ; 
public:
    Agent();
    void update(){
        std::cout << "agent updating. will execute current State " << std::endl;
        currentState->execute(this);
    }
    void changeState(State * newState){
        delete currentState;
        currentState = newState;
    }
};
class OffState : public State
{
 public:
 ~OffState() {std::cout << "deleting OffState Object" <<std::endl;}
    void execute(Agent * agent){
        std::cout << "Nothing happens in the off State " << std::endl;
    }
};

class OnState : public State
{
static int count ; 
int id;
public:
    OnState(){
        id = count;
        count++;
    }
    ~OnState() {std::cout << "removing OnState id :- " <id<<std::endl;}
        void execute(Agent * agent){
            std::cout << "OnState executing" << std::endl;
            agent->changeState(new OffState());  
            std::cout << "executed after deleting OnState ? id:- " << id  << std::endl;
        }
};
int OnState::count = 0;
Agent::Agent():currentState(new OnState()){ 
}

main(){
    Agent smith;
    smith.update();
}

在这里,Agent的当前状态被初始化为一个OnState对象。该对象通过Agent中的update()方法访问。调用OnState的execute方法。现在这个execute方法间接地删除调用它的OnState对象。然而,在此之后,控制权被传递回OnState对象中的execute()方法。更重要的是,它能够打印"id"的值。内存指针不应该因为delete currentState而被删除吗?

或者在某些情况下,系统可能会崩溃,并且在某些情况下,内存内容不会立即被操作系统填充。

我认为函数定义不是存储在特定于实例的内存中,但这并不能解释"id"值如何仍然可以访问。

代码的输出是:-

agent updating. will execute current State 
OnState executing
removing OnState id :- 0
removing State

删除OnState后执行?id: - 0

问候。

只能删除指针,不能删除引用。点击这个链接。

问题1 "为什么仍然可以访问已删除实例的虚函数和成员变量"

函数调用使用实例时调用虚函数和成员变量,在以下场景:在你删除后,如果另一个分配碰巧在你释放的块中分配,并且改变了你删除实例中的虚函数指针的值,这将在你调用时触发异常

内存删除只会导致堆块被堆管理返回到空闲堆列表中。(适用于glibc和Windows heap)。堆永远不会将虚拟页面返回给虚拟内存管理器,这意味着在堆中删除的内存可以访问,并且只在有效范围内读取不会触发任何异常。

问题2 "我认为函数定义不是存储在特定于实例的内存中,但这并不能解释"id"值如何仍然可以访问。"

可执行文件中的函数定义在代码段中(对于dll将在进程之间共享,并且在某些高级文件中会触发COW), new变量分配在堆中。如前所述,可以访问内存,因此在堆块被其他分配占用之前,您仍然可以访问先前的值(据我所知,由于性能原因,windows堆和glic都不会重新填充或重置删除块的值)。

访问已删除对象的成员将导致未定义行为

读取已删除对象的成员值通常会返回该地址的值。请记住,在此期间可以在该地址分配新对象,因此您可以获得与旧成员值不同的值。

您使用new操作符来分配OnState对象,因此它将在堆上分配。成员变量是存储在堆内存中的对象的一部分,您有指向它的指针。即使删除了对象,仍然可以通过指针读取其成员。

只要不回收虚拟内存页,读访问就不应该抛出异常(无论如何,依赖这个假设是一个坏主意)。