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

做网站 就上凡科网网络推广有多少种方法

做网站 就上凡科网,网络推广有多少种方法,噼里啪啦在线看免费观看视频,推广网络营销外包公司C进阶#xff1a;详解多态#xff08;多态、虚函数、抽象类以及虚函数原理详解#xff09; 结束了继承的介绍#xff1a;C进阶#xff1a;详细讲解继承 那紧接着的肯定就是多态啦 文章目录 1.多态的概念2.多态的定义和实现2.1多态的构成条件2.2虚函数2.2.1虚函数的概念2…C进阶详解多态多态、虚函数、抽象类以及虚函数原理详解 结束了继承的介绍C进阶详细讲解继承 那紧接着的肯定就是多态啦 文章目录 1.多态的概念2.多态的定义和实现2.1多态的构成条件2.2虚函数2.2.1虚函数的概念2.2.2虚函数的重写2.2.3虚函数重写的两个例外 2.3C11里 override 和 final2.4重载、覆盖(重写)、隐藏(重定义)的对比 3.抽象类3.1概念3.2接口继承和实现继承 4.虚函数原理4.1虚函数表vtable4.2多态的原理4.3动态绑定与静态绑定 5.单继承和多继承关系的虚函数表5.1单继承中的虚函数表5.2多继承中的虚函数表 1.多态的概念 多态是指同一个函数名可以根据调用对象的不同而具有不同的实现。它分为两种类型编译时多态静态多态和运行时多态动态多态。 编译时多态 通过函数重载和运算符重载实现是在编译阶段确定函数调用。重载允许一个函数名有多个定义编译器根据函数参数和上下文来选择正确的定义。运行时多态 通过虚函数和继承实现是在运行阶段确定函数调用。运行时多态允许通过基类指针或引用来调用派生类的函数实现了动态绑定。 2.多态的定义和实现 2.1多态的构成条件 多态的实现通常依赖于虚函数。在基类中声明虚函数然后在派生类中进行重写覆盖。通过基类指针或引用调用虚函数时将根据对象的实际类型调用相应的派生类函数 从上面这段话我们知道在继承中要构成多态还有两个条件 必须通过基类的指针或者引用调用虚函数 被调用的函数必须是虚函数且派生类必须对基类的虚函数进行重写 class Person { public:virtual void BuyTicket() { cout 买票-全价 endl; }//虚函数 };class Child:public Person { public:virtual void BuyTicket() { cout 买票-半价 endl; }//子类进行重写了 };void test1() {Person* p new Person;//基类的指针p-BuyTicket();//调用BuyTicketChild ch;p ch;//现在基类的指针指向了子类p-BuyTicket(); }int main() {test1();return 0; }2.2虚函数 2.2.1虚函数的概念 虚函数是在基类中使用 virtual 关键字声明的成员函数它的存在允许在派生类中进行函数的重写覆盖。通过虚函数可以实现运行时多态性 class Person { public:virtual void BuyTicket() { cout 买票-全价 endl; }//虚函数 };2.2.2虚函数的重写 虚函数的重写Override是指在派生类中重新实现覆盖了基类中已经声明为虚函数的函数。在进行函数重写时子类中的虚函数的返回值类型、函数名、参数列表必须与基类中的虚函数完全相同 注意在重写基类虚函数时派生类的虚函数在不加virtual关键字时虽然也可以构成重写(因为继承后基类的虚函数被继承后在派生类依旧保持虚函数属性)但是该种写法不规范大家还是少用为好。 2.2.3虚函数重写的两个例外 协变基类与派生类虚函数返回值类型不同) 派生类重写基类虚函数时与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用派生类虚函数返回派生类对象的指针或者引用时称为协变 class A { public:virtual A* f() { return new A; } };class B:public A { public:virtual B* f(){return new B;} };析构函数的重写基类与派生类的析构函数名字不同 如果基类的析构函数为虚函数此时派生类析构函数只要定义无论是否加virtual关键字都与基类的析构函数构成重写虽然基类与派生类析构函数名字不同。虽然函数名不相同看起来违背了重写的规则其实不然这里可以理解为编译器对析构函数的名称做了特殊处理编译后析构函数的名称统一处理成destructor class Person { public:virtual ~Person(){cout ~Person() endl;} };class Student : public Person { public:virtual ~Student(){cout ~Student() endl;} };void test2() {Person* p1 new Person;Person* p2 new Student;delete p1;delete p2; }int main() {test2();return 0; }这里推荐大家把析构函数设为虚函数上面也是一个经典场景——使用多态时通过基类指针删除派生类对象 2.3C11里 override 和 final final用于在派生类中阻止对虚函数的进一步重写或者在类定义中阻止类被继续派生 防止进一步的派生 class Base final {// ... };防止虚函数的进一步重写 class Base { public:virtual void f() final {// ...} };override检查派生类虚函数是否重写了基类某个虚函数如果没有重写编译报错 class Car { public:virtual void Drive() {} };class Benz :public Car { public:virtual void Drive() override {} };这里派生类中符合重写要求没有报错 2.4重载、覆盖(重写)、隐藏(重定义)的对比 3.抽象类 3.1概念 在虚函数的后面写上 0则这个函数为纯虚函数 。 包含纯虚函数的类叫做抽象类 也叫接口 类抽象类不能实例化出对象但可以定义基类指针用来实现多态 派生类继承后也不能实例化出对象 只有重写纯虚函数派生类才能实例化出对象 。 纯虚函数规范了派生类必须重写另外纯虚函数更体现出了接口继承 class Car//抽象类不能实例化出对象 { public:virtual void Drive() 0; // 纯虚函数 };class Car1 :public Car { public:virtual void Drive() // 必须重写基类虚函数派生类才能实例化出对象{cout Car1-舒适 endl;} };class Car2 :public Car { public:virtual void Drive(){cout Car2-操控 endl;} };void test3() {Car* p1 new Car1;p1-Drive();Car* p2 new Car2;p2-Drive(); }int main() {test3();return 0; }3.2接口继承和实现继承 普通函数的继承是一种实现继承派生类继承了基类函数可以使用函数继承的是函数的实现。虚函数的继承是一种接口继承派生类继承的是基类虚函数的接口目的是为了重写达成多态继承的是接口。所以如果不实现多态不要把函数定义成虚函数 普通函数的继承实现继承 在普通函数的继承中派生类继承了基类的函数的具体实现。派生类可以直接使用基类的函数而不需要重新实现该函数。这种继承关系主要关注函数的具体行为和功能。 虚函数的继承接口继承 在虚函数的继承中派生类继承了基类的虚函数的接口即函数的声明。派生类必须重新实现基类的虚函数并且可以通过多态性实现运行时的动态绑定。这种继承关系强调了对函数接口的统一定义为实现多态提供了基础。 4.虚函数原理 4.1虚函数表vtable class Base { public:virtual void Func1(){cout Func1() endl;} private:int _base 1; };int main() {Base b;return 0; }通过观察测试我们发现b对象中除了 _base成员还多一个 _vfptr 放在前面注意有些平台可能会放到对象的最后面跟平台有关对象中的这个指针我们叫做虚函数表指针 v代表 virtualf代表 function。 一个含有虚函数的类中都至少都有一个虚函数表指针因为虚函数的地址要被放到虚函数表中(其实是一个函数指针数组)虚函数表也简称虚表 利用下面的代码进一步讨论 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 _base 1; };class Derive : public Base { public:virtual void Func1(){cout 虚函数Derive::Func1() endl;} private:int _derive 2; };int main() {Base bb;Derive dd;return 0; }虚函数表Virtual Function Table是在编译期间生成的。编译器在编译每个包含虚函数的类时会在该类的内部生成一个虚函数表其中包含了该类中所有虚函数的地址。这个过程是在编译期间静态地完成的因为编译器可以确定每个类中虚函数的数量和排列顺序。 虚函数表指针vptr的赋值是在对象的构造函数中完成的。具体来说当创建一个对象时首先会调用该对象的构造函数而构造函数的初始化列表是在对象实际构造之前执行的。 在构造函数的初始化列表中虚表指针vptr会被赋值为该类的虚函数表的首地址。这样在对象的构造期间虚表指针就已经指向了正确的虚函数表从而确保在对象的构造期间就可以正确调用虚函数 派生类对象dd中也有一个虚表指针dd对象由两部分构成一部分是父类继承下来的成员虚表指针也就是存在这一部分另一部分是自己的成员 基类b对象和派生类d对象虚表是不一样的这里我们发现Func1完成了重写所以dd的虚表中存的是重写的Derive::Func1所以虚函数的重写也叫作覆盖覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法覆盖是原理层的叫法 Func2继承下来后是虚函数所以放进了虚表Func3也继承下来了但是不是虚函数所以不会放进虚表 虚函数表本质是一个存虚函数指针的指针数组一般情况这个数组最后面放了一个nullptr(这个也是看平台) 总结一下派生类的虚表生成 先将基类中的虚表内容拷贝一份到派生类虚表中如果派生类重写了基类中某个虚函数用派生类自己的虚函数覆盖虚表中基类的虚函数派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后 虚函数和普通函数一样的都是存在代码段的只是他的指针又存到了虚表中。另外对象中存的存的是虚表指针。那么虚表其实在vs下是也存在代码段的 4.2多态的原理 class Person { public:virtual void BuyTicket() { cout 买票-全价 endl; } };class Student : public Person { public:virtual void BuyTicket() { cout 买票-半价 endl; } };void Func(Person p) {p.BuyTicket(); }void test4() {Person Mike;Func(Mike);Student Johnson;Func(Johnson); }int main() {test4();return 0; }观察下图的红色箭头我们看到p是指向mike对象时p-BuyTicket在mike的虚表中找到虚函数是Person::BuyTicket。 观察下图的蓝色箭头我们看到p是指向johnson对象时p-BuyTicket在johson的虚表中找到虚函数是Student::BuyTicket。 满足多态以后的函数调用不是在编译时确定的是运行起来以后到对象中找的。不满足多态的函数调用时编译时确认好的 其实这里还是利用了切割编译器看到的都是父类不过指向子类时里的父类是切割过去的而已里面的虚表也是子类覆盖后的找到的地址也是子类的虚函数的 两个问题 基类对象的指针 / 引用调用虚函数的原理是什么 基类指针或引用调用虚函数时编译器生成的机器代码确实会先访问对象的虚函数指针vptr再通过虚函数表vtable找到实际要调用的虚函数的地址最终进行调用。这种动态绑定的过程使得程序在运行时能够根据对象的实际类型来调用正确的虚函数实现了多态性 为什么多态必须要用基类的指针 / 引用来调用虚函数而用基类对象调用却不行 当派生类对象赋值给基类对象时只会拷贝对象中的数据成员而不会拷贝派生类的虚表指针。因此基类对象中的虚函数调用会绑定到基类的虚函数表上而无法访问派生类的虚函数。 多态必须使用基类的指针/引用来调用虚函数的原因主要是因为基类指针/引用可以在运行时指向派生类对象而且能正确地调用派生类的虚函数。这是因为在运行时基类指针/引用会指向实际对象的虚表从而实现了动态绑定根据对象的实际类型调用正确的虚函数 4.3动态绑定与静态绑定 静态绑定又称为前期绑定(早绑定)在程序编译期间确定了程序的行为也称为静态多态 比如函数重载 动态绑定又称后期绑定(晚绑定)是在程序运行期间根据具体拿到的类型确定程序的具体行为调用具体的函数也称为动态多态。 5.单继承和多继承关系的虚函数表 在单继承和多继承关系中下面来主要研究的是派生类对象的虚表模型因为基类的虚表模型没什么需要特别研究的 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 A { public:virtual void func1(){cout B::func1 endl;}//这里B自己又多加了两个虚函数virtual void func3(){cout B::func3 endl;}virtual void func4(){cout B::func4 endl;} private:int _b; };int main() {B bb;return 0; }我们也可利用下面函数来打印虚表内容进行验证 typedef void(*vfptr)();void printvf(vfptr* ptr) {for (int i 0; ptr[i] ! nullptr; i)//以空指针结束{// 依次打印虚表各元素printf( 第%d个虚函数地址 :%p, i1, ptr[i]);// 把虚表各元素赋值给函数指针fvfptr f ptr[i];// 调用函数f();}cout endl; }int main() {B bb;printvf((vfptr*)(*(int*)bb));//先bb地址出来因为vs下虚表指针在最前面四个字节利用强转为int*取到前四个字节//然后*解引用得到地址再强转为vfptr* 进行调用函数return 0; }5.2多继承中的虚函数表 class Base1 { public:virtual void func1() { cout Base1::func1 endl; }virtual void func2() { cout Base1::func2 endl; } private:int b1; };class Base2 { public:virtual void func1() { cout Base2::func1 endl; }virtual void func2() { cout Base2::func2 endl; } private:int b2; };class Derive : public Base1, public Base2 { public:virtual void func1() { cout Derive::func1 endl; }//自己又加上两个fun3会在哪个虚表里填上呢virtual void func3() { cout Derive::func3 endl; } private:int d1; };int main() {Derive dd;Base1* b1 dd;Base1* b2 dd;printvf((vfptr*)(*(int*)b1));printvf((vfptr*)(*(int*)b2));return 0; }是有两个虚表的一个基类一个 观察上图可以看出多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中 好啦这次知识的内容就先到这里啦多态在笔试当中选择题经常考察在面试中也会问。以后大概率会对这部分进行梳理感谢大家支持
http://www.pierceye.com/news/329115/

