为什么会这样? C ++内存管理

时间:2012-12-23 14:22:10

标签: c++ memory-management

  

可能重复:
  Why the below piece of code is not crashing , though i have deleted the object?

今天我发现我对C ++内存管理一无所知。请看一下这段代码:

class A
{
 public:
     A(){std::cout << "constructor called" << this << std::endl;}
    ~A(){std::cout << "destructor called" << this << std::endl;}
      void test (){std::cout << "test" << this << std::endl;}
 };

 int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

A *aa = new A();

delete aa;
aa->test();
aa->test();
aa->test();
std::cout << "still works\n";
return a.exec();
}

为什么不崩溃?尽管析构函数被调用,为什么它仍然继续执行?当我调用test方法时,我处理不再属于该应用程序的内存。

更令人惊讶的是,即使我在aa = NULL;之后插入delete aa;,它仍然有效。 test方法被调用好了。我需要承认我完全糊涂了。析构函数的用途是什么,如果没有效果则赋值为NULL?

3 个答案:

答案 0 :(得分:10)

为什么会这样?

有两种方法可以回答您的问题:

技术答案:

您的代码有未定义的行为 它取消引用NULLdelete ed指针。根据C ++标准,它们都调用Undefined Behavior。它的工作与否是毫无意义的 未定义的行为意味着任何行为都是可能的,它可能会或可能不会崩溃,但这意味着您的程序无法提供任何明确定义的输出。它只是意味着任何行为都是可能的,并且可能不一致或定义得很好。

实际答案:

它不会崩溃,因为编译器在调用成员函数时实际上不会解除this。除非函数是virtual函数,否则编译器会将this作为第一个参数传递给函数,从而将成员函数调用转换为通常的函数调用。它可以这样做,因为编译器可以准确地确定在编译时自己调用哪个函数。所以实际上,通过已删除或NULL指针调用成员函数不会取消引用this如果NULLdelete编辑,则无效)。此外,只有在函数体内访问任何成员时才会取消引用this 在您的情况下,您永远不会访问函数体内的任何成员,因此它不会崩溃 添加一个成员并在你的函数中取消引用它,它肯定会崩溃。

无论如何,在实际答案中所说的技术答案已经超越了一切,因为标准说明了这一点。

答案 1 :(得分:3)

  

为什么不崩溃?

您通过取消引用已删除的指针来调用未定义的行为。未定义的行为意味着您的程序可以执行任何。任何事情都不包括崩溃。

如果未定义的行为总是意味着您的程序立即崩溃,那么它将很容易调试和修复。未定义行为的最严重问题之一是程序在测试时看起来可以正常工作,然后当您将它发送给客户时,它们会得到您无法重现的异常行为。即使事情似乎在你的机器上运行,你也应该总是避免调用未定义的行为。

答案 2 :(得分:2)

这只是未定义的行为,其中包括发生重大崩溃的可能性!

不多说,但标准中的引用可能有助于未定义行为的含义

1.3.24未定义的行为

  

本国际标准没有要求的行为[注:未定义   当本国际标准遗漏任何行为时,可能会出现这种行为   行为的明确定义或程序使用错误的时间   构造或错误的数据。允许的未定义行为范围   从无法预测的结果完全忽略这种情况到   在记录中的翻译或程序执行期间表现   环境的方式特征(有或没有发行   (诊断消息),终止翻译或执行   (发布诊断信息)。许多错误的计划   构造不会产生未定义的行为;他们必须是   诊断。 - 结束说明]