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

电子商务网站建设的展望自己做壁纸的网站

电子商务网站建设的展望,自己做壁纸的网站,网站优化是做什么的,玩具网站的制作文章目录 前言多态的原理多态的条件要求虚函数表用程序打印虚表多继承的虚函数表静态多态和动态多态菱形虚拟继承 前言 上篇讲解了多态的原理#xff0c;这篇文章来详细讲解一下多态的原理。 这里有一道常考笔试题#xff1a;sizeof(Base)是多少#xff1f; 为什么不是8这篇文章来详细讲解一下多态的原理。 这里有一道常考笔试题sizeof(Base)是多少 为什么不是8 可以调试带大家看一下。 仔细看对象的头部多了一个指针。 这个指针叫做虚函数表指针。 上面不重要重要的是下面的东西多态的原理。 这个指针指向的表里到底有什么东西呢 多态的原理 看下面这里有两个对象一个是mike,一个是johnson,这两个对象都有表指针。 class Person { public:virtual void BuyTicket() { cout 买票-全价 endl; } }; class Student : public Person { public:virtual void BuyTicket() { cout 买票-半价 endl; } }; void Func(Person p) {p.BuyTicket(); } int main() {Person mike;Func(mike);Student johnson;Func(johnson);return 0; }我们之前讲过构成多态跟什么有关。 跟指针或者这个引用指向的对象有关。 为什么怎么实现的 这个指针指向父类调用父类的虚函数指向子类调用子类的虚函数。 怎么做到的呢 大家看父类对象的虚表存的是父类的虚函数子类对象的虚表存的是子类的虚函数。 编译器是怎么做的呢? 编译器也是判断构不构成多态如果不构成多态它就编译时确定调用的地址。 怎么确定呢 看person是什么类型。那它去person里面找到这个函数确定这个地方的地址。 如果是多态 它就会去指向的对象的虚表里去找。 编译器也很简单就是严格的去卡这个多态的条件满不满足。 带大家来调试看一下 构成多态的情况 p.BuyTicket();这个指令执行不知道调用的是谁。为甚么 这个person对象有两种情况 上面这段汇编代码的本质就是跟调用的指针对象或引用对象的类型已经无关 看指向的对象指向的父类调用父类的指向子类调用子类的。 多态就是转换成汇编的问题 不构成多态直接确定地址构成多态转成对应的汇编指令。 这段指令干嘛无法确定地址不知道调用谁的那引用指向的是父类 它找到父类头四个字节找到虚表的指针找到虚表找到虚函数靠的就是这个虚函数。 指向子类就会切割或者切片 单看p.BuyTicket();这个指令它不知道指的是子类还是父类。 汇编指令一样为什么调用结果不一样 因为传不同的对象不同的对象虚表是不一样的。 虚函数的另外一个名字为什么叫做覆盖 如果在子类里面重写虚函数以后子类里面对应的虚表位置会把它拷贝过来 覆盖成我的虚函数一样。 你可以这样认为重写是语法层的概念覆盖是原理层的概念。 多态的条件要求 现在可以反过来思考多态的条件 1.多态的条件为什么是重写 因为要覆盖虚表那个虚函数的位置。 2.为什么指针或者引用呢 因为指针和引用既可以指向父类对象也可以指向子类对象。 为什么不把虚函数直接存到对象的头上呢 因为他可能有多个虚函数都存到对象里面不合适。 其次同类型的虚表一样。 虚函数表本质是一个虚函数指针数组 如果有多个虚函数 再来感受什么叫覆盖。 第一个虚函数完成了重写可以这样认为子类对象先把父类对象的表拷贝过来。 然后重写那个覆盖成我自己的。没有重写就不覆盖。 虚函数表其实是在编译的时候就确定好 没有重写是一个样子 完成了重写是另外一个样子。 虚函数表里可能有多个地址那具体调用哪一个呢 看函数的声明顺序是第几个。 3.如果是父类的对象能不能实现多态 父类的指针或者引用在这里可以切片。父类的对象也可以切片。 对象为什么不能实现多态从原理上看 它转换成指令就是编译的时候如果是对象peron直接去调person的就可以了。 它也可以实现切片为什么不往多态去实现 如果是指针和引用与对象的区别是什么它们的切片有点不一样。 如果是指针和引用的切片 如果是指针是指向这个父类或者引用这个父类。 子类呢把子类对象父类那部分切出来。然后指向或引用切出来的那部分。 子类这部分的虚表还是子类的。 如果是对象呢 如果是个父类没什么问题如果是子类呢 子类给父类的切片成员会拷贝过去它会调用拷贝构造。 这里涉及一个问题虚表会不会拷过去 如果不会拷过去父类的对象的虚表里面永远是父类的虚函数。 它不敢拷贝虚表因为拷贝有一个很大的问题。 因为拷贝了就乱了。假设对虚表进行深拷贝父类对象的虚表到底是子类的虚函数 还是父类的虚函数完全分不清楚。 所以对象的切片只拷贝成员不拷贝虚表。 感受一下虚表没有变 只有虚函数的地址才会存进虚表。 再来一个问题 虚函数存在虚表这句话对不对 不对虚函数跟普通函数一样都是放在代码段的。 但是虚函数的地址会别放进虚函数表。 这里涉及到linux操作系统的知识。大家可以去了解一下。 大家可以看一下同类的对象是不是构成同一张虚表 父类和子类的虚表不一样因为子类要重写要有独立的虚表。 监视窗口看到的是被修饰过的监视窗口看到的不一定是最真实的。 class Base { public:virtual void Func1(){cout Base::Func1() endl;}virtual void Func2(){cout Base::Func2() endl;}void Func3(){cout Base::Func3() endl;}private:int _b 1; };class Derive : public Base { public:virtual void Func1(){cout Derive::Func1() endl;} private:int _d 2; };int main() {Base b1;Base b2;Base b3;Derive d;b1.Func1();b1.Func3();return 0; }虚函数表 记住不是虚函数进入虚表而是虚函数的地址进入虚表。 虚表的全称是虚函数表。 虚表的本质就是一个指针数组。 基类的虚表 派生类的虚表 派生类的虚表也有两个虚函数的地址 不同的是你可以这样认为子类的虚表是把父类的虚表给拷贝过来 拷贝过来以后做什么是呢 重写虚函数重写的那个位置会完成覆盖覆盖成我重写的虚函数。 多态的本质就是依靠虚表来实现的。 比如有个父类的指针或者引用可以指向父类对象也可以指向 子类对象指向子类对象是把子类对象父类那部分给切出来。 你可以这样认为对于这个指针而言看到的都是父类对象。 只是一个本身就是父类对象一个是子类对象里切出来的父类对象。 ptr-Func1();底层的汇编都是一样的代码的本质都是转换成汇编。 它不管你是什么它都去虚表里面找那个虚函数的地址。 所以指向父类调用父类的指向子类调用子类的。 假设在派生类里面加了一个Func4(); 现在Func1();完成了重写Func4();没有完成重写。 我们现在看一下Func4();在不在虚表里呢 没有看到Func4();哪去了呢 Func4();是虚函数怎么没在虚表呢 我们去看一下内存窗口。 Func1()和Func2()都在那这个是Func4();吗 怎样验证一下呢 可以打印Func4();的地址对比一下吗可以是可以但是后面还有其他更复杂的情况。 现在是一个单继承那多继承呢还有菱形继承。 接下来我们就会讲到一个新玩法用程序打印虚表。 用程序打印虚表 怎么打印 假设我已经有虚表的地址了这个函数指针的数组的地址 现在怎么打印。 它是一个函数指针处理起来比较麻烦。 这句话是什么意思。这里typedef一个函数指针。 函数指针本身很特殊应该是这样的。 但是函数指针typedef不能前面是类型后面是重命名的名字。 函数指针定义变量或者typedef都应改放到中间。 打印数组很简单 但是不确定这个数组有多大因为不同 对象的虚表是不一样的g下就只能写死比如知道有三个 就只能打印三个。但是vs系列给了一个遍历。 vs系列在存储虚表的时候在数组的最后放了一个nullptt, g没有。 如果自己vs的编译器没有看到nullptr,清理一下解决方案然后再重新生成解决方案 就可以了。 再接着往下看我现在要把虚表的地址给取出来。 怎么样把虚表的地址取出来呢 这个指针在对象的头4个字节或者头8个字节。 如何去取对象的头4个字节 可以回顾一下学大小端的时候想取低位的值。 假设给你一个整型我想取这个整型的第一个字节是怎么取的。 1.定义联合体(这里再定义一个联合体加不进来了 2.将int的地址jint强转成char再解引用。 这里我们用第二种玩法。 但是这里是int,函数传参传不过去。int在强转为对应的类型。 传参的时候不会直接转吗 不会直接转是隐式类型转换C只有相近类型才支持隐式类型转换。 比如int, double, char. 指针都是一个地址但是指针的类型决定了指针接引用的时候看多大。 注意不能用sizeof()去算数组只要传参都会出问题。 还有这个不是我们平时用的那种数组只有我们定义的静态的数组才能算数组的大小0。 其他地方都不行。 还有一种更直接的方式 这还是简化过的如果直接把函数指针套进来就变成天书了。 带大家理解一下。 为甚么不直接这样呢 先说结论这样是不行的。 首先你要传过去的地址在哪在对象的头4个字节或8个字节。 必须有解引用才能把对象的头4个字节或8个字节取出来。 b是指向对象的指针你要传1号位置的指针还是2号位置的指针。2号。 而你现在传的是1号2号位置的指针在对象的头4个字节上怎么取出来 强转成VF_PTR**, 指针解引用在32位看4个字节在64为看8个字节。 这两种写法的差异是什么 第一种写法具有一定的局限性局限性在于它只能在32位跑 64位下就跑不通了。 第二种写法都适应VF_PTR**解引用是看VF_PTR*,VF_PTR*在 32位4个字节64位8个字节。 现在已经可以打印出来虚表里虚函数的地址了但是怎么确认就是这个呢 再教大家一招。 有个疑问父类没有Func4();怎么能进入虚表呢 这个虚表已经不仅仅属于父类了它被继承了。只是生长点是子类对象父类的一部分。 Func4();是子类的其次这个虚表严格来说是属于子类的。 父类的虚表和子类的虚表不是同一个子类继承了以后子类把虚表拷贝了一份 然后子类对其重写自己的虚函数也会进入这个虚表。 虚表是在什么阶段生成的 编译的时候就生成了因为编译的时候就有这些函数的地址就可以组成父类的虚表和子类的虚表。 对象中虚表什么时候初始化 它是在构造函数的初始化列表的时候初始化。自己可以单独通过调试看一下。 虚表存在哪里 首先虚表不在对象里面对象里面的是虚表指针。 它有没有可能在栈上 绝对不可能因为多个对象存指向同一张虚表。栈里面只有栈帧函数调用结束了然后销毁了不可能。 有没有可能在堆上呢 有可能但是不合理。堆一般是动态申请的。不可能。 接下来我们可以验证一下是在静态区还是常量区 打印几个地址来对比一下就可以了。 对比地址的远近虚表的地址跟常量区最接近。 其实大家可以仔细想想虚表被编译好了会不会改 虚表在编译的过程中可能被改尤其是子类的虚表。 运行的时候不会被改所以放在常量区更合适。 其实看下面这个也能看出来 编译好的函数是一串指令这串指令的地址就是函数的地址函数的地址是放到代码段 常量区的。 多继承的虚函数表 对于Base1和Base2没什么关键就是看多继承的Derive 先看监视窗口。 Derive应该有两张虚表因为它同时继承了Base1和Base2 两张虚表里重写了func1();func2();没动。 现在有一个问题子类的func3();放在哪里呢 我们这里借助虚表打印看一下。 现在有一个问题第一张虚表在第一个位置打印第二张虚表怎么大? 两张虚表是放在两个对象里的无法确定它是不是连续。 因为Base1除了这张虚表还有其他成员变量。 1.跳过Base1,加上sizeof(Base1); 2.用切片借助指针的偏移。(Base2的指针会自动偏移) 但是这样不对d是Derive*, Derive*1跳过Derive,强转成char*,char*1跳过一个字节。 它放到第一张表去了 要理解指针的偏移。 我们先看一下下面这道题。 这道题理解了切片就能做。 p1虽然跟p3的地址一样但是意义不一样。 谁先继承谁就先声明谁先声明谁就在前面。 func1会完成重写。它会重写两份覆盖两个位置覆盖Base1的虚表也会覆盖Base2的虚表。 但是这里面有一个非常奇怪的现象。这才是真正的大难题十个学C的9个都会翻车。 大家有没有发现重写的func1的地址不一样 首先问大家一个问题这个函数是不是func1是不是重写的fuc1的地址 是因为我们后面的字符串是去调用这个函数打印的。 但是这个地址为什么不一样呢 这个问题很深很不容易理解我们只有看汇编才能看懂。 这两个都会转成汇编代码这两段汇编代码一不一样。这两个地方调用的是不是同一个函数 这里是不是call的同一个地址 很多人都认为是一样的因为这里符合多态的条件。 **你可以认为第二个地址被封装过。**因为不封装完成不了调用因为有些条件我们理解有一些偏差。 这里有深层次的原因。 重新运行地址不变不利于比较因为这涉及进程加载的一些原因。它要进行重定位。 ptr1是正常调用。 大家看ptr2连续jmp了好几次为甚么 jmp就相当于封装。 右边有一段指令非常特殊 ecx存的是this指针然后配着这个图大家就能看懂 去调用子类的这个函数的时候。 ptr1没有处理的原因是因为它恰好指向子类对想的开始。 调用子类的函数this指针应该指向子类对象。 ptr2去调用子类的这个函数的时候this指针不对。 这个指令的作用就是修正this指针的位置。 这里不一定减8它减的是一个Base1的大小。 这里还涉及一个问题如果先继承Base2,base1就要修正。 静态多态和动态多态 有些地方会分静态多态和动态多态。 那什么是静态多态呢 函数重载。 一般语言层面说静态都是指的编译时。 函数重载就是通过编译时实现的。 什么是动态多态呢 对应运行时。 这两个本质都是写死了。 菱形虚拟继承 A有一个虚函数func,B有一个虚函数func,C有一个虚函数func, D没有虚函数这是不行的。 D如果不重写会报错。它说不明确为什么 现在B重写了C重写了现在有一个问题A的虚表里放谁的虚函数 这个问题感兴趣的可以自己去了解一下我这里就先不回答了。
http://www.zqtcl.cn/news/135003/

