我看vc6中虚继承的实现

这两天试了一下,来说两句

因手头上只有vc6编译器,故只看了vc6的方式

我的测试程序如下
#include "stdafx.h"
class mostbase1
{
public:
 mostbase1():i(1){};
 int i;
};
class mostbase2
{
public:
 mostbase2():j(2){};
 int j;
};

class base1:public virtual mostbase1,public virtual mostbase2
{
};
class base2:public virtual mostbase1,public virtual mostbase2
{
};
class derived:public base1,public base2
{
};
void f(derived* pderived)
{
 mostbase1* pbase1 = pderived;
 mostbase2* pbase2 = pderived;
 int k = pderived->i;
 k = pderived->j;
 k = pbase1->i;
 k = pbase2->j;
}
int main(int argc, char* argv[])
{
 derived d;
 f(&d);
 printf("Hello World!/n");
 return 0;
}
经过我的测试及查看汇编代码,得知vc的虚基类转换确如Inside the c++ objects model中所说是用一个virtual base class table来实现的
base1和base2中各有一个__vbct__ptr(大小是4个字节)用来指向一个virtual base class table,该表格存储着base类中虚基类在base类中的偏移,其后就是数据,当derived继承自base1和base2,首先是存放base1和base2的__vbct_ptr,再就是数据,将base1和base2相同基类的数据放到一起
base1类的内存布局如下(base2与base1相同):
pvoid* __vbct_base1  ----先是虚基类表的地址,此时__vbct_base1数据如下
      ----00 00 00 00 04 00 00 00 08 00 00 00
      ----mostbase1在base1中的偏移为[__vbct_base1+4]的值
int i;     ----mostbase1类中的i
int j;     ----mostbase2类中的j

derived类的内存布局如下:
pvoid* __vbct_base1; ----base1类的虚基类表的地址,其中的值有变化,数据如下
                                ----00 00 00 00 08 00 00 00 0C 00 00 00
pvoid* __vbct_base2; ----base2类的虚基类表的地址,数据如下
                                ----00 00 00 00 04 00 00 00 08 00 00 00
int i;
int j;

 


下面是f(derived* pderived)函数的汇编代码,对其它进行分析

29:   void f(derived* pderived)
30:   {
0040C230   push        ebp
0040C231   mov         ebp,esp
0040C233   sub         esp,60h
0040C236   push        ebx
0040C237   push        esi
0040C238   push        edi
0040C239   lea         edi,[ebp-60h]
0040C23C   mov         ecx,18h
0040C241   mov         eax,0CCCCCCCCh
0040C246   rep stos    dword ptr [edi]
31:       mostbase1* pbase1 = pderived;
0040C248   cmp         dword ptr [ebp+8],0          [1]
0040C24C   jne         f+27h (0040c257)
0040C24E   mov         dword ptr [ebp-18h],0        [2]
0040C255   jmp         f+35h (0040c265)
0040C257   mov         eax,dword ptr [ebp+8]
0040C25A   mov         ecx,dword ptr [eax]          [3]
0040C25C   mov         edx,dword ptr [ebp+8]
0040C25F   add         edx,dword ptr [ecx+4]        [4]
0040C262   mov         dword ptr [ebp-18h],edx
0040C265   mov         eax,dword ptr [ebp-18h]
0040C268   mov         dword ptr [ebp-4],eax        [5]
32:       mostbase2* pbase2 = pderived;
0040C26B   cmp         dword ptr [ebp+8],0
0040C26F   jne         f+4Ah (0040c27a)
0040C271   mov         dword ptr [ebp-1Ch],0        [6]
0040C278   jmp         f+58h (0040c288)
0040C27A   mov         ecx,dword ptr [ebp+8]
0040C27D   mov         edx,dword ptr [ecx]          [7]
0040C27F   mov         eax,dword ptr [ebp+8]  
0040C282   add         eax,dword ptr [edx+8]        [8]
0040C285   mov         dword ptr [ebp-1Ch],eax
0040C288   mov         ecx,dword ptr [ebp-1Ch]
0040C28B   mov         dword ptr [ebp-8],ecx
33:       base1* p1 = pderived;
0040C28E   mov         edx,dword ptr [ebp+8]
0040C291   mov         dword ptr [ebp-0Ch],edx      [9]
34:       p1->i = 1;
0040C294   mov         eax,dword ptr [ebp-0Ch]
0040C297   mov         ecx,dword ptr [eax]
0040C299   mov         edx,dword ptr [ecx+4]        [10]
0040C29C   mov         eax,dword ptr [ebp-0Ch]
0040C29F   mov         dword ptr [eax+edx],1
35:       base2* p2 = pderived;
0040C2A6   cmp         dword ptr [ebp+8],0
0040C2AA   je          f+87h (0040c2b7)
0040C2AC   mov         ecx,dword ptr [ebp+8]
0040C2AF   add         ecx,4                                 [11]
0040C2B2   mov         dword ptr [ebp-20h],ecx
0040C2B5   jmp         f+8Eh (0040c2be)
0040C2B7   mov         dword ptr [ebp-20h],0
0040C2BE   mov         edx,dword ptr [ebp-20h]
0040C2C1   mov         dword ptr [ebp-10h],edx      [12]
36:       p2->j = 1;
0040C2C4   mov         eax,dword ptr [ebp-10h]
0040C2C7   mov         ecx,dword ptr [eax]
0040C2C9   mov         edx,dword ptr [ecx+8]        [13]
0040C2CC   mov         eax,dword ptr [ebp-10h]
0040C2CF   mov         dword ptr [eax+edx],1
37:       int k = pderived->i;
0040C2D6   mov         ecx,dword ptr [ebp+8]
0040C2D9   mov         edx,dword ptr [ecx]
0040C2DB   mov         eax,dword ptr [edx+4]        [14]
0040C2DE   mov         ecx,dword ptr [ebp+8]
0040C2E1   mov         edx,dword ptr [ecx+eax]      [15]
0040C2E4   mov         dword ptr [ebp-14h],edx
38:       k = pderived->j;
0040C2E7   mov         eax,dword ptr [ebp+8]
0040C2EA   mov         ecx,dword ptr [eax]
0040C2EC   mov         edx,dword ptr [ecx+8]        [16]
0040C2EF   mov         eax,dword ptr [ebp+8]
0040C2F2   mov         ecx,dword ptr [eax+edx]
0040C2F5   mov         dword ptr [ebp-14h],ecx
39:
40:   }
0040C2F8   pop         edi
0040C2F9   pop         esi
0040C2FA   pop         ebx
0040C2FB   mov         esp,ebp
0040C2FD   pop         ebp
0040C2FE   ret


