2015-10-09 58 views
4

我觉得很奇怪,未使用的虚拟函数仍然必须定义,而不像未使用的普通函数。我对含蓄的vtablevpointers有些了解,它们是在创建类对象时创建的 - 这有点回答了问题(该函数必须被定义,以便可以定义指向虚函数的指针),但是这会推动我的查询还在后面。为什么必须定义未使用的虚拟功能?

如果根本没有虚拟函数被调用的可能,为什么需要为函数创建一个条目vtable

class A{ 
    virtual bool test() const; 
}; 

int main(){ 
    A a; //error: undefined reference to 'vtable for A' 
} 

即使我宣布A::test()这是从来没有在程序中使用,但它仍然抛出了一个错误。编译器是否可以不运行程序,并且实现从未调用test() - 因此它不需要vtable条目?或者这是对编译器期望的不合理的事情?

+0

你不能实例化一个虚拟类。但是创建一个空函数或仅仅返回其声明类型的常量是很简单的。 – Logicrat

回答

5

因为在编译器编写者的部分上解决这个问题是一个非常困难的问题,所以当能够离开虚函数的有用性未定时,这个问题至多是可疑的。编译器作者肯定有更好的问题需要解决。

此外,即使您不称之为您正在使用该功能。你正在接受它的地址。

+0

我甚至会说这是不可能解决的:假设你有一个你用'dlopen'打开的库,它提供了一个指向具有未定义的虚函数的类的指针。编译器不可能抓住这个,所以这将是一个运行时错误... – Florian

3

OP说他已经知道vtables和vpointers,所以他明白未使用的虚拟函数和未使用的非虚函数之间存在差异:未使用的非虚函数在任何地方都没有被引用,而虚函数在它的类的vtable中至少引用一次。所以,本质上问题是要问,为什么编译器不够聪明,如果该函数没有在任何地方使用,就不要在vtable中放置对虚函数的引用。这将允许函数也未定义。

编译器一次只能看到一个.cpp文件,所以它不知道是否有某个源文件调用了该函数。

一些工具支持这种分析,他们称之为“全局”分析或类似的东西。你甚至可以在某些编译器中找到它,并且可以通过一些编译器选项进行访问。但它从来没有默认启用,因为它会大大减慢编译速度。

事实上,为什么你可以留下一个非虚函数undefined的原因也与缺乏全局分析有关,但用一种不同的方式:如果编译器知道你已经省略了函数的定义,它可能至少会警告你。但由于它不做全球分析,它不能。这一点可以通过以下事实得到证实:如果您尝试使用未定义的函数,那么错误将不会被编译器捕获:它将被链接器捕获。

所以,只需定义一个空的虚函数,其中包含一个ASSERT(FALSE)并继续你的生活。

+0

@AlexD很好,OP说他已经知道vtable和vpointers,所以我不觉得我需要解决这个问题。但是你是对的,最好是陈述事情,而不是留下暗示。所以,我修改了我的答案。 –

+0

“编译器一次只能看到一个.cpp文件,所以它不知道是否有某个源文件调用了该函数。”如果我有一个调用该函数的源文件,该源文件是否包含该类的头文件(缺少虚拟定义)?如何在不定义源文件中的类的情况下调用成员函数? – AntiElephant

+0

@AntiElephant有一个[声明和定义之间的区别](http://stackoverflow.com/questions/1410563/what-is-the-difference-between-a-definition-and-a-claclaration)。头文件将包含该函数的***声明***,使得任何一段代码都可以调用该函数。但函数的***定义***是您提供实际函数体的位置,通常在一个.cpp文件中完成。 –

0

虚拟函数的重点在于它们可以通过基类指针进行调用。如果您从不使用基类虚函数,那么您为什么定义它?如果使用它,你必须离开父实现(如果它不是纯虚拟的),或者定义你自己的实现,那么通过基类使用你的对象的代码实际上可以使用它。在这种情况下,功能使用的是,它只是没有直接使用。