相关文章:

  • 装修公司网站源代码网站建设岗位周计划
  • 有没有专门学做婴儿衣服的网站org.wordpress utils
  • 网站关键词 提醒哪个网站做视频有钱挣
  • 建设企业网站注意事项菜篮网网站开发技术
  • 怎么把图片做超链接到网站wordpress 配置模板
  • 湘潭网站seo惠州市建设厅网站
  • 广州外贸网站效果百度竞价开户需要多少钱
  • 广州做手机网站信息附近卖建筑模板市场
  • 怎么看网站开发语言信息dw网站建设视频下载
  • 做网站虚拟主机多少钱wordpress中category参数
  • 山东省建设执业师网站建设网站图片
  • 网站建设的安全可行性网站建设教学设计
  • 网站架设建设动易门户网站价格
  • 公司快速建站商城网站建设讯息
  • it公司做网站用什么软件鲁中网站
  • 制作属于自己的app教程北京和隆优化招聘
  • wordpress会员卡系统青岛百度优化
  • 网站的管理系统网站权限配置
  • 龙岗高端网站建设在进行网站设计时
  • 网站制作定制浙江交工宏途交通建设有限公司网站
  • 域名网站计划怎么写高端网站建设 引擎技
  • 做自己的网站流量怎么桂林人论坛桂林板路
  • 上海制作网站多少钱wordpress主题站主题
  • 企业网站开发软件WordPress访问者ip
  • 视频网站dedecms在源码之家下载的网站模板可以作为自己的网站吗
  • 西宁好的网站建设公司怎样将视频代码上传至网站
  • 内网网站开发专业建站公司报价
  • 做地方网站需要什么部门批准天津专业做标书
  • 域名注册信息查询网站推广seo是什么
  • 做外贸网站哪家公司好常见的管理系统