[1]dword ptr [ebp+8h]就是pderived,先看看是不是为NULL.
[2]dword ptr [ebp-18h]是一个中间变量,当pderived为NULL时,将其也赋为NULL
[3]取出__vbct_base1,其位置在derived类的开始处
[4]取出mostbast1类在derived中的偏移,此值在_vbct_base1+4的位置,占用4个字节,其值为8, 因为derived前有两个__vbct_prt,都为4字节,故mostbast1在derived的偏移为8.
[5]将pbase1赋值,dword ptr [ebp-4]存放pbase1;
[6]同(2),只是中间变量的地址不同
[7]同(3),取出__vbct_base1
[8]同(4), 取出mostbast2类在derived中的偏移,此时为12
[9]取出derived类中的base1的地址,也就是derived中__vbct_base1的地址,可能你有疑问,看下面
[10]用p1存取数据时,还是通过__vbct_base1来做的,通过__vbct_base1得到mostbase1在derived中 的偏移,最后得到的地址是pderived+8
[11]现在取出__vbct_base2的地址,看到了add ecx,4么
[12]将p2赋值
[13]通过__vbct_base2来取得mostbase2的地址,再来存取j
[14]通过__vbct_base1来取得mostbase1的地址
[15]偏移在eax中,取出i来
[16]通过__vbct_base1来取得mostbase2的地址

