今天偶然看到这个面试题,第一感觉是既然所有对象共享虚函数代码,那么虚函数指针对所有对象就是一样的,因此虚函数表也就是一样的,没有必要为每个对象复制一份一模一样的虚函数表。所以做个验证,在这儿记录一下。如有错误,欢迎留言指正,感谢感谢。
1. 单一虚函数表
验证代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
class A
{
public:
A() { cout << "A()" << endl; }
virtual void f() { cout << "f()" << endl; }
virtual void g() { cout << "g()" << endl; }
virtual ~A() { cout << "~A()" << endl; }
};
typedef void(*Func)();
int main()
{
A a1, a2;
ll* vptr;
vptr = (ll*)*(ll*)(&a1);
cout << "a1虚函数表地址: "<< vptr << endl;
Func f = (Func)(*vptr);
cout << "a1.f()地址: " << (ll*)f << endl;
f();
f = (Func)*(vptr+1);
cout << "a1.g()地址: " << (ll*)f << endl;
f();
vptr = (ll*)*(ll*)(&a2);
cout << "a2虚函数表地址: " << vptr << endl;
f = (Func)(*vptr);
cout << "a2.f()地址: " << (ll*)f << endl;
f();
f = (Func)*(vptr + 1);
cout << "a2.g()地址: " << (ll*)f << endl;
f();
return 0;
}
输出结果:
VS2017 x64平台 | Ubuntu1604 64位(g++5.4.0) |
---|---|
A() A() a1虚函数表地址: 00007FF7FB04ADB8 a1.f()地址: 00007FF7FB041456 f() a1.g()地址: 00007FF7FB0414A1 g() a2虚函数表地址: 00007FF7FB04ADB8 a2.f()地址: 00007FF7FB041456 f() a2.g()地址: 00007FF7FB0414A1 g() ~A() ~A() | A() A() a1虚函数表地址:0x400f20 a1.f()地址: 0x400d3e f() a1.g()地址: 0x400d6a g() a2虚函数表地址: 0x400f20 a2.f()地址: 0x400d3e f() a2.g()地址: 0x400d6a g() ~A() ~A() |
输出f和g的地址以及对f和g进行调用是为了验证程序的正确性。无论是VS还是g++,两个对象的虚函数表地址都是一样的,说明两个对象共享虚函数表。
2. 多个虚函数表
既然单一虚函数表类的对象都共享虚函数表,那么无论继承类的对象有多少个虚函数表,这些虚函数表应该都是共享的。下面验证:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
class A
{
public:
virtual void fA() { cout << "fA()" << endl; }
virtual void gA() { cout << "gA()" << endl; }
};
class B
{
public:
virtual void fB() { cout << "fB()" << endl; }
virtual void gB() { cout << "gB()" << endl; }
};
class C: public A, public B
{
};
typedef void(*Func)();
int main()
{
C c1, c2;
ll* vptr;
vptr = (ll*)*(ll*)(&c1);
cout << "c1虚函数表1地址: "<< vptr << endl;
Func f = (Func)(*vptr);
cout << "c1.fA()地址: " << (ll*)f << endl;
f();
f = (Func)*(vptr+1);
cout << "a1.gA()地址: " << (ll*)f << endl;
f();
vptr = (ll*)*(ll*)(&c2);
cout << "c2虚函数表1地址: " << vptr << endl;
f = (Func)(*vptr);
cout << "a2.fA()地址: " << (ll*)f << endl;
f();
f = (Func)*(vptr + 1);
cout << "a2.gA()地址: " << (ll*)f << endl;
f();
cout << endl;
vptr = (ll*)*((ll*)(&c1)+1);
cout << "c1虚函数表2地址: " << vptr << endl;
f = (Func)(*vptr);
cout << "c1.fB()地址: " << (ll*)f << endl;
f();
f = (Func)*(vptr + 1);
cout << "a1.gB()地址: " << (ll*)f << endl;
f();
vptr = (ll*)*((ll*)(&c2)+1);
cout << "c2虚函数表2地址: " << vptr << endl;
f = (Func)(*vptr);
cout << "a2.fB()地址: " << (ll*)f << endl;
f();
f = (Func)*(vptr + 1);
cout << "a2.gB()地址: " << (ll*)f << endl;
f();
return 0;
}
输出如下:
VS2017 x64平台 | Ubuntu1604 64位(g++5.4.0) |
---|---|
c1虚函数表1地址: 00007FF6BACCBE18 c1.fA()地址: 00007FF6BACC12F3 fA() a1.gA()地址: 00007FF6BACC12B7 gA() c2虚函数表1地址: 00007FF6BACCBE18 a2.fA()地址: 00007FF6BACC12F3 fA() a2.gA()地址:00007FF6BACC12B7 gA() c1虚函数表2地址: 00007FF6BACCBE38 c1.fB()地址: 00007FF6BACC1244 fB() a1.gB()地址: 00007FF6BACC11DB gB() c2虚函数表2地址: 00007FF6BACCBE38 a2.fB()地址: 00007FF6BACC1244 fB() a2.gB()地址: 00007FF6BACC11DB gB() | c1虚函数表1地址: 0x401028 c1.fA()地址: 0x400d76 fA() a1.gA()地址: 0x400da2 gA() c2虚函数表1地址: 0x401028 a2.fA()地址: 0x400d76 fA() a2.gA()地址: 0x400da2 gA() c1虚函数表2地址: 0x401048 c1.fB()地址: 0x400dce fB() a1.gB()地址: 0x400dfa gB() c2虚函数表2地址: 0x401048 a2.fB()地址: 0x400dce fB() a2.gB()地址: 0x400dfa gB() |
可以看出,c1和c2的两个虚函数表地址仍然是一样的。
结论:如果一个类有虚函数,那么这个类的所有对象共享一个虚函数表。