当前位置: 首页 > news >正文

长沙推广型网站建设阿里巴巴怎么优化关键词排名

长沙推广型网站建设,阿里巴巴怎么优化关键词排名,中国纪检监察报数字报,wordpress浏览数【C】—— 多态(下) 4 多态的原理 4.1 虚函数表指针4.2 多态的原理4.3 动态绑定和静态绑定 4.4 虚函数表 4 多态的原理 4.1 虚函数表指针 我们以一道题来引入多态的原理 下面编译为 32 位程序的运行结果是什么() A、编译报错  B…

【C++】—— 多态(下)

  • 4 多态的原理
    • 4.1 虚函数表指针
    • 4.2 多态的原理
    • 4.3 动态绑定和静态绑定
    • 4.4 虚函数表

4 多态的原理

4.1 虚函数表指针

  我们以一道题来引入多态的原理

下面编译为 32 位程序的运行结果是什么()
A、编译报错  B、运行报错  C、8  D、12

class Base
{
public :virtual void Func1(){cout << "Func1()" << endl;}
protected:int _b = 1;char _ch = 'x';
};
int main()
{Base b;cout << sizeof(b) << endl;return 0;
}

  按照我们之前的知识,这题答案应该选:C

  但我们不妨多留一个心眼:这题如果是考察内存对齐,为什么要加一个虚函数呢?是不是没有这么简单。

我们来看下运行结果:

在这里插入图片描述

  为什么呢? b b b类 中除了 _ b b b 和 _ c c c 成员,还多一个 _ v f p t r vfptr vfptr成员 放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针 v v v 代表 v i r t u a l virtual virtual f f f 代表 f u n c t i o n function function)。一个含有虚函数的类中至少都有一个虚函数表指针,因为一个类所有虚函数的地址要被放到这个类对象的虚函数表中,虚函数表也简称虚表

class Base
{
public :virtual void Func1(){cout << "Func1()" << endl;}virtual void Func2(){cout << "Func2()" << endl;}void Func3(){cout << "Func3()" << endl;}protected:int _b = 1;char _ch = 'x';
};

在这里插入图片描述

  虚函数表其实是一个数组该数组中存放着该类中所有虚函数的地址。虚函数表本质是一个函数指针数组,而 _ v f p t r vfptr vfptr 则是指向这个数组的指针
  通过图片我们也可以看到:虚函数表中放着虚函数 F u n c 1 ( ) Func1() Func1() F u n c 2 ( ) Func2() Func2() 的地址,因为 F u n c 3 ( ) Func3() Func3() 不是虚函数,并没有放进去。

在这里插入图片描述

  
  

4.2 多态的原理

  认识到了虚表指针的存在,我们就可以进一步来了解多态的原理啦
  
  我们结合具体的样例来学习

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
protected:string _name;
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
protected:int _id;
};
class Soldier : public Person {
public:virtual void BuyTicket() { cout << "买票-优先" << endl; }
protected:string _codename;
};
void Func(Person* ptr)
{ptr->BuyTicket();
} int main()
{Person ps;Student st;Soldier sr;Func(&ps);Func(&st);Func(&sr);return 0;
}

  上述代码中有三个类,每个类都有一个虚表指针

在这里插入图片描述
  
  在这里插入图片描述

  可以看到,三个类中虚函数表的 B u y T i c k e t ( ) BuyTicket() BuyTicket() 函数指针的地址都是不同的
  
  多态是怎么做到指向谁就去调用谁的呢?
  在编译阶段,编译器检查语法,看满不满足多态的条件。如果满足多态,在编译这段指令时,底层不再是编译时通过调用对象确定函数的地址,而是变成:在运行时,到指向对象的虚函数表中去找对应虚函数的地址,进行调用
  这样就实现了指针引用指向基类就调用基类的虚函数,指向派生类就调用派生类对应的虚函数
  对 F u n c ( ) Func() Func() 函数的 p t r ptr ptr 来说,不论传递的是父类对象还是子类对象,在它眼里都是父类对象,不同的是子类需要进行切片, p t r ptr ptr 看到是是子类切片后剩下的父类对象。但是没关系,如果满足多态条件, p t r ptr ptr 会进入这个父类的虚函数表中查找对应的虚函数的地址,找到谁就调用谁
  
  满足多态时的汇编代码:

