如果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;
}
都成功编译。
如何解释这个?
答案 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
的对象,就不可能没有构造函数和析构函数的定义,即使您没有'自己定义它们;也就是说,编译器会隐式生成定义。)