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

时间:2016-09-01 04:55:25

标签: c++ return member delete-operator

这是我遇到的情景: -

#include <iostream>

class Agent;
class State;
class OffState;
class OnState;

class State
{
public:
    virtual void execute(Agent * agent) = 0;
    virtual ~State() {std::cout << "removing State\n";}
};

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();

}

在此,代理的当前状态初始化为OnState对象。可以通过Agent中的update()方法访问此对象。这将调用OnState的execute方法。现在,这个execute方法间接删除了调用它的OnState对象。然后,控制将传递回OnState对象中的execute()方法。更重要的是它能够打印“id”的值。由于删除currentState,不应删除指向的内存。

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

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

代码的输出是: -

agent updating. will execute current State 
OnState executing
removing OnState id :- 0
removing State
删除OnState后执行

? id: - 0

问候。

3 个答案:

答案 0 :(得分:0)

您只能删除指针,而不能删除引用。 检查此link。这可能会对你有所帮助。

答案 1 :(得分:0)

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

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

内存删除只会导致堆块通过堆管理返回到堆的空闲列表。(对于glibc和windows堆都是如此)。堆永远不会将虚拟页面返回到虚拟内存管理器,这意味着您可以访问的堆中已删除的内存,只有在有效范围内读取时才会触发任何异常。

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

可执行文件中的函数定义在代码段中(对于dll将在进程之间共享,在某些senrior中将触发COW),新的变量在堆中分配。如上所述,可以访问内存,因此在通过其他分配获取堆块之前,您仍然可以访问以前的值(因为我知道,由于性能,windows heap和glic都不会重新填充或重置删除块的值)。

答案 2 :(得分:0)

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

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

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

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