【问题标题】:Overloading operator delete in a base class在基类中重载运算符删除
【发布时间】:2011-06-15 09:09:10
【问题描述】:

来自 C++ 标准 (ISO/IEC 14882:2003(E)),§12.5.4,关于重载operator delete

如果删除表达式以一元 :: 运算符开头,则在全局范围内查找解除分配函数的名称。否则,如果 delete-expression 用于释放静态类型具有虚拟析构函数的类对象,则释放函数是在动态类型的虚拟析构函数 (12.4) 的定义中查找的函数。否则,如果删除表达式用于释放对象 类 T 或其数组,对象的静态和动态类型应相同,并在 T 范围内查找释放函数的名称。如果此查找未找到名称,则在全局范围内查找名称。如果查找的结果不明确或无法访问,或者查找选择了一个放置解除分配函数,则程序是非良构的。

§12.5.7 也很有趣:

由于成员分配和释放函数是静态的,它们不能是虚拟的。 [注意:但是,当删除表达式的强制转换表达式引用类类型的对象时,因为实际调用的释放函数是在作为对象的动态类型的类的范围内查找的,如果析构函数是虚的,效果是一样的。例如,

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::operator delete()释放。]

看完之后,我想知道......

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

§5.3.5.5 也可能是相关的:

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

【问题讨论】:

  • 我的猜测:他们走的是虚拟桌子。正如标准所说,该类需要有一个虚拟析构函数才能以正确的方式工作。对于delete 的非重载版本也是如此。
  • 我试图回答,但您似乎真正想要的是有人查看 GCC 源代码并向您报告。我想你可以自己做。

标签: c++ operator-overloading


【解决方案1】:

我对 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 是非虚拟的,由于它是从虚拟析构函数调用的,因此对于所有效果和用途都可以认为是虚拟的。

【讨论】:

  • 这很有趣,谢谢。我会将您的答案标记为已接受,至少在有人提出更具体的 x86 答案之前。
  • @e-t172:我认为您不需要x86(处理器)特定的答案。这是一个高层次的决定,虽然它向 ABI 展示,但我严重怀疑 VC++、gcc 或 clang 是否会为此在后端实现特定的解决方法。
【解决方案2】:

在 GCC 4.8 中深入了解 Emit 中的汇编代码

GCC 会生成两段代码(对于析构函数为虚的类):

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

而对于析构函数不是virtual的类,调用deallocation函数指令会在你调用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函数与虚析构函数绑定在一起。 所以我认为这将回答你关于 deallocate 函数如何像虚拟但也是静态的实现的问题。

【讨论】:

    猜你喜欢
    • 2014-09-27
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-26
    相关资源
    最近更新 更多