最近在看侯捷的深入浅出MFC时,了解到C++的相关知识,比如this指针到底是怎么出现的?虚函数是如何做到准确调用某个函数的,明明大家都长的一样?普通的成员函数是怎么被调用的?覆盖和隐藏又是什么?。。。好多问题,以前都没考虑过,花了点时间研究了一下,特此记录;
侯捷:深入浅出MFC:第二章C++ (额,其实有些部分的内容顺序乱了,第二章没什么问题)
CShape:基类,CRect:派生类;
1 #include <iostream> 2 3 using namespace std; 4 5 class CShape // 形状 6 { 7 private: 8 int m_color; 9 public: 10 void setcolor(int color) { m_color = color; } 11 }; 12 13 class CRect : public CShape 14 { 15 }; 16 17 int main() 18 { 19 CRect rect1,rect2; 20 rect1.setcolor(10); // rect1.m_color = 10; 21 rect2.setcolor(1); // rect2.m_color = 1; 22 return 0; 23 }
1、首先看this指针:派生类调用基类的函数(两个派生类对象调用同一个基类函数setcolor),结果分别成功设置改变了自己对象本身的数据?既然调用同一个函数,又是怎么实现分别改变两个对象的?(请注意,setcolor函数中只有一个参数color,他是无法去找到对象的地址的,也就是说根本做不到改变对象的成员变量的值)由此可以发现,必定有其他因素指定了对象的位置,否则无法实现;
原因在于隐藏的this指针,实际上的setcolor函数应该是这样的:void setcolor((CShape*)this,int color){ this->m_color = color; } ,这样就可以理解了,this指针指向调用函数的那个对象,通过this指针操作就可以改变对象的成员数据了,rect1.setcolor(10)中this指针指向了rect1,所以才能定向改变rect1的成员变量;
所以实际上,成员函数还是拿了对象指针做参数。
2、接下来又发现了一个问题:rect1.setcolor(10)这一句话就能调用函数setcolor,那么rect1是如何调用函数setcolor?最简单的想法,应该是对象中记录了每个函数的地址,当出现函数调用的时候,就转到这个函数中。对象中保存太浪费,类中保存就好了,然而实际上是这样,也不是这样。函数地址肯定是要保存的,但是并非我所想的对象调用函数。
rect1.setcolor(10);被编译器进行转换:CShape::setcolor((CRect*)&rect1, 10);这实际上就等于使用了一个写在CShape类中的setcolor函数,所谓的对象调用函数,实际上是函数使用对象的指针作为参数。
至此:对象调用函数的问题解决!
3、覆盖和隐藏到底是什么东西:覆盖/重载/隐藏,还是得分清楚的,需要搞明白到底哪个是哪个;
C++ 重载、重写(覆盖)、隐藏各自要求与异同总结: 简单来说,覆盖就是多态那一套,基类指针指向基类对象,就去基类找,基类指针指向派生类对象去派生类找,没找到再去基类找;;;隐藏,额,不知道怎么说,算了算了,搞明白再来;区分重载(overload),覆盖(Override)和隐藏(hide)
4、看虚函数吧:当使用基类指针指向派生类对象时,怎么才能知道它到底是使用基类的函数,还是派生类的函数呢?而不是全部流回至基类函数?
在不加虚函数时,基类指针指向派生类,指向了一个派生类的对象,当其调用函数时,自然而然的使用了基类函数(隐藏情况)。而虚函数解决这个问题。在有虚函数的情况下,每个类维护一个虚函数表,如果派生类没有改写基类的虚函数,那就存基类中的函数地址,如果改写了,那就存派生类中改写后的函数地址;
有了虚函数表之后问题就很好解决了,每个对象都存一个指向虚函数表的指针vptr,当调用虚函数时,通过vptr指针找到虚函数表,然后再通过虚函数表查找对应的函数,继而找到对应的函数地址,即可实现函数调用,只要虚函数表正确找到函数。(当基类指针指向派生类对象时,虽然指针类型改变,但是传进去的指针找到的虚函数表而是属于派生类的,所以能够正确调用函数)
5、本来是想用C模拟出来一个C++类的,就是实现C++的this指针/虚函数机制,emmmmmm,果然是我天真了。。草草做了一个盗版(盗版都算不上。。。),手动给成员赋值,手动模拟虚函数表,就是抄的网上别人写的C模拟C++多态。。。
--------------请特别注意104行隐藏系列函数存在的问题!!!!!!!(理论上(C++中)要调用基类函数)
1 #include <stdio.h> 2 3 // ------------------------------------------------------------- 4 5 // 函数指针 6 typedef void(*Virtual_Fun) (); 7 typedef void(*Derived_Fun) (); 8 typedef void(*Inherit_Fun) (); 9 typedef void(*Hide_Fun) (); 10 11 // ------------------------------------------------------------- 12 13 // 函数实现 14 void Virtual_Fun_Base() { 15 printf("Virtual_Fun_Base--我是基类的虚函数!\n"); 16 } 17 18 void Virtual_Fun_Derived() { 19 printf("Virtual_Fun_Derived--我是派生类的虚函数!\n"); 20 } 21 22 void Derived_Fun_Derived() { 23 printf("Derived_Fun_Derived--我是派生类独有的函数!\n"); 24 } 25 26 void Inherit_Fun_Base() { 27 printf("Inherit_Fun_Base--我是从基类继承下来的函数!\n"); 28 } 29 30 void Hide_Fun_Base() { 31 printf("Hide_Fun_Base--我是基类函数--隐藏系列!\n"); 32 } 33 34 void Hide_Fun_Derived() { 35 printf("Hide_Fun_Derived--我是派生类函数--隐藏系列!\n"); 36 } 37 38 // ------------------------------------------------------------- 39 40 // 基类+派生类 41 typedef struct MY_VIRTUAL{ // 拿My_Virtual充当虚函数表 42 Virtual_Fun virtual_fun; // 虚函数virtual_fun() 43 }My_Virtual; 44 45 typedef struct MY_BASE{ 46 int base_number; // 基类成员 47 Inherit_Fun inherit_fun; // 普通函数--直接继承系列 48 Hide_Fun hide_fun; // 普通函数--隐藏系列 49 My_Virtual virtual; // 虚函数表--仅一个虚函数; 50 }MyBase; 51 52 typedef struct MY_DERIVED{ 53 int base_number; // 基类成员 54 Inherit_Fun inherit_fun; // 普通函数--直接继承系列 55 Hide_Fun hide_fun; // 普通函数--隐藏系列 56 My_Virtual virtual; // 虚函数表--仅一个虚函数; 57 // 上面部分为继承MyBase,也可直接嵌套结构体MyBase 58 Derived_Fun derived_fun; // 派生类函数--独有 59 }MyDerived; 60 61 // ------------------------------------------------------------- 62 63 int main09() 64 { 65 MyBase myBase; 66 // 基类初始化 67 myBase.base_number = 10; // 成员变量 68 myBase.inherit_fun = Inherit_Fun_Base; // 直接继承函数 69 myBase.hide_fun = Hide_Fun_Base; // 隐藏系列 70 myBase.virtual.virtual_fun = Virtual_Fun_Base; // 虚函数系列 71 // 基类初始化 72 73 MyDerived myDerived; 74 // 派生类初始化 75 myDerived.base_number = 10; // 成员变量 -- 直接继承,保持不变 76 myDerived.inherit_fun = Inherit_Fun_Base; // 直接继承函数 -- 直接继承,保持不变 77 myDerived.hide_fun = Hide_Fun_Derived; // 隐藏系列 -- 发生变化,不再相同 78 myDerived.virtual.virtual_fun = Virtual_Fun_Derived; // 虚函数系列 -- 虚函数表改变,发生变化 79 myDerived.derived_fun = Derived_Fun_Derived; // 派生独有函数 80 // 派生类初始化 81 82 printf("-----------------\n基类显示:\nbase_number = %d\n",myBase.base_number); 83 myBase.inherit_fun(); 84 myBase.hide_fun(); 85 myBase.virtual.virtual_fun(); 86 87 printf("\n-----------------\n派生类显示:\nbase_number = %d\n",myDerived.base_number); 88 myDerived.inherit_fun(); 89 myDerived.hide_fun(); 90 myDerived.virtual.virtual_fun(); 91 myDerived.derived_fun(); 92 93 MyBase* pBase; 94 // 基类指针指向基类对象 95 pBase = &myBase; 96 printf("\n-----------------\n基类指针指向基类对象:\nbase_number = %d\n",pBase->base_number); 97 pBase->inherit_fun(); 98 pBase->hide_fun(); 99 pBase->virtual.virtual_fun(); 100 // 基类指针指向派生类对象 101 pBase = (MyBase*)&myDerived; 102 printf("\n-----------------\n基类指针指向派生类对象:\nbase_number = %d\n",pBase->base_number); 103 pBase->inherit_fun(); 104 pBase->hide_fun(); // 这里有问题,这是隐藏系列,理论上应该是要调用基类函数的,实际用了派生类函数,错误× --- 也就是说,C++中这里由编译器生成代码,调用基类的hide_fun函数; 105 pBase->virtual.virtual_fun(); 106 107 return 0; 108 }
问题解决;
2022-10-21