C++中的虚函数的实现要领
当前位置:以往代写 > C/C++ 教程 >C++中的虚函数的实现要领
2019-06-13

C++中的虚函数的实现要领

C++中的虚函数的实现要领

副标题#e#

进修 C++ 的同志不知道有没有和我一样碰着过这样的狐疑:C++中的虚函数到底怎么实现的?在各类担任干系中,虚函数表的布局到底是什么样的?曾经我是很想虽然,但是厥后在利用ATL的进程中,我发明并不是我想的那样。各人知道,操作C++语言自己的特性举办COM编程虽然是很利便的事,可是你就得随时随地都知道那虚函数内外头到底是些什么对象。讲C++语法的书没有义务汇报你C++发生的虚函数表是什么样的,这就是头痛的地址。

自已做试验是件很快乐的事,我很愿意这么做。

首先写个函数,作为我们尝试的基本。传入虚函数表指针,显示虚数表的内容。

void DispVFT(DWORD* pVFT)
{
printf("VFT Pointer:%p\n" , pVFT);
printf("Begin\n");
DWORD* p = (DWORD* )*pVFT;//获得VFT的首址
while(*p) file://这个处所我是看表项是不是为空来判定是否到了表尾,
file://大大都环境都是对的,不外不能为准
{
printf("VF:%p , %p\n", p , *p);
p++;
}
printf("End\n\n");
}

首先我们看单个类时的虚函数表的环境:

class C1
{
public:
C1()
{
file://printf(%22in/ C1\n");
file://dispvft((dword*)this/);
};
virtual F1()
{}
};
void main()
{
C1 c1;
file://由于C1中没有成员数据,所有我们可以用这种方法判定
file://c1/中的虚函数表指针的个数
printf("vftptr count :%d\n" , sizeof(C1) / 4);
file://显示内存布局
DispVFT((DWORD* )&c1);
}

输出:

vftptr count :1
VFT Pointer:0012FF7C
Begin
VF:00420048 , 00401078(C1::F1)
End


#p#副标题#e#

很纯真,不消多讲,这是我们料想之中的功效。

下面我们举办简朴担任的尝试

class C2 : public C1
{
public:
C2(){
printf("In C1\n");
DispVFT((DWORD*)this);
}
virtual F2()
{}
};
void main()
{
C2 c2;
C1* pC1 = &c2;
printf("vftptr count :%d\n" , sizeof(C2) / 4);
printf("C1\n");
DispVFT((DWORD*)pC1);
printf("C2\n");
DispVFT((DWORD*)&c2);
}

输出:

In C1
VFT Pointer:0012FF7C
Begin
VF:00420048 , 00401087(C1::F1)
End
In C2
VFT Pointer:0012FF7C
Begin
VF:004200F4 , 00401087(C1::F1) file://输出的第一项是表的首址与对应的表项内容,看看地点,与 In C1的差异,说明是差异的两个表
VF:004200F8 , 0040108C(C2::F2)
End
vftptr count :1
C1
VFT Pointer:0012FF7C
Begin
VF:004200F4 , 00401087(C1::F1)
VF:004200F8 , 0040108C(C2::F2)
End
C2
VFT Pointer:0012FF7C
Begin
VF:004200F4 , 00401087(C1::F1)
VF:004200F8 , 0040108C(C2::F2)
End

#p#副标题#e#

各人可以看到最后虚函数表指针仍然是同一个,表中按顺序放入了C1(基类)与C2(派生类)的虚函数指针。

下面是多重担任

class C1
{
public:
C1()
{
file://printf(%22in/ C1\n");
file://dispvft((dword*)this/);
}
virtual F1(){}
};
class C2
{
public:
C2(){
file://printf(%22in/ C1\n");
file://dispvft((dword*)this/);
}
virtual F2(){}
};
class C3 : public C1 , public C2
{
public:
C3(){
file://printf(%22in/ C1\n");
file://dispvft((dword*)this/);
}
virtual F3(){}
};
void main()
{
C3 c3;
C2* pC2 = &c3;
C1* pC1 = &c3;
printf("vftptr count :%d\n" , sizeof(C3) / 4);
printf("C1\n");
DispVFT((DWORD*)pC1);
printf("C2\n");
DispVFT((DWORD*)pC2);
printf("C3\n");
DispVFT((DWORD*)&c3);
}

输出:

