学科网站建设方案中小企业网站制作哪家好
- 作者: 五速梦信息网
- 时间: 2026年03月21日 07:03
当前位置: 首页 > news >正文
学科网站建设方案,中小企业网站制作哪家好,网页qq登录手机版,百度关键词排名突然没了目录 一、抽象类 1#xff09;抽象类定义 2#xff09;抽象类的继承 3#xff09;抽象类实现多态 4#xff09;抽象类的好处 二、多态的实现原理 1#xff09;虚函数的存储方式 2#xff09;子类中虚函数的存储方式 ① 子类将基类中的虚表原封不动的拷贝到自己的…目录 一、抽象类 1抽象类定义 2抽象类的继承 3抽象类实现多态 4抽象类的好处 二、多态的实现原理 1虚函数的存储方式 2子类中虚函数的存储方式 ① 子类将基类中的虚表原封不动的拷贝到自己的虚表中 ② 如果子类中重写了基类中的虚函数则将子类虚表中基类的虚函数替换为子类自己重写了的虚函数地址 ③ 如果子类有新增的虚函数则接着将子类新增的虚函数按照其在子类中的声明顺序依次增加到子类虚表的尾部 3代码检验虚函数在虚表中的先后位置 4对于多态的原理的剖析 ① 当不满足多态的条件时 ② 满足多态的条件通过指针或者引用调用 三、其他继承方式下的虚表模型 1单继承方式 2多继承方式 源码注意调用TestPrintB1与TestPrintB2方法获取类中虚表的顺序 一、抽象类 在多态上那篇文章中写了这样一个类Shape然后通过子类圆、矩形、三角形分别继承该Shape类基类通过重写基类中的Print与GetPerimeter这俩个虚函数然后达到了多态的效果。 可是这里仍然有一些问题就是这个基类的函数Print与GetPerimeter这俩个函数的执行对于基类对象来说用Shape定义一个对象s这个s是个啥呢它是一个图形它是个啥图形不免得引发争议这个Shape就不应该定义对象它的功能函数也不应该被实现 class Shape { public:virtual void Print(){cout 未知图形 endl;}virtual double GetPerimeter(){cout 未知图形无法获得周长 endl;} }; 这个Shape就是一个抽象类形状这个概念在问题领域中不是直接存在的它只是一个抽象概念。 1抽象类定义 在虚函数的后面写上0表示该函数为纯虚函数包含纯虚函数的类称为抽象类也叫接口类抽象类不能实例化出对象。 class Shape { public:virtual void Print() 0;virtual double GetPerimeter() 0; }; 2抽象类的继承 抽象类可以被继承但子类中如果没有对基类所有的纯虚函数进行重写则子类也无法进行实例化对象。即子类也是一个抽象类 对基类中的所有纯虚函数进行重写之后才能完成子类实例化对象 class Shape { public:virtual void Print() 0; // 限定为纯虚函数表示在子类中必须要对其进行重写virtual int GetPerimeter() 0; };class Triangle : public Shape { public:Triangle(int a, int b, int c): _a(a), _b(b), _c©{}virtual void Print()override // 养成给基类中需要重写的虚函数加上override的习惯{cout △ endl;}virtual int GetPerimeter()override{return _a _b _c;} private:int _a;int _b;int _c; }; int main() {Triangle t(3, 4, 5); // 正常编译 } 3抽象类实现多态 上面说了抽象类无法实例化对象但是抽象类也是一种类型所以它可以创建指针或者引用。 那么就可以用到多态来传递参数 void Test(Shape s) // 传入Shape类型的引用 {s.Print();cout s.GetPerimeter() endl; }int main() {Triangle t(3, 4, 5);Test(t); // 实现多态Shape s; // 实例化对象报错 Shape* ptrs t; // 定义一个指针 OKShape rs t; // 定义一个引用 OK // 基类的指针或引用可以指向子类return 0; } 4抽象类的好处 ① 代码更加符合逻辑——有些类就是无法创建对象所以将它声明为抽象类 ② 不用花费时间来考虑纯虚函数中的代码应该怎么写 ③ 抽象类实际规范了后续子类要实现的虚函数的原型 》将接口规范化 二、多态的实现原理 这里使用的是VS2019 x86 32位的编译环境下进行研究一个指针为4个字节 1虚函数的存储方式 class D { public:D(int d, int b): _d(d){cout B::B() endl;}virtual void func1(){cout D::func1() endl;}virtual void func2(){cout D::func2() endl;}virtual void func3(){cout D::func1() endl;}int _d; };int main() {cout sizeof(D) endl; // 结果为 8D d(1,2);return 0; } 创建一个D类类中存在三个虚函数这三个虚函数在类中的模型是如何呢 ① 调试并查看用D创建出来的对象d发现d中前四个字节存放的是一个类似于地址的东西接着存放的是_d的值也占用四个字节所以sizeof(D)为8个字节 ② 那么这四个字节的地址是什么呢 展开这个地址发现里面又存放了三个地址而这三个地址都指向了我定义的三个虚函数 ③ 调用内存窗口看一下d,然后再打开另外一个窗口查看一下d中前四个字节的地址存放的是啥 ④ 对比这内存窗口与监视窗口的这三个地址发现就是②中所说的这块地址指向了三个虚函数的地址。 ⑤ 模型推导 ⑥ 同一个类中定义俩个对象它们的虚表地址是相同的。共用同一张虚表 结论 如果一个类中存在虚函数类的大小多个4个字节 ① 则编译时编译器会为类中的虚函数创建一个虚表编译器一定会生成类的构造方法 ② 虚表中的内容存放的是各个虚函数的入口地址 ③ 并且虚表中虚函数的先后次序和类中定义的虚函数的先后次序一致。 ④ 同一个类中的多个对象共用同一张虚表 2子类中虚函数的存储方式 上面了解了关于类中虚函数是如何存储的那么下面通过继承来学习子类中的虚函数的存储方式 ① 子类将基类中的虚表原封不动的拷贝到自己的虚表中 如图将B中的俩个虚函数继承下来后置入自己的虚表中。函数访问时通过虚表指针来进行访问。 class B{ public:int _b;virtual void func1(){cout B::func1() endl;}virtual void func2(){cout B::func2() endl;} };class D : public B{ public:int _d; }; ② 如果子类中重写了基类中的虚函数则将子类虚表中基类的虚函数替换为子类自己重写了的虚函数地址 D子类重写了基类的虚函数func1() ③ 如果子类有新增的虚函数则接着将子类新增的虚函数按照其在子类中的声明顺序依次增加到子类虚表的尾部 通过内存监视窗口了解虚表内的函数存储D::func3()为在子类D中新增的虚函数D::func1为子类重写了基类的虚函数B::func2为基类继承下来的虚函数 class B { public:int _b;virtual void func1(){cout B::func1() endl;}virtual void func2(){cout B::func2() endl;} };class D : public B { public:int _d;virtual void func1(){cout D::func1() endl;}virtual void func3(){cout D::func3() endl;} }; 3代码检验虚函数在虚表中的先后位置 通过上面的学习可以知道通过一个对象的内存模型可以知道其前四个字节为虚表指针那么通过这个虚表指针就可以访问到存储虚函数入口地址的函数指针数组接着通过这个函数指针数组来访问每个函数的入口地址 ① 获取虚表指针内容 使用一个四个字节大小的int类型的指针来进行强转d对象则这个接收结果的p现在就指向了0xcd9b64这个地址如果这时候对这个p解引用则就可以得到虚表指针的内容 ② 对p进行解引用 解引用后的值指向了虚表指针数组可是这里的data是一个int类型的整形数据——下面看到0xcd9b64是我通过设计将整数16进制转化看到的 ③ 对data整形数据进行类型转换 首先看一下函数指针如何取别名对void*()取别名为 typede void(PVft)(); PVft fp (PVft*) data ④ 从上到下依次打印虚表中的内容 while (*fp){(*fp)();fp;} 4对于多态的原理的剖析 ① 当不满足多态的条件时 例如下面使用基类的对象来直接进行调用函数 编译器在编译阶段就可以确定调用哪个类的函数因为此时编译器编译时看到的是对象的静态类型是哪个类的对象就去哪个类里面调用它的成员函数。 ② 满足多态的条件通过指针或者引用调用 直白的当了解了子类虚函数的存储结构后多态其实也就是一种特殊的函数调用机制它通过传入的对象指针判断访问的是哪个对象的类型然后通过该类型的虚表确定该如何来调用入口函数 三、其他继承方式下的虚表模型 1单继承方式 单继承方式下的虚表模型就是上面我们一直所探讨的用例 父类与子类中各有一张虚表当然是在有虚函数的前提下 2多继承方式 如以下继承体系B1func1、func2、B2func3、func4为俩个基类D继承自这俩个基类D中重写了B1中的func1、B2中的func4、以及新增虚函数func5 在多继承中子类中存在俩个虚表子类新增的虚函数地址按次序放到第一张虚表后面 源码注意调用TestPrintB1与TestPrintB2方法获取类中虚表的顺序 class B1 { public:int _b1;virtual void func1(){cout B1::func1() endl;}virtual void func2(){cout B1::func2() endl;} };class B2 { public:int _b2;virtual void func3(){cout B2::func3() endl;}virtual void func4(){cout B2::func4() endl;} }; class D : public B1, public B2 { public:int _d;virtual void func1()// 重写父类B1的虚函数{cout D::func1() endl;}virtual void func4()// 重写父类B2的虚函数{cout D::func4() endl;}virtual void func5()// 子类新增虚函数{cout D::func5() endl;} };typedef void(VFPT)(); void TestPrintB1(B1 b, const string info) {cout info endl;VFPT fp (VFPT)(int*)b;while (*fp){(fp)();fp;}cout endl; } void TestPrintB2(B2 b, const string info) {cout info endl;VFPT fp (VFPT)(int*)b;while (*fp){(*fp)();fp;}cout endl; }int main() {D d;d._b1 1;d._b2 2;d._d 3;TestPrintB1(d, D of B1);TestPrintB2(d, D of B1); }
- 上一篇: 学建设网站首页网络广告公司怎么做
- 下一篇: 学了dw 就可以做网站了吗指数平台
相关文章
-
学建设网站首页网络广告公司怎么做
学建设网站首页网络广告公司怎么做
- 技术栈
- 2026年03月21日
-
学剪辑有必要报班吗正安县网站seo优化排名
学剪辑有必要报班吗正安县网站seo优化排名
- 技术栈
- 2026年03月21日
-
学会网站建设方案微网站开发第三方平台
学会网站建设方案微网站开发第三方平台
- 技术栈
- 2026年03月21日
-
学了dw 就可以做网站了吗指数平台
学了dw 就可以做网站了吗指数平台
- 技术栈
- 2026年03月21日
-
学什么专业可以做网站东莞网站建设模板设计
学什么专业可以做网站东莞网站建设模板设计
- 技术栈
- 2026年03月21日
-
学生成绩管理系统网站建设网站建设一般是用哪个软件
学生成绩管理系统网站建设网站建设一般是用哪个软件
- 技术栈
- 2026年03月21日
