这是我遇到的情景: -
#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
问候。
答案 0 :(得分:0)
您只能删除指针,而不能删除引用。 检查此link。这可能会对你有所帮助。
答案 1 :(得分:0)
问题1 “为什么仍然可以访问已删除实例的虚函数和成员变量”
函数调用在调用虚函数和成员变量时使用实例,在以下场景中:删除后,如果在已释放的块中偶然分配了另一个分配,并在已删除的实例中更改虚函数指针的值,这将在触发异常时触发异常你打来电话。
内存删除只会导致堆块通过堆管理返回到堆的空闲列表。(对于glibc和windows堆都是如此)。堆永远不会将虚拟页面返回到虚拟内存管理器,这意味着您可以访问的堆中已删除的内存,只有在有效范围内读取时才会触发任何异常。
问题2 “我认为函数定义没有存储在特定于实例的内存中,但这并不能解释”id“值是如何仍然可访问的。”
可执行文件中的函数定义在代码段中(对于dll将在进程之间共享,在某些senrior中将触发COW),新的变量在堆中分配。如上所述,可以访问内存,因此在通过其他分配获取堆块之前,您仍然可以访问以前的值(因为我知道,由于性能,windows heap和glic都不会重新填充或重置删除块的值)。
答案 2 :(得分:0)
访问已删除对象的成员会导致未定义的行为。
读取已删除对象的成员值通常会返回该地址中存在的任何内容。请记住,在此期间可以在该地址分配新对象,因此您可以从旧成员值中获取不同的值。
您正在使用new
运算符来分配OnState
对象,因此它将在堆上分配。成员变量是存储在堆内存中的对象的一部分,并且您有一个指向它的指针。即使您删除了该对象,您仍然可以通过指针读取其成员。
只要不回收虚拟内存页面,读访问就不应该抛出异常(无论如何,依靠这个假设是个坏主意。)