C++单个类的所有对象是否共享虚函数表的验证

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a_big_pig/article/details/78018194

今天偶然看到这个面试题,第一感觉是既然所有对象共享虚函数代码,那么虚函数指针对所有对象就是一样的,因此虚函数表也就是一样的,没有必要为每个对象复制一份一模一样的虚函数表。所以做个验证,在这儿记录一下。如有错误,欢迎留言指正,感谢感谢。

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的两个虚函数表地址仍然是一样的。

结论:如果一个类有虚函数,那么这个类的所有对象共享一个虚函数表。

展开阅读全文

没有更多推荐了,返回首页