我正在颠倒一个假定是用MSVC编译的程序。似乎每个vtable中的第一个条目都是类的析构函数。但是,当我查看反汇编和反编译时,似乎所有析构函数都带有第二个参数,并且仅当该第二个参数为非空时才释放对象的内存。

的目的是什么第二个论点?我认为,如果它是一个析构函数,则该类应始终被破坏,并释放其内存。那么,为什么第二个参数可能会阻止释放内存呢?

评论

在C ++中,有一个名为“ placement new operator”的结构。可用于在预分配区域中构造对象。例如在堆栈上。在这种情况下,当没有为对象分配内存时,析构函数将无法释放该内存。请查看“如何删除由placement new分配的内存?”在geeksforgeeks.org/placement-new-operator-cpp上,有一个很好的例子。可以像这样直接调用析构函数:“ //不删除:显式调用析构函数。pe->〜Complex();”

有趣的是,我不知道新的刊登位置。因此,然后,在原始代码中调用delete对象可能会使用第二个非空参数调用析构函数,而对析构函数的调用(例如,因为对象位于堆栈上并且超出范围)将具有null第二个参数,因此不要尝试取消分配堆栈上的内存。我理解正确吗?

这正是我对第二个参数的用法的理解。

#1 楼

这些包装器在带有虚拟析构函数的类中使用,可以涵盖两种情况:


确保在对象通过operator delete语句销毁后调用正确的delete pClass;
删除通过new Class[N]分配的数组delete [] class_array;语句中的表达式,以确保使用正确的operator delete删除正确数量的项目并在此过程中处理潜在的异常

我的旧文章中:


当类具有虚拟析构函数时,编译器将生成一个helper
函数-删除析构函数。其目的是确保在销毁类时调用正确的operator delete
用于删除销毁的伪代码的伪代码如下所示:

virtual void * A::'scalar deleting destructor'(uint flags)
{
  this->~A();
  if (flags&1) A::operator delete(this);
};


该函数的地址放在vftable中,而不是
析构函数的地址中。这样,如果另一个类覆盖了virtual
析构函数,则将调用该类的operator delete。尽管在真正的代码中,operator delete很少被覆盖,所以通常
您会看到对默认delete()的调用。

有时编译器还可以
生成向量删除析构函数。其代码如下:

virtual void * A::'vector deleting destructor'(uint flags)
{
  if (flags&2) //destructing a vector
  {
    array = ((int*)this)-1; //array size is stored just before the this pointer
    count = array[0];
    'eh vector destructor iterator'(this,sizeof(A),count,A::~A);
    if (flags&1) A::operator delete(array);
  }
  else {
    this->~A();
    if (flags&1) A::operator delete(this);
  }
};



有关更多详细信息,请参见Visual C ++的主要开发者之一Jan Gray的C ++:幕后。

我还建议您使用自定义运算符将某些类设置为new / delete,然后检查生成的代码。