最近在看侯捷的深入浅出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 }

首先看this指针:派生类调用基类的函数(两个派生类对象调用同一个基类函数setcolor),结果分别成功设置改变了自己对象本身的数据?既然调用同一个函数,又是怎么实现分别改变两个对象的?(请注意,setcolor函数中只有一个参数color,他是无法去找到对象的地址的,也就是说根本做不到改变对象的成员变量的值)由此可以发现,必定有其他因素指定了对象的位置,否则无法实现;

原因在于隐藏的this指针,实际上的setcolor函数应该是这样的:void setcolor((CShape*)this,int color){ this->m_color = color; }     ,这样就可以理解了,this指针指向调用函数的那个对象,通过this指针操作就可以改变对象的成员数据了,rect1.setcolor(10)中this指针指向了rect1,所以才能定向改变rect1的成员变量;

所以实际上,成员函数还是拿了对象指针做参数


 接下来又发现了一个问题:rect1.setcolor(10)这一句话就能调用函数setcolor,那么rect1是如何调用函数setcolor?最简单的想法,应该是对象中记录了每个函数的地址,当出现函数调用的时候,就转到这个函数中。对象中保存太浪费,类中保存就好了,然而实际上是这样,也不是这样。函数地址肯定是要保存的,但是并非我所想的对象调用函数。

rect1.setcolor(10);会被编译器进行转换CShape::setcolor((CRect*)&rect1, 10);这实际上就等于使用了一个写在CShape类中的setcolor函数,所谓的对象调用函数,实际上是函数使用对象的指针作为参数。

至此:对象调用函数的问题解决!


 覆盖和隐藏到底是什么东西:覆盖/重载/隐藏,还是得分清楚的,需要搞明白到底哪个是哪个;

C++ 重载、重写(覆盖)、隐藏各自要求与异同总结: 简单来说,覆盖就是多态那一套,基类指针指向基类对象,就去基类找,基类指针指向派生类对象去派生类找,没找到再去基类找;;;隐藏,额,不知道怎么说,算了算了,搞明白再来;区分重载(overload),覆盖(Override)和隐藏(hide)


 虚函数吧:当使用基类指针指向派生类对象时,怎么才能知道它到底是使用基类的函数,还是派生类的函数呢?而不是全部流回至基类函数?

在不加虚函数时,基类指针指向派生类,指向了一个派生类的对象,当其调用函数时,自然而然的使用了基类函数(隐藏情况)。而虚函数解决这个问题。在有虚函数的情况下,每个类维护一个虚函数表,如果派生类没有改写基类的虚函数,那就存基类中的函数地址,如果改写了,那就存派生类中改写后的函数地址;

有了虚函数表之后问题就很好解决了,每个对象都存一个指向虚函数表的指针vptr,当调用虚函数时,通过vptr指针找到虚函数表,然后再通过虚函数表查找对应的函数,继而找到对应的函数地址,即可实现函数调用,只要虚函数表正确找到函数。(当基类指针指向派生类对象时,虽然指针类型改变,但是传进去的指针找到的虚函数表而是属于派生类的,所以能够正确调用函数)


 本来是想用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();     // 这里有问题,这是隐藏系列,理论上应该是要调用基类函数的,实际用了派生类函数,错误×
105     pBase->virtual.virtual_fun();
106 
107     return 0;
108 }

问题解决;

2022-10-21

原文地址:http://www.cnblogs.com/2015-16/p/16812829.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性