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

郑州免费网站制作文昌网站 做炸饺子

郑州免费网站制作,文昌网站 做炸饺子,外贸网站源代码下载,wordpress 韩版 企业1.多态的概念 多态的概念#xff1a;通俗来说#xff0c;就是多种形态#xff0c;具体点就是当不同的对象#xff0c;去完成某个行为#xff0c;会产生不同的状态 举个例子#xff1a;同样是吃饭#xff0c;狗吃狗粮#xff0c;猫吃猫粮#xff0c;不同的对象#…1.多态的概念 多态的概念通俗来说就是多种形态具体点就是当不同的对象去完成某个行为会产生不同的状态 举个例子同样是吃饭狗吃狗粮猫吃猫粮不同的对象对于同一个行为会有不同的状态 2.多态的定义和实现 2.1虚函数 虚函数即被virtual修饰的类成员函数注意是类成员函数其他函数不能被virtual修饰 class A { public:virtual void func() {cout hello C endl;} }; 2.2虚函数的重写 虚函数的重写(覆盖)派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同)称子类的虚函数重写了基类的虚函数。 class A { public:virtual void func() {cout hello C endl;} };class B : public A{ public://虚函数重写virtual void func() {cout hello ZXWS endl;} }; 2.3多态的构成条件 1.必须通过基类的指针或者引用调用虚函数 2.被调用的函数必须是虚函数且派生类必须对基类的虚函数进行重写 class A { public:virtual void func() {cout hello C endl;} };class B : public A{ public:virtual void func() {cout hello ZXWS endl;} };void test(A a)//必须是基类的引用/指针 {a.func();//该函数是虚函数且被重写 }int main() {A a;B b;test(a);test(b);return 0; } 总结多态的实现本质就是由虚函数的重写实现的再次强调一下 虚函数重写规则的总结和补充 1.virtual关键字 2.三同返回值类型/函数参数/函数名相同 注意 1.返回值不同也能构成虚函数重写但是返回值的类型必须是引用/指针称为协变返回值得全是指针/全是引用且得是父子关系---父对父子对子【基类中的虚函数返回值为父类派生类中虚函数返回值为子类】这里的父子关系可以是任意一对父子关系 【注释】基类、派生类和父类、子类是一个意思上面的表述是为了表示两对父子关系 class A {}; class B : public A {};class Person { public://父对父子对子对virtual A* func() {cout A* func() endl;return nullptr;}//父对子子对父错//virtual B* func() {// cout A* func() endl;// return nullptr;//} };class Student : public Person { public:virtual B* func() {cout B* func() endl;return nullptr;}//virtual A* func() {// cout B* func() endl;// return nullptr;//} }; 2.析构函数的重写(正常来看基类和派生类的名字不同) 如果基类的析构函数为虚函数此时派生类析构函数只要定义无论是否加virtual关键字都与基类的析构函数构成重写虽然基类与派生类析构函数名字不同看起来违背了重写的规则其实不然这里可以理解为编译器对析构函数的名称做了特殊处理编译后析构函数的名称统一处理成destructor class Person { public:virtual ~Person(){cout ~Person() endl;} };class Student : public Person { public:virtual ~Student() {cout ~Student() endl;} };int main() {Person* p new Person;Person* s new Student;//如果析构函数不能实现虚函数重写这里的空间释放就会出现问题//所以编辑器将析构函数的名字做了特殊处理delete p;delete s;return 0; }3.只要基类中的虚函数加了关键字virtual派生类中可以不加推荐都加上 2.4 C11 override 和 final  从上面的虚函数重写的规则我们不难看出C对虚函数的重写比较严格这就导致我们在写的时候容易出错且一些种错误在编译期间不会报错只有在程序运行时没有得到预期结果才来debug会得不偿失因此C11提供了override和final两个关键字可以帮助用户检测是否重写 1.final修饰虚函数表示该虚函数不能再被重写 2.override: 检查派生类虚函数是否重写了基类某个虚函数。 2.5重载、覆盖(重写)、隐藏(重定义)的对比  3.抽象类 3.1概念 在虚函数的后面写上 0 则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类也叫接口 类抽象类不能实例化出对象。派生类继承后也不能实例化出对象只有重写纯虚函数派生 类才能实例化出对象。纯虚函数规范了派生类必须重写另外纯虚函数更体现出了接口继承 class A { public:virtual void func() 0; };class B : public A { public:virtual void func() {cout hello C endl;}; };class C : public A { public:virtual void func() {cout hello ZXWS endl;}; };int main() {//A a;//抽象类不能实例化对象B b;C c;A* pb b;A* pc c;pb-func();pc-func();return 0; } 3.2接口继承和实现继承 普通函数的继承是一种实现继承派生类继承了基类函数可以使用函数继承的是函数的实现。虚函数的继承是一种接口继承派生类继承的是积累虚函数的接口目的是为了重写达成多态继承的是接口。所以如果不是先多态就不要把函数定义为虚函数 大家来猜猜下面代码的打印结果 class A { public:virtual void test() {func();}virtual void func(int x 0) {cout A- x endl;} }; class B : public A { public:virtual void func(int x 1) {cout B- x endl;} };int main() {B b;A* pb b;pb-test();return 0; } 代码分析 首先pb调用A类中的test函数因为B类中没有重写该虚函数所以还是调用A类中的test。然后test函数里调用func函数func函数实现了重写所以调用B类中的但是注意这里的重写只是重写函数的内部实现函数接口(函数的声明)还是A类的函数接口所以x的缺省值为0而不是1 大家要清楚普通继承和虚函数继承的差别 4.多态的原理 在讲多态的原理之前我们先来看看下面代码的运行结果 class A { public:virtual void func(int x 0) {cout A- x endl;} private:int _a;char ch; }; int main() {cout sizeof(A) endl;return 0; } 哎为什么这是12呢正常我们用内存对齐来算的结果应该是8才对呀这个类和之前的类的唯一区别在于它多了一个虚函数我们就猜测类里面来应该存了func这个函数的相关信息。 下面我们来调试看看 显然a对象中确实多了一个指针所以对象a的大小为12(用的是32位的编译环境地址是4字节)那么这个指针有什么用呢 这个指针指向的空间里存放了虚函数的地址我们称这块空间为虚函数表简称虚表所以这个指针叫做虚函数表指针 (一个含有虚函数的类中都至少有一个虚函数表指针因为虚函数的地址要被放到虚函数表中) 那么多态是如何通过虚函数实现的呢 通过上面的图我们就应该能理解多态的实现原理了本质就是在虚表里查找函数里面放的啥函数就调用啥函数而虚表里存放的虚函数根据子类对父类的虚函数有没有重写来决定存放哪个函数的地址 同时这里也能解释为什么多态的实现需要调用父类的引用/指针因为父类对子类对象的引用并没有生成临时变量而是直接引用的子类继承来的父类部分那么当然也能通过虚函数表指针调用对应的函数实现多态指针同理 (注意有些平台可能会将虚函数表指针放到对象的最后面这个跟平台有关) 还有一点同一个类的对象公用同一张虚函数表 4.3动态绑定和静态绑定 1. 静态绑定又称为前期绑定(早绑定)在程序编译期间确定了程序的行为也称为静态多态比如函数重载 2. 动态绑定又称后期绑定(晚绑定)是在程序运行期间根据具体拿到的类型确定程序的具体行为调用具体的函数也称为动态多态。 5.单继承和多继承关系的虚函数表 虚函数表存在哪个区域栈堆还是什么 class A { public:virtual void func() { cout A endl; } }; void test() {A aa;int a0;int* p new int;const char* str hello ZXWS;static int c 0;printf(代码区%p\n,str);printf(静态区%p\n, c);printf(堆区%p\n, p);printf(栈区%p\n, a);printf(虚表%p\n, *((int*)aa));//VS中虚函数表指针的地址一般在对象的起始位置printf(虚函数%p\n, A::func);return 0; } 很显然虚表的地址离代码区比较近其实虚表就在代码区这也符合我们的认识因为虚表的内容是不能被修改的当然虚函数其实和正常函数没什么区别都在代码区 5.1单继承 class A { public:virtual void func1() { cout A::func1 endl; }virtual void func2() { cout A::func2 endl; } private:int _a; };class B { public:virtual void func1() { cout B::func1 endl; }virtual void func3() { cout B::func3 endl; }virtual void func4() { cout B::func4 endl; } private:int _b; };int main() {A a;B b;return 0; } 当我们看到调试窗口就会发现B类的虚函数表中只有两个虚函数而B类中应该有4个虚函数才对那么会为什么呢 这里解释一下这是编辑器自己做了处理我们要想看清还是得看地址空间如下 VS编辑器会在虚函数表的结尾放一个空指针从内存窗口来看b的虚函数表确实是存了4个地址但是我们不能确定下面我们来验证一下 typedef void(*VF)();//重定义函数指针 void PrintVF(VF v[]) {for (int i 0; v[i]; i) {printf([%d]:%p- , i, v[i]);v[i]();//用函数指针调用函数printf(\n);} } int main() {A a;B b;PrintVF((VF*)(*(int*)b));//这里传的是虚函数表指针//VS中虚函数表指针的地址一般在对象的起始位置这里注意32位地址4字节64位地址8字节//这里是32位下用int*,64位下要用long long*return 0; } 通过验证单继承中虚函数确实都在虚函数表中验证时要注意编辑器有时候对虚表处理不干净需要我们重新生成解决方案重新编译一下 5.2多继承中的虚函数表 class A1 { public:virtual void func1() { cout A1::func1 endl; }virtual void func2() { cout A1::func2 endl; } private:int a1; };class A2 { public:virtual void func1() { cout A2::func1 endl; }virtual void func2() { cout A2::func2 endl; } private:int a2; };class B : public A1, public A2 { public:virtual void func1() { cout B::func1 endl; }virtual void func3() { cout B::func3 endl; } private:int b; };int main() {B b;printf(虚表地址%p\n,* ((int*)b));PrintVF((VF*)(*((int*)b)));A2* p b;printf(虚表地址%p\n, *((int*)p));PrintVF((VF*)(*((int*)p)));return 0; } 可以看出多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中且子类会继承父类的虚函数表 这里简单说明一下为什么两个虚表中B::func1函数的地址不同的问题其实这是为了修正this指针我们知道多态的实现离不开指针(引用)当我们用p去调用func1函数时显然传过去的this指针是不对的p仅仅指向A2的部分所以编辑器会在调用B::func1这个函数之前将this指针修正成b对象的起始地址而继承来的A1成员的起始地址恰好和b对象的起始地址相同不需要修正所以直接填了函数地址所以两者地址不同但是最终调用的函数一样 5.3菱形继承、菱形虚拟继承 这里只要了解即可 菱形进程和多继承一样无非是消耗了空间这里就不多讲了 我们来看看菱形虚拟继承 class A { public:virtual void func1() { cout A1::func1 endl; } private:int a1; };class A1 : virtual public A{ public:virtual void func1() { cout A1::func1 endl; }virtual void func2() { cout A1::func2 endl; } private:int a12; };class A2 : virtual public A { public:virtual void func1() { cout A2::func1 endl; }virtual void func2() { cout A2::func2 endl; } private:int a23; };class B : public A1, public A2 { public:virtual void func1() { cout B::func1 endl; }virtual void func3() { cout B::func3 endl; } private:int b4; };上面两个图是分开调试的需要单独分析仅仅当长个见识不会也没关系 六、一些小点 1.inline函数可以是虚函数吗 可以不过编译器就忽略inline属性这个函数就不再是 inline因为虚函数要放到虚表中 class A { public:inline virtual void func() { cout A::func(); } }; int main() {A a;A* p new A;a.func();p-func();return 0; }上面的调试内容需要将相关优化打开正常调试看不到这里也就是给大家看看  2.静态成员可以是虚函数吗 不能因为静态成员函数没有this指针使用类型::成员函数的调用方式无法访问虚函数表所以静态成员函数无法放进虚函数表。 3.构造函数可以是虚函数吗 不可以因为虚表指针是在构造函数初始化列表阶段才初始化的 4.对象访问普通函数快还是虚函数快 首先如果是普通对象是一样快的。如果是指针对象或者是引用对象则调用的普通函数快因为构成多态运行时调用虚函数需要到虚函数表中去查找。
http://www.zqtcl.cn/news/402029/