vftptr count :2
C1
VFT Pointer:0012FF78
Begin
VF:00420104 , 00401046(C1::F1)
VF:00420108 , 0040101E(C3::F3)
End
C2
VFT Pointer:0012FF7C
Begin
VF:00420100 , 00401028(C2::F2)
VF:00420104 , 00401046(C1::F1)
VF:00420108 , 0040101E(C3::F3)
End
C3
VFT Pointer:0012FF78
Begin
VF:00420104 , 00401046(C1::F1)
VF:00420108 , 0040101E(C3::F3)
End

#p#副标题#e#

虚函数表指针酿成两个了,也就是说此刻是用两个虚函数表指针维护一个表,总结一下就是,虚函数表指针的个数便是基类的个数。至于虚函数表的个数,应该是三个。你可以把我在结构函数中加的代码去掉注释符,看看输出。你会发明每次输出的表的首地点都是纷歧样,那表虽然也不是同一个表。

下面说说多层担任的环境:

#p#分页标题#e#

class C1
{
public:
C1()
{
printf("In C1\n");
DispVFT((DWORD*)this);
}
virtual F1(){}
};
class C2 : public C1
{
public:
C2(){
printf("In C2\n");
DispVFT((DWORD*)this);
}
virtual F2(){}
};
class C3 : public C2
{
public:
C3(){
printf("In C3\n");
DispVFT((DWORD*)this);
}
virtual F3(){}
};
void main()
{
C3 c3;
C2* pC2 = &c3;
C1* pC1 = &c3;
printf("vftptr count :%d\n" , sizeof(C3) / 4);
printf("C1\n");
DispVFT((DWORD*)pC1);
printf("C2\n");
DispVFT((DWORD*)pC2);
printf("C3\n");
DispVFT((DWORD*)&c3);
}

输出:

In C1
VFT Pointer:0012FF7C
Begin
VF:00421090 , 00401046(C1::F1)
End
In C2
VFT Pointer:0012FF7C
Begin
VF:0042010C , 00401046(C1::F1) //这里是类C2的vftable,第一个输出是它首址与表项内容
VF:00420110 , 00401028(C2::F2)
End
In C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046(C1::F1)
VF:004210A0 , 00401028(C2::F2)
VF:004210A4 , 00401078(C3::F3)
End
vftptr count :1
C1
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C2
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End

#p#副标题#e#

获得的功效:我们看到了虚函数表指针是一个,但是你仔细看看每个结构函数的输出!输出的第一项是表的首址与对应的表项。各人可以看到,首址都是纷歧样的这说明是三个差异的表,那么这个类就有三个虚函数表。你大概会想,这三个表在什么时候用呢,事实上,在C3的实例被结构出来后,只有最后一个表,也就是C3的表在用,其它的表跟本就是没有用的,C++在没有通过你同意的环境下,在挥霍你的空间(多重担任也存在同样的问题)。微软想了个步伐把其它的不消的虚函数表去掉:__declspec(novtable)

class __declspec(novtable)C1
{
public:
C1()
{
file://printf(%22in/ C1\n");
file://dispvft((dword*)this/); file://这里得去掉,既然没有谁人表,怎么输出
}
virtual F1(){}
};
class __declspec(novtable)C2 : public C1
{
public:
C2(){
file://printf(%22in/ C2\n");
file://DispVFT((DWORD*)this);//这里得去掉,既然没有谁人表,怎么输出
}
virtual F2(){}
};
class C3 : public C2
{
public:
C3(){
printf("In C3\n");
DispVFT((DWORD*)this);
}
virtual F3(){}
};
void main()
{
C3 c3;
C2* pC2 = &c3;
C1* pC1 = &c3;
printf("vftptr count :%d\n" , sizeof(C3) / 4);
printf("C1\n");
DispVFT((DWORD*)pC1);
printf("C2\n");
DispVFT((DWORD*)pC2);
printf("C3\n");
DispVFT((DWORD*)&c3);
}

输出:

In C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
vftptr count :1
C1
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C2
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End
C3
VFT Pointer:0012FF7C
Begin
VF:0042109C , 00401046
VF:004210A0 , 00401028
VF:004210A4 , 00401078
End

可以看到一切正常,只是在不知不觉中,你的措施瘦身乐成,不外假如你抉择要去掉类的虚函数表,你最好可以确定这个类应该是个被担任的基类,而不是最后派生利用的类。不然大概会堕落,好比:

void func1(C1* p)
{
p->F1();
}
void main()
{
C2 c2;
func1(&c2);
}

    关键字:

在线提交作业