我最近对C ++虚拟表有疑问。
为什么C ++使用虚拟表?
=>因为C ++编译器不知道实际的函数地址
--->为什么
=>因为C ++编译器不知道对象的确切类型(Cat?Dog?Animal?)指针" panimal"指向
---为什么呢?编译器可以用任何方式找出对象类型吗?
=>是的,我认为编译器可以通过跟踪对象类型来实现。
让我们考虑一下对象指针获取其值的来源。确实有2个来源。
因此,通过向后跟踪分配线程到原始源对象
=>编译器能够找出指针的确切类型。
=>编译器知道所调用的确切函数的地址
=>不需要虚拟表。
对象类型跟踪保存每个类实例的虚拟表memery和虚拟表指针。
对象类型跟踪在哪里不起作用?
图书馆链接。
如果库函数返回基类指针,则编译器无法跟踪回原始源对象。编译器可能适应库代码和非库代码。对于导出的库类,请使用虚拟表。对于其他类,只需跟踪该对象类型以节省内存。
我不确定上述陈述中是否存在错误,如果有的话,请指出。提前谢谢〜
答案 0 :(得分:1)
在某些情况下,是的,编译器可以找出指针在编译时指向的类型。构建一个不能的案例很容易。
int x;
cin >> x;
Animal* p;
if (x == 10)
p = new Cat();
else
p = new Dog();
如果编译器在所有情况下都可以证明对象的类型,则可以根据as-if规则从其生成的代码中消除虚拟表。
答案 1 :(得分:0)
编译器能够找出指针的确切类型。
是的,但是你希望它如何在运行时调用正确的函数?编译器知道,但是c ++没有虚拟机告诉它在运行时传递的对象的类型,ergo,需要vtable用于继承类型的虚函数。 您是否希望编译器为导致每个虚函数执行的所有不同代码路径创建代码,以便它在运行时调用正确的函数?如果可能的话,这将导致更大的二进制文件。
答案 2 :(得分:0)
在这个例子中,很明显,无论编译器可以采用什么样的静态代码分析,在ptrA-> f()调用的实际方法都是如此;只能在运行时知道。
#include <sys/time.h>
#include <iostream>
#include <stdlib.h>
struct A {
virtual int f()
{
std::cout<<"class A\n";
}
};
struct B: public A {
int f()
{
std::cout<<"class B\n";
}
};
int main()
{
A objA;
B objB;
A* ptrA;
timeval tv;
gettimeofday(&tv, NULL);
unsigned int seed = (unsigned int)tv.tv_sec;
int randVal = rand_r(&seed);
if( randVal < RAND_MAX/2)
{
ptrA=&objA;
}
else
{
ptrA=&objB;
}
ptrA->f();
return 0;
}`