在这里插入图片描述

  前面的 m o v mov mov 指针简单来说就是:找到 _ v f p t r vfptr vfptr 指针,再找到对应的虚函数表,再找到对应的函数指针,最后将指针给 e a x eax eax 寄存器,寄存器去 c a l l call call 函数地址

  下面,我将父类的 v i r t u a l virtual virtual 去掉,他们就不满足多态的条件了,再来看看他们的汇编代码

class Person {
public:void BuyTicket() { cout << "买票-全价" << endl; }
protected:string _name;
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
protected:int _id;
};
class Soldier : public Person {
public:virtual void BuyTicket() { cout << "买票-优先" << endl; }
protected:string _codename;
};

在这里插入图片描述

  两句代码搞定, p t r ptr ptr 是父类的指针,直接调用父类的 B u y T i c k e t ( ) BuyTicket() BuyTicket() 函数,与指向的对象无关。
  

4.3 动态绑定和静态绑定

  • 对不满足多态条件(指针或者引用+调用虚函数)的函数调用是在编译时绑定,也就是编译时确定调用函数的地址,叫做静态绑定
  • 满足多态条件的函数调用是在运行时绑定,也就是在运行时到指向对象的虚函数表中找到调用函数的地址,也就叫做动态绑定
// ptr是指针+BuyTicket是虚函数满⾜多态条件。
// 这⾥就是动态绑定,编译在运⾏时到ptr指向对象的虚函数表中确定调⽤函数地址
ptr->BuyTicket();
00EF2001 mov eax, dword ptr[ptr]
00EF2004 mov edx, dword ptr[eax]
00EF2006 mov esi, esp
00EF2008 mov ecx, dword ptr[ptr]
00EF200B mov eax, dword ptr[edx]
00EF200D call eax// BuyTicket不是虚函数,不满⾜多态条件。
// 这⾥就是静态绑定,编译器直接确定调⽤函数地址
ptr->BuyTicket();      
00EA2C91 mov ecx, dword ptr[ptr]
00EA2C94 call Student::Student(0EA153Ch)

从运行效率上来说,静态绑定更高一点,毕竟只有两句指令。
  
  

4.4 虚函数表

  • 基类对象的虚函数表中存放基类所有虚函数的地址
    • 这一点我们前面已经讲过了
        

      
  • 同一个类的对象虚函数表共用,不同类型对象虚表各自独立
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}virtual void Func2(){cout << "Func2()" << endl;}protected:int _b = 1;char _ch = 'x';
};
int main()
{Base b1;Base b2;Base b3;return 0;
}

在这里插入图片描述

  这也解释了为什么虚函数不放在对象中,而是放在一个数组之中,因为不同的对象好共享
  如果不把虚函数地址放在虚函数表中,而是放在对象之中,那么每个对象都要存一份,太过冗余。像这样放在一个公共的地方,无论有几个虚函数,都只需多 4 个字节来存储指针就行
  


  

  • 派生类由两部分构成,继承下来的基类自己的成员,一般情况下继承下来的基类中有虚函数表指针,自己就不会再生成虚函数表指针。但需要注意的是,这里继承下来的基类部分虚函数表指针和基类对象的虚函数表指针不是同一个就像基类对象的成员和派生类对象中的基类对象成员也独立的
      

  

  • 派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数就会被覆盖成派生类重写的虚函数地址
  • 派生类的虚函数表包含:基类的虚函数地址派生类重写的虚函数地址派生类自己的虚函数地址

什么意思呢?

