2011-06-15 72 views
5

从C++标准(ISO/IEC 14882:2003(E)),§12.5.4,约超载operator delete重载操作者删除在基类

如果delete表达式与一元开始::运算符,解除分配函数的名称在全局范围内查找。否则,如果使用delete-expression来取消分配其静态类型具有虚拟析构函数的类对象,则释放函数是动态类型的虚拟析构函数(12.4)的定义中的查找找到的函数。否则,如果使用delete-expression来释放类T或其数组的对象,则该对象的静态和动态类型应该相同,并且在T的范围内查找解除分配函数的名称。如果此查找失败找到名字,名字在全球范围内查找。如果查找结果不明确或无法访问,或者查找选择了位置释放函数,则该程序不合格。

§12.5.7也很有意思:

由于成员分配和释放函数是静态的,他们不能是虚的。 [注意:但是,当delete-expression的cast-expression引用类类型的对象时,因为实际调用的释放函数是在作为对象的动态类型的类的范围内查找的,所以如果析构函数是虚拟的,效果是一样的。例如,

struct B { 
    virtual ˜B(); 
    void operator delete(void*, size_t); 
}; 
struct D : B { 
    void operator delete(void*); 
}; 
void f() 
{ 
    B* bp = new D; 
    delete bp; // uses D::operator delete(void*) 
} 

在这里,存储用于d类的非阵列目的通过d释放::操作者删除(),由于虚拟析构函数。]

看完这个,我想知道...

  • 这是标准的一部分,由所有主要的C++编译器(MSVC++,GCC)完全支持?
  • 如果是这样,他们是怎么做到的?隐藏的虚拟功能? “特殊”虚拟析构函数调用? RTTI?
  • 使用标准示例:如果f()和D :: operator delete()在单独的EXE/DLL/DSO中定义,那么会出现问题吗? (假设一切都使用相同的编译器进行编译,当然)

§5.3.5.5也可能是相关的:

在第一种方式(删除对象),如果静态类型的操作数与其动态类型不同,静态类型应该是操作数动态类型的基类,静态类型应该具有虚拟析构函数或行为未定义。在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为是未定义的。

+0

我的猜测:他们走虚拟表。正如标准所说,这个类需要有一个虚拟析构函数以正确的方式工作。对于非重载版本的“delete”也是如此。 – Xeo 2011-06-15 09:14:39

+0

我试着回答,但看起来你真正想要的是让别人查看GCC源代码并向你报告。我想你可以自己做。 – littleadv 2011-06-15 09:45:39

回答

6

我对VC++ ABI了解不多,但Itanium ABI已有详细记录。

the name mangling scheme抬头看,看到:

<ctor-dtor-name> ::= C1  # complete object constructor 
       ::= C2  # base object constructor 
       ::= C3  # complete object allocating constructor 
       ::= D0  # deleting destructor 
       ::= D1  # complete object destructor 
       ::= D2  # base object destructor 

有趣的是:D0 # deleting destructor,这意味着,即使delete是非虚拟的,因为它是从虚析构函数调用时,它可以被认为是虚拟的所有效果和目的。

+0

这很有趣,谢谢。我会将您的答案标记为已接受,至少在有人提出更具体的x86答案之前。 – 2011-06-15 10:11:43

+1

@ e-t172:我不认为你需要'x86'(处理器)特定的答案。这是一个高层次的决定,尽管它展示给ABI,但我严重怀疑VC++,gcc或clang会在后端实现特定的变通办法。 – 2011-06-15 10:20:34

0

挖入汇编代码后在由GCC 4.8发射

的GCC将产生两个片的代码(类,其析构函数是虚拟的):

One is assembly snippet#1 for {Destructor + Dealloc} 
The other is assembly snippet#2 for {Destructor only} 

而对于其析构函数是类不是虚拟的,调用解除分配函数指令将在您调用delete的地方生成。

(经过讨论假设析构函数是虚拟的) 因此,对于下面的代码:

delete C // This will be translate as call snippet#1 for the correct dynamic type 

如果你的代码如下:

p->C::~C() // this will be translate to call snippet#2 

因此解除分配功能与虚拟结合在一起析构函数。 所以我认为这将回答你的问题,关于deallocate函数如何实现像虚拟,但也是静态的。