从前面的学习我们已经知道如何给 定义一个类成员函数指针了.
那么成员函数也是可以通过这种方式定义的,不过有些细节上的不同个,参见代码
class Teacher26base {
public:
Teacher26base() {
}
void Teacher26basefun() {
cout << "Teacher26basefun called" << endl;
}
};
class Teacher26son : public Teacher26base {
public:
Teacher26son() {
}
void Teacher26sonfun() {
cout << "Teacher26sonfun called" << endl;
}
int static Teacher26sonstaticfunc(string str) {
cout << "Teacher26sonstaticfunc called str = " << str << endl;
return 10;
}
};
void quanjuTeacher26() {
cout << "全局quanjuTeacher26 called" << endl;
}
void main() {
//旧的回忆,对于全局函数的访问,可以通过定义一个函数指针类型
//1.定义一个 函数指针 类型 QuanJuFuncType
typedef void(*QuanJuFuncType)();
//2.使用类型 定义 变量 func,由于是一个指针,
QuanJuFuncType func = quanjuTeacher26;
func();
//3.直接定义一个函数指针类型
void(*jutiQuanJuFunc)();
jutiQuanJuFunc = &quanjuTeacher26;
//jutiQuanJuFunc = quanjuTeacher26;//对于全局函数,前面加不加&都一样
jutiQuanJuFunc();
//4.对于类成员函数,我们可以这么干吗,是的,也可以这么干,不过前面要加上类命名空间
void(Teacher26son::*jutiQuanJuFuncTeacher26son)();
//4.1 但是在类成员指针的赋值的时候,一定要加上&
jutiQuanJuFuncTeacher26son = &Teacher26son::Teacher26sonfun;
//4.2 调用时,需要一个类对象,//注意调用的时候,是(对象.*xxx)(),这是为了和类成员函数 tea.xxx区分
Teacher26son tea;
(tea.*jutiQuanJuFuncTeacher26son)();
//5.如果是类静态成员函数
int(*staticfunc)(string tempvalue) = &Teacher26son::Teacher26sonstaticfunc;//静态成员也是一样的,不同的是,需要加上类命名空间,注意的是和 全局函数一样,前面加不加 &都可以,加不加& 是历史原因。
int result = staticfunc("abc");
cout << "result = " << result << endl;
}
如果要弄一个 指向类成员虚函数呢?
class Teacher26base {
public:
Teacher26base() {
}
void Teacher26basefun() {
cout << "Teacher26basefun called" << endl;
}
int virtual Teacher26basevirtualfunc(double dou) {
cout << "Teacher26basevirtualfunc called dou = " << dou << endl;
int inttemp = (int)dou;
return inttemp;
}
int virtual Teacher26basevirtualfunc2(double dou) {
cout << "Teacher26basevirtualfunc2 called dou = " << dou << endl;
int inttemp = (int)dou * 2;
return inttemp;
}
};
class Teacher26son : public Teacher26base {
public:
Teacher26son() {
}
void Teacher26sonfun() {
cout << "Teacher26sonfun called" << endl;
}
int static Teacher26sonstaticfunc(string str) {
cout << "Teacher26sonstaticfunc called str = " << str << endl;
return 10;
}
int virtual Teacher26basevirtualfunc(double dou) {
cout << "Teacher26sonvirtualfunc called dou = " << dou << endl;
int inttemp = (int)dou;
return inttemp;
}
int virtual Teacher26basevirtualfunc2(double dou) {
cout << "Teacher26sonvirtualfunc2 called dou = " << dou << endl;
int inttemp = (int)dou * 2;
return inttemp;
}
};
void quanjuTeacher26() {
cout << "全局quanjuTeacher26 called" << endl;
}
void main() {
//旧的回忆,对于全局函数的访问,可以通过定义一个函数指针类型
//1.定义一个 函数指针 类型 QuanJuFuncType
typedef void(*QuanJuFuncType)();
//2.使用类型 定义 变量 func,由于是一个指针,
QuanJuFuncType func = quanjuTeacher26;
func();
//3.直接定义一个函数指针类型
void(*jutiQuanJuFunc)();
jutiQuanJuFunc = &quanjuTeacher26;
//jutiQuanJuFunc = quanjuTeacher26;//对于全局函数,前面加不加&都一样
jutiQuanJuFunc();
//对于类成员函数,我们可以这么干吗,是的,也可以这么干,不过前面要加上类命名空间
void(Teacher26son::*jutiQuanJuFuncTeacher26son)();
jutiQuanJuFuncTeacher26son = &Teacher26son::Teacher26sonfun;//但是在类成员指针的赋值的时候,一定要加上&
//调用时,需要一个类对象,
Teacher26son tea;
(tea.*jutiQuanJuFuncTeacher26son)();//注意调用的时候,是(对象.*xxx)(),这是为了和类成员函数 tea.xxx区分
//如果是静态成员函数
int(*staticfunc)(string tempvalue) = &Teacher26son::Teacher26sonstaticfunc;//静态成员也是一样的,不同的是,需要加上类命名空间,注意的是和 全局函数一样,前面加不加 &都可以,加不加& 是历史原因。
int result = staticfunc("abc");
cout << "result = " << result << endl;
//5.如果是虚函数呢?由于虚函数也是依赖于 类对象调用的,也就是也需要this指针
// 虚函数是如下的两个:int virtual Teacher26sonvirtualfunc(double dou)
// int virtual Teacher26sonvirtualfunc2(double dou)
//5.1 定义如下
int(Teacher26base::*vir)(double tempdou) = &Teacher26base::Teacher26basevirtualfunc;
//5.2 使用 类指针 调用 虚函数指针类型
Teacher26base *ptea26 = new Teacher26son();
result = (ptea26->*vir)(3.14);
00A6F46E mov esi,esp
00A6F470 sub esp,8
00A6F473 movsd xmm0,mmword ptr [__real@40091eb851eb851f (0A79F78h)]
00A6F47B movsd mmword ptr [esp],xmm0
00A6F480 mov ecx,dword ptr [ptea26]
00A6F483 call dword ptr [vir]
00A6F486 cmp esi,esp
00A6F488 call __RTC_CheckEsp (0A61596h)
00A6F48D mov dword ptr [result],eax
cout << "result1 = " << result << endl;
//5.3 使用 类对象 调用 虚函数指针类型
Teacher26base tea26;
result = (tea26.*vir)(78.8);
00A6F4D7 mov esi,esp
00A6F4D9 sub esp,8
00A6F4DC movsd xmm0,mmword ptr [__real@4053b33333333333 (0A79F98h)]
00A6F4E4 movsd mmword ptr [esp],xmm0
00A6F4E9 lea ecx,[tea26]
00A6F4EC call dword ptr [vir]
00A6F4EF cmp esi,esp
00A6F4F1 call __RTC_CheckEsp (0A61596h)
00A6F4F6 mov dword ptr [result],eax
cout << "result2 = " << result << endl;
//5.4 通过 类指针 正常调用 虚函数
ptea26->Teacher26basevirtualfunc(3.14);
00A6F538 mov esi,esp
00A6F53A sub esp,8
00A6F53D movsd xmm0,mmword ptr [__real@40091eb851eb851f (0A79F78h)]
00A6F545 movsd mmword ptr [esp],xmm0
00A6F54A mov eax,dword ptr [ptea26]
00A6F54D mov edx,dword ptr [eax]
00A6F54F mov ecx,dword ptr [ptea26]
00A6F552 mov eax,dword ptr [edx]
00A6F554 call eax
00A6F556 cmp esi,esp
00A6F558 call __RTC_CheckEsp (0A61596h)
//5.5 通过 类对象 正常调用 虚函数
tea26.Teacher26basevirtualfunc(23);
00A6F55D sub esp,8
00A6F560 movsd xmm0,mmword ptr [__real@4037000000000000 (0A79F88h)]
00A6F568 movsd mmword ptr [esp],xmm0
00A6F56D lea ecx,[tea26]
00A6F570 call Teacher26base::Teacher26basevirtualfunc (0A61442h)
//5.6 通过类对象 正常调用普通函数
tea26.Teacher26basefun();
00A6F575 lea ecx,[tea26]
00A6F578 call Teacher26base::Teacher26basefun (0A61749h)
//5.6 那么这个5.2, 5.3, 5.4有区别吗?debug看反汇编的代码
cout << "断点在这里" << endl;
}
结论:
但是这个两种方式调用 虚函数的具体步骤 会有不同。
//5.1 定义如下
int(Teacher26base::*vir)(double tempdou) = &Teacher26base::Teacher26basevirtualfunc;
//5.2 使用 类指针 调用 虚函数指针类型
Teacher26base *ptea26 = new Teacher26son();
result = (ptea26->*vir)(3.14);
00A6F46E mov esi,esp
00A6F470 sub esp,8
00A6F473 movsd xmm0,mmword ptr [__real@40091eb851eb851f (0A79F78h)]
00A6F47B movsd mmword ptr [esp],xmm0
00A6F480 mov ecx,dword ptr [ptea26]
00A6F483 call dword ptr [vir]
00A6F486 cmp esi,esp
00A6F488 call __RTC_CheckEsp (0A61596h)
00A6F48D mov dword ptr [result],eax
cout << "result1 = " << result << endl;
//5.3 使用 类对象 调用 虚函数指针类型
Teacher26base tea26;
result = (tea26.*vir)(78.8);
00A6F4D7 mov esi,esp
00A6F4D9 sub esp,8
00A6F4DC movsd xmm0,mmword ptr [__real@4053b33333333333 (0A79F98h)]
00A6F4E4 movsd mmword ptr [esp],xmm0
00A6F4E9 lea ecx,[tea26]
00A6F4EC call dword ptr [vir]
00A6F4EF cmp esi,esp
00A6F4F1 call __RTC_CheckEsp (0A61596h)
00A6F4F6 mov dword ptr [result],eax
看到: result = (ptea26->*vir)(3.14); 通过 指向类虚函数指针的方法 调用该函数,也会有vcall的出现
00A6F4EC call dword ptr [vir] //在这一步的时候,按F11 就会发现004D187F jmp Teacher26base::`vcall'{0}' (04D776Eh) ,然后再F11,就会发现会走到这里:004D1799 jmp Teacher26son::Teacher26basevirtualfunc (04D7C60h)
//5.4 通过 类指针 正常调用 虚函数
ptea26->Teacher26basevirtualfunc(3.14);
00A6F538 mov esi,esp
00A6F53A sub esp,8
00A6F53D movsd xmm0,mmword ptr [__real@40091eb851eb851f (0A79F78h)]
00A6F545 movsd mmword ptr [esp],xmm0
00A6F54A mov eax,dword ptr [ptea26]
00A6F54D mov edx,dword ptr [eax]
00A6F54F mov ecx,dword ptr [ptea26]
00A6F552 mov eax,dword ptr [edx]
00A6F554 call eax
00A6F556 cmp esi,esp
00A6F558 call __RTC_CheckEsp (0A61596h)
//5.5 通过 类对象 正常调用 虚函数
tea26.Teacher26basevirtualfunc(23);
00A6F55D sub esp,8
00A6F560 movsd xmm0,mmword ptr [__real@4037000000000000 (0A79F88h)]
00A6F568 movsd mmword ptr [esp],xmm0
00A6F56D lea ecx,[tea26]
00A6F570 call Teacher26base::Teacher26basevirtualfunc (0A61442h)
//5.6 通过类对象 正常调用普通函数
tea26.Teacher26basefun();
00A6F575 lea ecx,[tea26]
00A6F578 call Teacher26base::Teacher26basefun (0A61749h)
发现是和有继承的情况下是一样的。都是5步完成。
public:
int virtual Teacher27virtualfunc(double dou) {
cout << "Teacher27virtualfunc called dou = " << dou << endl;
int inttemp = (int)dou;
return inttemp;
}
};
void main() {
Teacher27 tea;
tea.Teacher27virtualfunc(3.14);
0028A37A sub esp,8
0028A37D movsd xmm0,mmword ptr [__real@40091eb851eb851f (0299F78h)]
0028A385 movsd mmword ptr [esp],xmm0
0028A38A lea ecx,[tea]
0028A38D call Teacher27::Teacher27virtualfunc (0281906h)
cout << "end" << endl;
}
也会看到 vcall的使用
printf("Teacher26base:: %p\n",&Teacher26base::Teacher26basevirtualfunc);
// 00E8F5C6 push offset Teacher26base::`vcall'{0
//}' (0E8187Fh)
//00E8F5CB push offset string "Teacher26base:: %p\n" (0E991E8h)
//00E8F5D0 call _printf(0E81091h)
//00E8F5D5 add esp, 8
在使用函数指针调用成员虚函数的时候会使用到vcall
int(Teacher26base::*vir)(double tempdou) = &Teacher26base::Teacher26basevirtualfunc;
int(Teacher26son::*vir1)(double tempdou) = &Teacher26son::Teacher26basevirtualfunc;
如上的两行代码,都是定义一个 函数指针,且是类的虚函数指针,到这里,我们应该有个意识,这两个里面都会使用到vcall代码段,vcall里面应该都存储的有 偏移,且由于Teacher26basevirtualfunc是第一个虚函数,因此偏移应该是0才对。
那么再调用的时候,我们一定要一个类指针,或者类引用
Teacher26base *ptea26 = new Teacher26son();
result = (ptea26->*vir)(3.14);
cout << "result1 = " << result << endl;
指针和引用由于是动态绑定,因此等号右边是啥,ptea26就会指向的是啥。
因此当前case下,this就是 Teacher26son,有了this,有了偏移,就能找到对应的真正的函数,调用一下就可以找到真正的函数地址了。