了解C ++中的delete运算符

时间:2013-06-26 15:52:50

标签: c++ dynamic-allocation

请考虑以下代码:

#include <iostream>
#include <string>
 using namespace std;

class A{ 
 public: 
     int x; 
 public: 
     A(){x=0;} 
     void fun1(){ 
         cout << "fun1 is called \n"; 
         cout << "Address of this is " << this <<endl; 
         delete this; 
     } 
     void fun2() 
     { 
         cout << "fun2 called \n"; 
     } 
     ~A() 
     { 
             cout << "Object Destroyed" << endl; 
     } 
 }; 

 int main() 
 { 
     A* ptr=new A; 
     cout << "Address of ptr is " << ptr <<endl; 
     ptr->fun1(); 
     ptr->fun2(); 
     return(0); 
 }

输出是:

$ ./TestCPP
Address of ptr is 0x20010318
fun1 is called
Address of this is 0x20010318
Object Destroyed
fun2 called

我的问题是,当我们在delete中调用fun1()时,它会破坏this指针指向的对象,即地址0x20010318。它调用析构函数,如输出所示。因此,在调用fun1()之后,地址0x20010318处的对象被销毁并且该内存被释放。那么为什么在输出中我们可以看到fun2()?这只是垃圾价值吗?我的意思是对象不存在,但在ptr -> fun2()指向的位置fun2()的定义仍然存在?

也可以请某人解释delete的工作原理。例如,调用new调用operator newconstructordelete操作是否类似?

由于

7 个答案:

答案 0 :(得分:4)

您所做的是技术上未定义的行为,因此,就标准而言,任何事情都可能发生。

除此之外,您所看到的实际行为很容易被推理。 fun2是非虚函数。编译器将在编译时解析对它的调用。当对象被销毁时,该功能不会被破坏。当你调用ptr->fun2()时,你的编译器就会调用该函数。由于该函数不依赖于任何成员数据,因此输出是可预测的(尽管就标准而言,它并非如此)。

这是我在空指针上调用非虚成员函数的演示。显然这是错误的,但它会完全按预期打印语句:http://ideone.com/pddnGt

这并不是说代码也不错。您的代码中永远不应该有未定义的行为。在较低的优化级别上,出于调试目的,编译器可能会很好地检查此类事情并使用错误消息暂停程序,或者抛出异常。

答案 1 :(得分:2)

  

这只是垃圾值吗?我的意思是对象不存在,但在ptr -> fun2()指向的位置fun2()的定义仍然存在?

是的,它是未定义的行为,但因为没有任何实际重新使用内存,它似乎“工作”好。这实际上是一个严重的错误。 (NB A::fun2()的定义永远不会去任何地方,即代码,而不是数据,因此存在于程序的整个生命周期中,内存位置ptr处的对象停止存在,但其定义为成员函数没有。)

  

例如调用new调用operator new和构造函数,delete操作是否类似?

是的,它调用析构函数来销毁该位置的对象,然后调用operator delete来释放内存。

答案 2 :(得分:1)

如果您的方法不是虚拟的并且不包含对成员的引用,则它可以与某些编译器一起使用,因为程序集中的方法不需要有效的this指针。 无论如何,你必须对未定义的行为充满信心。

答案 3 :(得分:0)

delete中调用fun1后,您对fun2的通话是非法的。 C ++不会牵着你的手,所以对于这种“未定义的行为”,任何事情都可以发生,包括正确调用fun2

实际上delete调用对象的析构函数,然后释放内存,与new相反。

答案 4 :(得分:0)

当您致电delete时,系统会通知操作系统不再需要此内存,因此可供将来分配。但是,它不会被自动擦除。

在您的情况下,在致电fun2之前,其他人不会使用该内存。没有人试图在两个函数调用之间分配堆中的任何内存。因此,物体仍然在那里,永远不会被篡改。然而,这并不意味着在两次调用之间不可能进行存储器分配(例如,可以触发中断并且可以在处理中断期间分配存储器)。因此,你永远不应该这样做。 :)

答案 5 :(得分:0)

当您从类中调用非虚函数时,函数

class A { public: void function(int k) { ... } };

将被写成类似

的内容
void __A_function(A* this, int k);

如果您的函数不涉及this指针,它将被称为标准函数,忽略this参数。

我能预测的另一件事是,即使你喜欢这个

class A
{
private:
  int k;
public:
  A() : k(10) {}
  void function() { printf("%d\n",k); }
};

A* ptr=new A;
delete ptr;
ptr->function();
在大多数情况下,它会打印10张。因为new的内存尚未清理。

我将推荐使用Inside C ++对象模型来详细了解这一点。

答案 6 :(得分:0)

成员函数在编译时解析。

您可以执行以下操作:

A* ptr = NULL;
ptr->yourFunc();

只要您不访问对象中的数据存储(及其VTable,因此您不想调用方法),它就会起作用。

'this'的值为null,就可以了。但是,对任何事情进行解体都会导致段错误。