当我有这样的事情时:
class A
{
virtual void rat();
};
class B : public A
{
virtual void rat() { ; } //implemented!
};
int main(int argc, char **argv)
{
A *a = new B;
delete a;
}
我收到链接器错误:
除非我让基础老鼠变成纯粹的虚拟。
然而,当我有这个:
class A
{
public:
void rat();
};
int main(int argc, char **argv)
{
A a;
}
这个编译很好,并没有给我一个未定义的引用链接错误,除非我明确尝试在我的main(a.rat();
)中调用rat函数。未实现的基类虚函数的规则是什么,然而,在第一个失败的代码片段中,它是在派生类中实现的?
答案 0 :(得分:5)
当两个类都定义虚函数时,C ++编译器需要为类A
和B
构建vtables。要构建A
的vtable,编译器需要A::rat()
- 这是引用的来源。
当A
没有虚函数时,无法从任何地方引用A::rat
,因此您不会收到编译错误。
我相信你知道,你可以通过使A::rat
成为纯虚拟来修复这个错误,从而为vtable提供所需的值(在这种情况下,值为零)。
答案 1 :(得分:4)
需要实现每个非纯虚函数。
class A
{
public: void rat();
};
int main(int argc, char **argv)
{
A a;
}
上面的代码完全是一个不同的场景。在你不调用非纯虚函数的情况下,你既没有得到编译器/链接器错误,也没有调用它,尽管它在类定义中声明了它。
现在在下面的代码中,编译器只检查rat()
中是否存在名为A
的成员函数。
A a;
a.rat(); // Compiler passes. But linker bombs.
现在您应该收到链接器错误。
答案 2 :(得分:2)
由于A::rat()
不是纯虚拟,因此必须具有实现。
答案 3 :(得分:2)
标准法律术语 ODR使用:必须定义使用ODR的功能,否则会出错。
将函数标记为ODR使用的规则非常复杂,基本上它意味着函数以某种方式使用。在第二个示例中,未使用该函数,因此没有必要。
有一个特别警告:virtual
函数(除非它是纯粹的)始终被视为ODR使用。
答案 4 :(得分:2)
因为C ++标准要求它实现。来自C ++03§10.3/ 8:
在类中声明的虚函数应在该类中定义或声明为纯(10.4),或两者兼有;但不需要诊断(3.2)。
因此,您需要将其声明为纯(在右括号后面加= 0
后缀)或定义其实现。
至于在非虚拟情况下不调用函数时没有出现错误的原因,请参阅C ++03§3.2/ 2-3(强调我的)
2)表达式可能被评估,除非它出现在需要整数常量表达式的位置 (见5.19),是
sizeof
运算符(5.3.3)的操作数,或者是typeid
运算符的操作数, 表达式不指定多态类类型的左值(5.2.8)。对象或非超载 如果函数名称出现在可能已评估的表达式中,则使用。 虚拟成员函数是 如果它不纯,则使用。 [...]3)每个程序都应该包含每个非内联函数或使用的对象的一个定义 程序;无需诊断。该定义可以在程序中明确显示,可以在中找到 标准或用户定义的库,或(在适当时)它是隐式定义的(见12.1,12.4和12.8)。 内联函数应在每个使用它的翻译单元中定义。
因此,在非虚拟情况下,如果未使用,则不需要定义。但是在非纯虚拟情况下,即使它没有在代码中明确引用,它仍然被认为是由标准使用,因此它的定义是必需的。