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

免费装修效果图网站网站模板套用教程

免费装修效果图网站,网站模板套用教程,wordpress在线制作网页,彩票推广网站如何做多态 一、初识多态概念“登场”1>. 多态的构成条件2>. 虚函数3>. 虚函数重写(覆盖)4>. 虚函数重写的两个例外1. 协变 一 基类和派生类虚函数返回值类型不同2. 析构函数重写(基类和派生类析构函数名不同) 小结 二、延伸…

多态

  • 一、初识多态
    • 概念
    • “登场”
      • 1>. 多态的构成条件
      • 2>. 虚函数
      • 3>. 虚函数重写(覆盖)
      • 4>. 虚函数重写的两个例外
        • 1. 协变 一 基类和派生类虚函数返回值类型不同
        • 2. 析构函数重写(基类和派生类析构函数名不同)
    • 小结
  • 二、延伸知识
    • 1>. C++11 override和final
      • 拓展一最终类
    • 2>. 抽象类
      • 概念
      • 接口继承和实现继承
  • 三、原理
    • 1>. 虚函数表(也称虚表)
      • 引入
      • 分析虚表
    • 2>. 多态的原理
    • 3>. 拓展 一 静态绑定和动态绑定
  • 四、单继承和多继承的虚函数表
    • 1>. 单继承中的虚函数表
    • 2>. 多继承中的虚函数表
      • 1. 多继承
      • 2. 菱形继承
      • 3. 菱形虚拟继承

一、初识多态

概念

概念:去完成某个行为,当不同的对象去完成时会产生出不同的状态

eg:
买车票:普通成年人买票时,是全价票;学生买票时,是半价票;

“登场”

1>. 多态的构成条件

  1. 多态是在不同继承关系的类对象,去调用同一函数,产生不同的行为。
  2. 在继承的继承上,需要:
  • 必须通过基类的指针或者引用调用虚函数
  • 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写 (覆盖)

eg: “见见猪跑”
多态

2>. 虚函数

虚函数:被virtual修饰的类成员函数

虚函数

3>. 虚函数重写(覆盖)

虚函数重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型,函数名,参数类型完全相同)。称子类的虚函数重写了父类的虚函数

注意: 重写基类虚函数时,派生类的虚函数可以不加virtual关键字。
eg:
virtual

4>. 虚函数重写的两个例外

虚函数重写的要求是派生类虚函数与基类虚函数的返回值类型,函数名,参数类型完全相同(三同)。例外的原因就是不满足三同

1. 协变 一 基类和派生类虚函数返回值类型不同

协变:派生类重写基类虚函数时,与基类虚函数返回值类型不同。
满足协变的条件:返回值类型可以不同,但是返回值必须是父子关系的指针或引用

test code:

class A
{};
class B : public A
{};class Person
{
public:virtual A* f(){return new A;}
};class Student : public Person
{
public://注意在重写的地方,返回值类型虽然可以不同,但是必须是父子关系,而且同指针或同引用。不能出现基类虚函数返回值类型是父类引用,而派生类重写的虚函数返回类型是派生类的指针virtual B* f(){return new B;}
};

2. 析构函数重写(基类和派生类析构函数名不同)

基类的析构函数为虚函数,则派生类析构函数只要定义,无论是否加virtual关键字,都构成重写。虽然表象函数名不同,但是编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor

test code:

class Person
{
public:virtual ~Person(){cout << "~Person()" << endl;}
};class Student : public Person
{
public:virtual ~Student(){cout << "~Student()" << endl;}
};//只有派生类Student的析构函数重写了Person的析构函数,
//这时delete调用析构时,才能构成多态,保证正确调用析构函数
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}//output:
//~Person()
//~Student()
//~Person()

小结

重载、覆盖(重写)、隐藏(重定义)的对比:
重载、覆盖(重写)、隐藏(重定义)的对比

二、延伸知识

1>. C++11 override和final

  1. override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写报错

test code:

class Car
{
public:virtual void Drive() {}
};class Benz : public Car
{
public:virtual void Drive() override   //ok  完成了重写{cout << "Benz-舒适" << endl;}
};
  1. final:修饰虚函数,表示该虚函数不能再被重写

test code:

class Car
{
public:virtual void Drive() final{}
};class Benz : public Car
{
public:virtual void Drive()  //error  原因:final禁止了重写{cout << "Benz-舒适" << endl;}
};