相关文章:

  • 网站改版降权多久恢复经典网站
  • 南昌建网站的公司中国专业做鞋子的网站
  • 做100个垂直网站网站建设外包
  • 网站开发图片素材营销软文的范文
  • 手机网站免费建设排行湖南公司响应式网站建设价位
  • 专业网站设计制作价格wordpress 动画特效
  • 如何找企业联系做网站网站内容建设的核心和根本是
  • 网站制作找如何判断一个网站是恶意网站
  • dedecms 网站导航建设自己网站的流程图
  • 临海城市建设网站石家庄做网站的公司有哪些
  • 东光网站建设淘宝店铺装修开个送快餐网站怎么做
  • 建设网站有哪些怎么自学室内设计与装修
  • 苏州建设工程协会网站汶上网站建设哪家便宜
  • 湖南手机版建站系统信息做360网站优化
  • 为什么学网站开发中国猎头公司排行榜
  • 怎么给网站做api做设计找参考的设计网站有那些
  • vultr服务器做网站广州做seo整站优化公司
  • 怎么做一个门户网站婚介网站怎么做
  • 惠州做网站电话柳市哪里有做网站推广
  • 成都公司的网站制作网站建设网店名字
  • 网站备案医疗保健审批号是什么基于asp.net网站开发
  • 生活做爰网站如何用织梦做网站
  • 网站拒绝被百度收录c#+开发网站开发
  • 营销网站竞品分析报告东莞网页制作网站
  • 东莞手机手机端网站建设云企网站
  • 网站中弹出广告怎么做网站建设实践报告
  • 站长工具seo综合查询隐私查询导航网站诚信备案
  • 亳州做网站哪家好网站开发的现实意义
  • 创意视觉网站济南网站建设招标
  • 厦门市建设局报表网站南通网站建设计划书