如果operator delete没有实现,为什么不编译

时间:2015-02-28 08:45:19

标签: c++

如果operator delete没有实现,为什么不能编译?


class A
{ 
public: 
    virtual ~A(){ } 
private: 
    void operator delete(void *p); 
};

int main() 
{
    A a;
}

使用g ++编译,得到以下错误:

对'A :: operator delete(void *)'

的未定义引用

如果给它一个空的实现操作符删除

class A
{ 
public: 
    virtual ~A(){ } 
private: 
    void operator delete(void *p) {}
};

int main() 
{
    A a;
}

或删除"虚拟"

class A
{ 
public: 
    ~A(){ } 
private: 
    void operator delete(void *p); 
};

int main() 
{
    A a;
}

都成功编译。

如何解释这个?

1 个答案:

答案 0 :(得分:2)

嗯,之间存在巨大差异:

void operator delete(void *p); 

void operator delete(void *p) {}

前者只是声明函数;后者定义它。当你声明一个函数时,你基本上只是说"有一个函数,这里是它的参数类型和它的返回值",但是当你定义一个函数时,你写下来了实际包含该函数的代码。用一对空括号定义的函数什么也不做;声明但未定义的函数可能会做任何事情(在看到定义之前,您无法知道)。显然,除非已定义函数,否则无法调用函数。不太明显,除非已经定义,否则函数不能使用其地址。显然,这两个陈述都暗示如果你调用函数或获取函数的地址,并且你没有定义它,那么你的程序就是格式错误。

通常,未能定义需要定义的内容的结果是链接器错误。这是因为当您以需要定义的方式使用某些东西时,编译器会创建一个符号,告诉链接器在找到定义后将地址插入所需的位置。如果链接器找不到定义(以及地址),那么它就无法完成它的工作。

C ++标准有一套神秘的规则,它们基本上可以告诉您在什么条件下定义 required 的功能。有时它会,有时它不会。一个简单的例子:

int f();
int main() {}

这很好;函数f永远不会被隐式或显式调用;它的地址永远不会被拿走;它甚至没有被召唤的机会。因此,即使未定义f,此程序也会编译。

根据标准,如果 odr-used ,必须定义一个函数。关于operator delete([basic.def.odr] / 3),该标准有如下说明:

  

...非展示位置分配或解除分配功能   一个类由该类的构造函数的定义使用。非放置释放函数   一个类由该类的析构函数的定义使用,或者通过查找选择   虚拟析构函数的定义点(12.4)。 (脚注26)......

因此:如果您的类定义了构造函数或析构函数,那么它会使用您的operator delete。因此,你的析构函数是否是虚拟的并不重要;由于您定义了析构函数,因此您的程序格式不正确而没有operator delete的定义。 [1]

现在,您可能会问为什么当析构函数是非虚拟的时,您的编译器和链接器不会抱怨。好吧,工具链有时会接受代码,即使他们应该拒绝代码。在这种特殊情况下,如果析构函数不是虚拟的,那么您的程序实际上不需要operator delete的地址,因此如果您从未定义它,链接器将不会抱怨。但是当你将析构函数设置为虚拟时,因为释放函数的查找基于被释放的对象的动态类型,所以需要有一个用于解除分配函数的vtable条目,这意味着它的地址必须是已知的。因此链接错误。 (同样,我想重申一下,即使你的编译器和链接器在析构函数是非虚拟的时候接受代码,但是通过阅读标准,它仍然是错误的代码。)

[1]请注意,只要您实际创建类型为A的对象,就不可能没有构造函数和析构函数的定义,即使您没有'自己定义它们;也就是说,编译器会隐式生成定义。)