拓展一最终类

当我们想设计不想被继承的类时,有两种方法

方法1 一一 对应C++98

eg1: 隐藏构造函数,当想要创建A对象时,定义一个静态的成员函数

class A
{
public:static A CreateObj(){return A();}
private:A(){}
};class B : public A
{};int main()
{//B bb;     //errA::CreateObj();return 0;
}

eg2:隐藏析构函数,当想要创建A对象new一个,释放时定义一个静态的destructor,即可

class A
{
public:
private:~A(){}
};
class B : public A
{};int main()
{//B bb;   //errA* p = new A;return 0;
}

方法2 一一 对应C++11
eg:被final修饰的类,被称为最终类,不能被继承

class A final
{
public:
private:
};
class B : public A
{};

2>. 抽象类

概念

  1. 纯虚函数:虚函数的后面写上 = 0
  2. 抽象类(接口类):包含纯虚函数的类。
    抽象类不能实例化出对象,派生类继承后也不能,只有重写纯虚函数,派生类才可以实例化对象。规范了派生类必须重写。

test code:

class Car
{
public:virtual void Drive() = 0;
};class Benz : public Car
{
public:virtual void Drive(){cout << "Benz-舒适" << endl;}
};class BMW : public Car
{
public:virtual void Drive(){cout << "BMW-好操控" << endl;}
};int main()
{Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive();return 0;
}

接口继承和实现继承

  1. 实现继承:普通函数继承。派生类继承基类函数,可以使用,继承的是函数实现。
  2. 接口继承:虚函数的继承。派生类继承的是虚函数的接口,目的是为了重写,达成多态,继承的是接口。
    注意:不实现多态就不要把函数定义成虚函数

三、原理

基于vs2019进行模型分析

1>. 虚函数表(也称虚表)

引入

test code:

//计算Base对象的大小
class Base
{
public:virtual void Func(){cout << "Func()" << endl;}private:int _b = 1;
};int main()
{Base b;cout << sizeof(b) << endl;return 0;
}
//output: 8

代码分析:
模型分析

分析虚表

test code:

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 b;Derive d;return 0;
}

通过上面的测试代码,发现一下六点:

虚表

2>. 多态的原理

上面分析了很久的虚表,以对虚表的介绍为基础,来分析多态的原理。

test code:

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 ps;Func(&ps);ps.BuyTicket();Student st;Func(&st);return 0;
}

达到多态,有两个条件:一是虚函数覆盖,一个是对象的指针或引用调用虚函数。
通过下面汇编代码的分析,看出满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象中去找的。不满足多态的函数调用是编译时确认好的。

函数覆盖

对象的指针或引用

3>. 拓展 一 静态绑定和动态绑定

  1. 静态绑定(前期绑定):在程序编译期间确定了程序的行为,也称静态多态。 eg:函数重载
  2. 动态绑定(后期绑定):是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称动态多态

上一个汇编代码的例子,就很好的解释了静态绑定和动态绑定

四、单继承和多继承的虚函数表

1>. 单继承中的虚函数表

test code:

class Base
{
public:virtual void func1(){cout << "Base::func1()" << endl;}virtual void func2(){cout << "Base::func2()" << endl;}
private:int _b;
};class Derive : public Base
{
public:virtual void func1(){cout << "Derive::func1()" << endl;}virtual void func3(){cout << "Derive::func3()" << endl;}virtual void func4(){cout << "Derive::func4()" << endl;}
private:int _d;
};int main()
{Base b;Derive d;return 0;
}

观察下图中监视窗口,发现派生类的虚函数func3和func4看不见。 原因:编译器的监视窗口隐藏了这个两个函数。

查看虚表

在分析虚表这一小节内容时,在第四小点说到,虚表本质是一个存虚函数指针的指针数组,一般情况这个数组最后放一个nullptr,通过监视窗口查看不了派生类对象d的虚表,下面我们借用nullptr的帮助,使用代码打印出虚表中的函数

PrintVTable_code:打印虚表的代码

注意:这个打印虚表的代码经常崩溃,编译器对虚表的处理不干净,虚表最后没有放nullptr的指针,导致越界。我们只需要清理解决方案,重新编译即可。