以下是我构想的c伪码,可能不太正确,因为汇编代码已经优化过
void f(derived* pderived)
{
 ----mostbase1* pbase1 = pderived;
 mostbase1 *pbase1,*temp1;
 if (pderived == 0)
 {
  temp1 = 0;
 }
 else
 {
  temp1 = (mostbase1*)(pderived+(pderived->__vbct_base1[1]));
 }
 pbase1 = temp1;

 ----mostbase2* pbase2 = pderived;
 mostbase2 *pbase2,*temp2;
 if (pderived == 0)
 {
  temp2 = 0;
 }
 else
 {
  temp2 = (mostbase2*)(pderived+(pderived->__vbct_base1[2]));
 }
 pbase2 = temp2;
 ----base1* p1 = pderived;
 base1* p1 = &pderived->__vbct_base1;
 ----p1->i = 1;
 (mostbase1*)(p1+p1->__vbct_base1[1])->i = 1;
 ----base2* p2 = pderived;
 base2* p2 = &pderived->__vbct_base2;
 ----p2->j = 1;
 (mostbase2*)(p2+p2->__vbct_base2[2])->j = 1;
 ----int k = pderived->i;
 int k = (mostbase1*)(pderived+pderived->__vbct_base1[1])->i;
 ----k = pderived->j;
 k = (mostbase2*)(pderived+pderived->__vbct_base1[2])->j;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: vc2020 cdhtmldialog是继承自MFC的CDHtmlDialog类的。CDHtmlDialog类是MFC框架提供的一个用于创建基于HTML和脚本的自定义对话框的类。 CDHtmlDialog类可以让开发者使用HTML和脚本语言(如JavaScript)来创建对话框上的控件、界面和交互行为。它基于IE浏览器引擎,能够支持HTML和脚本语言的强大功能,使得开发者能够以更灵活和可扩展的方式来构建用户界面。 继承自CDHtmlDialog类的vc2020 cdhtmldialog可以根据具体需求来定制化自己的对话框。通过继承CDHtmlDialog类,可以在自己的对话框上添加各种HTML控件,如按钮、文本框、下拉框等,并通过JavaScript脚本来实现它们的交互行为。 CDHtmlDialog类提供了一系列重要的成员函数和事件处理函数,用于处理HTML控件的事件、脚本的执行和数据传递等。开发者可以根据需要重写这些函数,实现自己对控件和脚本的处理逻辑。 与传统的MFC对话框相比,vc2020 cdhtmldialog具有更高的扩展性和灵活性。通过使用HTML和脚本语言,开发者可以实现更复杂和动态的用户界面,以及更强大的交互功能。同时,CDHtmlDialog类的封装也简化了开发过程,降低了开发难度。 综上所述,vc2020 cdhtmldialog继承自MFC的CDHtmlDialog类,通过HTML和脚本语言来构建自定义对话框,具有高度的灵活性和扩展性,能够实现复杂的界面和交互功能。 ### 回答2: vc2020 cdhtmldialog是一个可以用来创建基于HTML的对话框的类。它使用CDHtmlDialog类作为基类,通过继承该类,我们可以方便地创建自己的HTML对话框。 继承是面向对象编程的一个重要概念,通过继承,我们可以从已有的类派生出新的类,新的类将拥有原有类的属性和方法。对于vc2020 cdhtmldialog来说,继承可以让我们自定义HTML对话框的行为和外观,以满足我们的特定需求。 继承vc2020 cdhtmldialog时,我们可以重写其父类的方法,实现自己的逻辑。例如,我们可以重写OnInitDialog方法,在对话框初始化时执行一些自定义操作;或者重写OnDocumentComplete方法,当HTML文档加载完成时执行一些需要的操作。 另外,我们还可以通过继承来添加新的属性和方法,以扩展vc2020 cdhtmldialog的功能。我们可以在新的类添加新的成员变量和成员函数,并在代码使用它们。 总之,通过继承vc2020 cdhtmldialog,我们可以根据自己的需求创建出功能丰富、个性化的HTML对话框。继承的优势在于代码的复用和扩展性,使得我们可以更加灵活地定制和开发HTML对话框应用程序。 ### 回答3: vc2020的cdhtmldialog是基于CDHtmlDialog类的,它允许我们创建使用HTML和脚本的用户界面。继承是一种面向对象编程的概念,它允许我们从已有的类派生出新的类,并继承它的属性和方法。 继承CDHtmlDialog类有几个好处。首先,我们可以利用CDHtmlDialog已有的功能来构建我们自己的用户界面。CDHtmlDialog类已经集成了许多常用的HTML和脚本控件,比如按钮、文本框、下拉列表等,我们可以直接使用它们来设计我们的界面,而无需从头编写这些控件的样式和行为。 此外,通过继承CDHtmlDialog类,我们可以重写其函数以实现我们的逻辑。CDHtmlDialog类提供了一些虚函数,比如OnInitDialog()、DoDataExchange()和OnDocumentComplete()等,通过重写这些函数,我们可以在特定的事件发生时执行我们自己的代码。例如,在OnInitDialog()函数,我们可以初始化一些数据或者执行一些必要的操作;在OnDocumentComplete()函数,我们可以处理网页加载完成后的逻辑。 另外,继承CDHtmlDialog类还使得我们能够对话框进行更加灵活的自定义。我们可以通过添加新的成员函数和成员变量来实现我们自己的功能,或者通过重写基类的函数来改变原有的行为。这种灵活性使得我们可以根据具体需求对对话框进行定制,满足项目的特殊要求。 总之,通过继承CDHtmlDialog类,我们可以构建基于HTML和脚本的用户界面,并且能够对其进行进一步的定制和自定义。这样,我们能够更加方便地开发出功能强大且美观的用户界面,提升用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值