編譯器將虛函數(shù)表在編譯時(shí)存放在對(duì)象的頭部,因此我們可以自由的訪問虛函數(shù)。
1. 訪問對(duì)象的數(shù)函數(shù)表。
比如有這樣一個(gè)類:
class A
{
public:
virtual int test1()
{
printf("A::test1\n");
return 0;
}
virtual int test2()
{
printf("A::test2\n");
return 0;
}
virtual int test3()
{
printf("A::test3\n");
return 0;
}
};
// 創(chuàng)建A的對(duì)象a
A a;
通過查找虛函數(shù)表來訪問該對(duì)象的虛函數(shù):
typedef int (* FUNC)(void);
FUNC func = (FUNC)*(int *)*((int *)&a + 0);
func();
func = (FUNC)*((int *)*(int *)&a + 1);
func();
func = (FUNC)*((int *)*(int *)&a + 2);
func();
對(duì)象a的虛函數(shù)表布局
A::test1 A::test2 A::test3
2. 子類重載了父類的虛函數(shù)。
比如:
class B:public A
{
public:
virtual int test1()
{
printf("B::test1\n");
return 0;
}
};
那么對(duì)象b的虛函數(shù)表布局應(yīng)該是:
B::test1 A::test2 A::test3
3. 子類沒有重載父類的虛函數(shù)
比如:
class B:public A
{
public:
virtual int test4()
{
printf("B::test4\n");
return 0;
}
};
那么對(duì)象b的虛函數(shù)表布局應(yīng)該是:
A::test1 A::test2 A::test3 B::test1
4. 多重繼承,有重載。
比如:
class A
{
public:
virtual int test1()
{
printf("A::test1\n");
return 0;
}
virtual int test2()
{
printf("A::test2\n");
return 0;
}
virtual int test3()
{
printf("A::test3\n");
return 0;
}
};
class C
{
public:
virtual int test1()
{
printf("C::test1\n");
return 0;
}
virtual int test4()
{
printf("C::test4\n");
return 0;
}
};
class B:public A, public C
{
public:
virtual int test1()
{
printf("B::test1\n");
return 0;
}
};
那么對(duì)象b的虛函數(shù)表布局應(yīng)該是:
B::test1 A::test2 A::test3
B::test1 C::test4
5. 多重繼承,沒有重載。
class B:public A, public C
{
public:
virtual int test5()
{
printf("B::test5\n");
return 0;
}
};
那么對(duì)象b的虛函數(shù)表布局應(yīng)該是:
A::test1 A::test2 A::test3 b::test5
C::test1 C::test4