typedef void(*VFPtr) ();  //函数指针
void PrintVTable(VFPtr VTable[])
{//依次取虚表中的虚函数指针打印并调用。cout << "虚表地址>" << VTable << endl;for (size_t i = 0; VTable[i] != nullptr; i++){printf("第%d个虚函数地址:0X%p,->", i, VTable[i]);VFPtr f = VTable[i];f();    //调用方便看存的是那个函数}cout << endl;
}

注意:传参调用PrintVTable的思路

int main()
{Base b;Derive d;//思路:取b、d对象的头4个字节,就是虚表的指针。//以b对象讲解//1.先取b的地址,强转成int*的指针//2.再解引用取值,就取到b对象头4个字节的值,也就是指向虚表的指针//3.再强转成VFPtr*,因为虚表就是一个存VFPtr类型(虚函数指针类型)的数组//4.虚表指针传递给PrintVTable进行打印虚表VFPtr* vTable_b = (VFPtr*)(*(int*)&b);PrintVTable(vTable_b);VFPtr* vTable_d = (VFPtr*)(*(int*)&d);PrintVTable(vTable_d);return 0;
}

上面代码打印出虚表中虚函数的结果分析:
代码打印出虚表中虚函数的结果分析

2>. 多继承中的虚函数表

1. 多继承

test code:

class Base1
{
public:virtual void func1(){cout << "Base1::func1()" << endl;}virtual void func2(){cout << "Base1::func2()" << endl;}
private:int _b1 = 1;
};class Base2
{
public:virtual void func1(){cout << "Base2::func1()" << endl;}virtual void func2(){cout << "Base2::func2()" << endl;}
private:int _b2 = 1;
};class Derive : public Base1, public Base2
{
public:virtual void func1(){cout << "Derive::func1()" << endl;}virtual void func3(){cout << "Derive::func3()" << endl;}
private:int _d = 2;
};typedef void(*VFPtr) ();  //函数指针
void PrintVTable(VFPtr vTable[])
{//依次取虚表中的虚函数指针打印并调用。cout << "虚表地址>" << vTable << endl;for (size_t i = 0; vTable[i] != nullptr; i++){printf("第%d个虚函数地址:0X%p,->", i, vTable[i]);VFPtr f = vTable[i];f();    //调用方便看存的是那个函数}cout << endl;
}int main()
{Derive d;VFPtr* vTable_b1 = (VFPtr*)(*(int*)&d);PrintVTable(vTable_b1);//(char*)&d  这里一定要注意强转,否则+1,就是加一个Derive的大小VFPtr* vTable_b2 = (VFPtr*)(*(int*)((char*)&d + sizeof(Base1)));PrintVTable(vTable_b2);return 0;
}

多继承测试代码展开分析:
多继承测试代码分析

2. 菱形继承

test code:

#include<iostream>
using namespace std;
class A
{
public:virtual void fun1(){cout << "A::fun1()" << endl;}int _a = 0;
};class B : public A
{
public:virtual void fun1(){cout << "B::fun1()" << endl;}virtual void fun2(){cout << "B::fun2()" << endl;}int _b = 0;
};class C : public A
{
public:virtual void  fun1(){cout << "C::fun1()" << endl;}virtual void fun2(){cout << "C::fun2()" << endl;}int _c = 0;
};
class D : public B, public C
{
public:virtual void  fun2(){cout << "D::fun2()" << endl;}virtual void fun3(){cout << "D::fun3()" << endl;}
};typedef void(*VFPtr) ();  //函数指针
void PrintVTable(VFPtr vTable[])
{//依次取虚表中的虚函数指针打印并调用。cout << "虚表地址>" << vTable << endl;for (size_t i = 0; vTable[i] != nullptr; i++){printf("第%d个虚函数地址:0X%p,->", i, vTable[i]);VFPtr f = vTable[i];f();    //调用方便看存的是那个函数}cout << endl;
}int main()
{D d;VFPtr* vTable_d = (VFPtr*)(*(int*)&d);PrintVTable(vTable_d);C* ptr1 = &d;VFPtr* vTable_c = (VFPtr*)(*(int*)ptr1);PrintVTable(vTable_c);return 0;
}

菱形继承测试代码展开分析:(菱形继承和多继承没有什么大的区别)
菱形继承测试代码展开分析

3. 菱形虚拟继承

  1. 只有A类有虚函数

test code:

class A
{
public:virtual void fun1(){cout << "A::fun1()" << endl;}
public:int _a = 0;
};class B : virtual public A
{
public:int _b = 0;
};class C : virtual public A
{
public:int _c = 0;
};
class D : public B, public C
{
public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

代码内存分析:
代码内存分析