class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
};
class Derive : public Base
{
public :// 重写基类的func1virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func1" << endl; }void func4() { cout << "Derive::func4" << endl; }
protected:int b = 2;
};

  现在 基类 B a s e Base Base 中有两个虚函数,派生类 D e r i v e Derive Derive 中重写了 f u n c 1 ( ) func1() func1(),并且有一个自己的虚函数 f u n c 3 ( ) func3() func3()

派生类的虚函数表生成逻辑是这样的:

  • 先将基类的虚函数表拷贝一份
  • 看有无完成重写/覆盖。派生类 D e r i v e Derive Derive 重写了 f u n c 1 func1 func1 函数,就会用重写的 f u n c 1 func1 func1 将基类的 f u n c 1 func1 func1 进行覆盖
  • f u n c 2 func2 func2 并没有完成重写,不管
  • 最后再加上自己的虚函数

  


  

  • 虚函数表本质是一个存虚函数指针的指针数组,一般情况下这个数组最后面放了一个 0x00000000 标记。(这个 C++ 并没有明确规定,各个编译器自行定义的,VS 系列编译器会在后面放个 0x00000000 标记,g++ 系列编译器不会放)
      

  

  • 虚函数存在哪?虚函数和普通函数一样的,编译好后是一段指令,都是存在 代码段(常量区) 的,只是虚函数的地址又存到了虚表中
  • 虚函数表存在哪?这个问题严格来说并没有标准答案,C++ 标准并没有规定,我们写下面的代码可以对比验证一下。VS下是存在代码段(常量区)
int main()
{int i = 0;static int j = 1;int* p1 = new int;const char* p2 = "xxxxxxxx";printf("栈:%p\n", &i);printf("静态区:%p\n", &j);printf("堆:%p\n", p1);printf("常量区:%p\n", p2);Base b;Derive d;Base* p3 = &b;Derive* p4 = &d;printf("Person虚表地址:%p\n", *(int*)p3);printf("Student虚表地址:%p\n", *(int*)p4);printf("虚函数地址:%p\n", &Base::func1);printf("普通函数地址:%p\n", &Base::func5);return 0;
}

运行结果:

在这里插入图片描述

  可以看到,虚表的地址和常量区的最接近。我们可以大致判定 VS 下虚函数表是放在代码段

  
  
  
  


  好啦,本期关于 多态 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 C++ 的学习路上一起进步!

http://www.hengruixuexiao.com/news/31199.html

相关文章:

  • 营销网站的设计思路推广app的单子都在哪里接的
  • 武汉品牌画册设计公司seo引擎优化服务
  • 免费自学平面设计的网站推广app网站
  • 重庆汉沙科技做网站怎么样推广任务发布平台app
  • 大型门户网站建设功能抖音seo排名软件
  • 杭州 建设网站制作重要新闻
  • 商业网站开发教程百度关键词搜索指数
  • 做商城网站多少钱重庆seo整站优化效果
  • 购买网域名的网站好windows优化大师电脑版
  • 中国响应式网站建设市场监督管理局投诉电话
  • 纪委网站建设情况汇报每日新闻快报
  • 杭州培训网站建设如何制作自己的网站
  • 如何做摄影网站云南优化公司
  • 网络平台搭建班级优化大师怎么下载
  • html怎么添加图片为背景优化课程设置
  • 西安网站建设中企建站百度seo关键词排名优化工具
  • 兰州网站优化服务广州关键词排名推广
  • 做二手车那个网站会员性价比高百度关键词推广公司
  • 郑州网站建设公司哪家好网络营销电子版教材
  • 网站建设客户来源怎么在百度发帖
  • 南昌企业制作网站农村电商平台
  • 手机网站的作用域名访问网站
  • h5制作软件 知乎百度起诉seo公司
  • 汽车门户网站有哪些网络推广服务费
  • 福州外网站建设网站怎么优化到首页
  • 18款禁用黄在线观看免费网站seo快速排名优化的软件
  • 外贸专业网站的公司代发关键词包收录
  • 网站建设销售话术流量推广平台
  • 用电脑做兼职的网站比较好网络营销的现状
  • 网站建设内部流程图百度关键词seo