相关文章:

  • 邯郸网站建设哪家好重庆app开发
  • 自学网站开发多久大型网站建站
  • 网站设计定制多少钱新增备案网站负责人
  • 匿名聊天网站开发网站关键字挖掘
  • 外国域名注册很多网站做网站的人找不到了
  • 好的学习网站打广告免费浏览器网站
  • 美团先做网站还是app学生网站建设的总结与评价
  • 网站建设代理网站wordpress微博
  • dw建设网站视频宁波seo优化项目
  • 网站里添加百度地图浙江网站建设公司
  • php网站开发最新需求排名优化百度
  • 网站制作的电话智慧校园信息门户网站建设
  • 网站备案申请福田企业网站优化方案
  • 企业网站seo怎么做有空间站的国家
  • Linux网站建设总结网站建设目的确定
  • 怎么做网站的内部链接wordpress 写php页面跳转
  • 推广自己的网站网页设计代码html文件怎么查
  • 网站在线制作软件邯郸公众号小程序制作
  • 网站后台生成静态页面天津百度推广电话号码
  • 网站单个页面301跳转湖南省建设局网站
  • 潮州网站建设十堰seo招聘
  • 企业网站建设公司公司系统优化的方法
  • 网站开发与sparkwordpress default
  • 品牌网站建设帮你大蝌蚪北京做网站建设的公司排名
  • 中国建设第一平台网站网络网站建设10大指标
  • 书画院网站源码网站主题模板下载不了
  • 邢台制作网站网上申报流程
  • 做网站的困难做的网站有营销效果吗
  • 高端集团网站建设公司做网站开发的有外快嘛
  • 网站服务器防火墙设置惠州网络推广公司哪家好