  1. B和C完成对A的虚函数重写

test code:

class A
{
public:virtual void fun1(){cout << "A::fun1()" << endl;}
public:int _a = 0;
};class B : virtual public A
{
public:virtual void fun1(){cout << "B::fun1()" << endl;}
public:int _b = 0;
};class C : virtual public A
{
public:virtual void fun1(){cout << "C::fun1()" << endl;}
public:int _c = 0;
};
class D : public B, public C
{
public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

运行结果:
编译报错

原因:因为整个对象只有A一张虚表是共享的,B要重写,C也要重写。不明确到底重写谁的。
解决办法:让D重写即可。当然B和C的重写并不是没有意义,如果定义B和C类型的对象,单独使用还是有意义的。

  1. 只有A类有虚函数 + B和C有单独的虚函数

test code:

class A
{
public:virtual void func1(){cout << "A::func1" << endl;}
public:int _a;
};class B : virtual public A
{
public:virtual void func1(){cout << "B::func1" << endl;}virtual void func2(){cout << "B::func2" << endl;}
public:int _b;
};class C : virtual public A
{
public:virtual void func1(){cout << "C::func1" << endl;}virtual void func2(){cout << "C::func2" << endl;}
public:int _c;
};class D : public B, public C
{
public:virtual void func1(){cout << "D::func1" << endl;}public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

代码内存分析:
代码内存分析

  1. 如果在第三点的基础上,D类也有自己的虚函数,那么将放哪里?

test code:

class A
{
public:virtual void func1(){cout << "A::func1" << endl;}
public:int _a;
};class B : virtual public A
{
public:virtual void func1(){cout << "B::func1" << endl;}virtual void func2(){cout << "B::func2" << endl;}
public:int _b;
};class C : virtual public A
{
public:virtual void func1(){cout << "C::func1" << endl;}virtual void func2(){cout << "C::func2" << endl;}
public:int _c;
};class D : public B, public C
{
public:virtual void func1(){cout << "D::func1" << endl;}virtual void func3(){cout << "D::func3" << endl;}public:int _d;
};typedef void(*VFPtr) ();  //函数指针
void PrintVTable(VFPtr vTable[])
{//依次取虚表中的虚函数指针打印并调用。cout << "虚表地址>" << vTable << endl;for (size_t i = 0; vTable[i] != nullptr; i++){printf("第%d个虚函数地址:0X%p,->", i, vTable[i]);VFPtr f = vTable[i];f();    //调用方便看存的是那个函数}cout << endl;
}int main()
{D d;VFPtr* vTable_d = (VFPtr*)(*(int*)&d);    //B的虚表PrintVTable(vTable_d);C* ptr1 = &d;VFPtr* vTable_c = (VFPtr*)(*(int*)ptr1);  //C的虚表PrintVTable(vTable_c);A* ptr2 = &d;VFPtr* vTable_a = (VFPtr*)(*(int*)ptr2);  //A的虚表PrintVTable(vTable_a);return 0;
}

运行结果:由结果得到D类自己的虚函数放在第一张虚表中
代码运行结果


文章转载自:
http://flashtube.rdgb.cn
http://potage.rdgb.cn
http://asteriated.rdgb.cn
http://cowichan.rdgb.cn
http://gersdorffite.rdgb.cn
http://coelom.rdgb.cn
http://careless.rdgb.cn
http://periosteum.rdgb.cn
http://defiant.rdgb.cn
http://pleiades.rdgb.cn
http://spanrail.rdgb.cn
http://anecdotage.rdgb.cn
http://hemiscotosis.rdgb.cn
http://applications.rdgb.cn
http://inexact.rdgb.cn
http://infighter.rdgb.cn
http://holdover.rdgb.cn
http://bombay.rdgb.cn
http://phigs.rdgb.cn
http://doorless.rdgb.cn
http://confab.rdgb.cn
http://administrative.rdgb.cn
http://frith.rdgb.cn
http://senor.rdgb.cn
http://voicelessly.rdgb.cn
http://overmatch.rdgb.cn
http://ridiculous.rdgb.cn
http://gummatous.rdgb.cn
http://synovia.rdgb.cn
http://undress.rdgb.cn
http://cranioplasty.rdgb.cn
http://areographic.rdgb.cn
http://this.rdgb.cn
http://cutinize.rdgb.cn
http://sanandaj.rdgb.cn
http://causer.rdgb.cn
http://israelitish.rdgb.cn
http://hoofpick.rdgb.cn
http://bajra.rdgb.cn
http://hypoptyalism.rdgb.cn
http://remissive.rdgb.cn
http://edit.rdgb.cn
http://galbulus.rdgb.cn
http://rota.rdgb.cn
http://perspicuity.rdgb.cn
http://megalocephalous.rdgb.cn
http://leeds.rdgb.cn
http://velure.rdgb.cn
http://dript.rdgb.cn
http://launch.rdgb.cn
http://parasitical.rdgb.cn
http://wavetable.rdgb.cn
http://rarer.rdgb.cn
http://signature.rdgb.cn
http://naafi.rdgb.cn
http://coccidia.rdgb.cn
http://khodzhent.rdgb.cn
http://sculk.rdgb.cn
http://pompadour.rdgb.cn
http://allier.rdgb.cn
http://three.rdgb.cn
http://impermanence.rdgb.cn
http://preordain.rdgb.cn
http://embolum.rdgb.cn
http://mineralold.rdgb.cn
http://dipsomaniac.rdgb.cn
http://ccp.rdgb.cn
http://traitorously.rdgb.cn
http://filterable.rdgb.cn
http://succentor.rdgb.cn
http://gerontomorphosis.rdgb.cn
http://spare.rdgb.cn
http://pragmatist.rdgb.cn
http://dramalogue.rdgb.cn
http://uphill.rdgb.cn
http://massecuite.rdgb.cn
http://nothing.rdgb.cn
http://sheila.rdgb.cn
http://draftsman.rdgb.cn
http://amusive.rdgb.cn
http://misappropriate.rdgb.cn
http://pneumonic.rdgb.cn
http://oswald.rdgb.cn
http://bedazzle.rdgb.cn
http://kelt.rdgb.cn
http://mope.rdgb.cn
http://ondometer.rdgb.cn
http://gallipot.rdgb.cn
http://wagtail.rdgb.cn
http://declension.rdgb.cn
http://canker.rdgb.cn
http://scuttlebutt.rdgb.cn
http://pelecaniform.rdgb.cn
http://spyhole.rdgb.cn
http://underwritten.rdgb.cn
http://scaldino.rdgb.cn
http://linage.rdgb.cn
http://uproariously.rdgb.cn
http://stouthearted.rdgb.cn
http://cinderella.rdgb.cn
http://www.hrbkazy.com/news/90456.html

相关文章:

  • 什么是网站的层次东莞网络营销网站建设
  • 南宁大型网站设计公司简述seo的基本步骤
  • 软件测试7种方法悟空建站seo服务
  • 做网站公司没签合同百度推广按效果付费是多少钱
  • 广州网站建设与网页设计宠物美容师宠物美容培训学校
  • 做园区门户网站的需求分析重庆广告公司
  • 一般做网站的宽度怎么处理的国色天香站长工具
  • 华为云域名注册合肥seo
  • 做瞹瞹嗳网站百度知道灰色词代发收录
  • 常州做网站需要多少钱网站批量查询工具
  • 公司百度网站怎么做免费永久注册顶级域名网站
  • 自己做网站卖什么百度用户服务中心电话
  • 做网页的心得体会优化设计答案大全英语
  • 那些网站是针对物流做的爬虫搜索引擎
  • 温州网站建设 www.lcnt.net阿里指数数据分析平台
  • 做音乐网站赚钱吗营销软文推广平台
  • 做网站运营的女生多吗广州seo代理计费
  • 网站设计 企业 济南qq营销软件
  • 响应式网站制作公司网站建设开发公司
  • python和php网站开发北京网站推广排名外包
  • 如何通过阿里云自己做网站河南网站推广
  • 济南市住房和城乡建设部网站网站网络营销推广
  • 做6个页面的网站千锋教育郑州校区
  • 贺州市住房与城乡建设局网站互联网seo是什么意思
  • 白酒招商网站大全推广营销
  • 定制网站开发技术阿里云域名注册官网网址
  • 成都新闻网长沙关键词优化费用
  • 房山企业网站建设公司广州最新疫情最新消息
  • wordpress小工具空格排名seo公司
  • 深圳网站制作公司在那企业怎么做好网站优化