网站开发的合同范本wordpress怎么开发文档

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

网站开发的合同范本,wordpress怎么开发文档,泉州网站建站模板,响应式企业网站设计C多态概念详解 一#xff0c;多态概念二#xff0c;多态的定义2.1 多态构成的条件2.2 什么是虚函数2.3 虚函数的重写2.3.1 虚函数重写的特例2.3.2 override和final 2.4 重载和重写#xff08;覆盖#xff09;和重定义#xff08;隐藏#xff09;的区别 三#xff0c;抽象… C多态概念详解 一多态概念二多态的定义2.1 多态构成的条件2.2 什么是虚函数2.3 虚函数的重写2.3.1 虚函数重写的特例2.3.2 override和final 2.4 重载和重写覆盖和重定义隐藏的区别 三抽象类3.1 概念3.2 接口继承和实现继承 四多态的原理4.1 虚函数表4.2 多态调用的底层原理4.3 静态绑定和动态绑定 五单继承和多继承的虚函数表5.1 单继承的虚函数表5.2 多继承的虚函数表 六继承和多态的常见问题 一多态概念 上节我们看了继承现在我们来看多态。 那么什么是多态呢通俗来说就是多种形态具体点就是去完成某个行为当不同的对象去完成时会产生出不同的状态。 举个例子对于买票这件事一个成人去买的话是全票但如果是学生则半价在这件事中成人和学生都可以买票但是不同的人买票价却不同这就是一种多态行为。 二多态的定义 2.1 多态构成的条件 多态是在不同继承关系的类对象去调用同一函数产生了不同的行为。 在继承中构成多态要满足两个条件 在子类中对父类的虚函数进行重写且必须调用。通过父类的指针或者引用调用虚函数 那什么是虚函数及什么是重写我们下面就来讲解 2.2 什么是虚函数 其实虚函数就是加上virtual的函数 比如下面的代码 class Person { public:virtual void BuyTicket(){cout 买票-全价 endl;} };2.3 虚函数的重写 虚函数要完成重写那么重写就是子类中有一个和父类一样的虚函数这个虚函数要求 函数名返回值类型参数列表相同 class Person { public:virtual void BuyTicket() {cout 买票-全价 endl; } }; class Student : public Person { public:virtual void BuyTicket() {cout 买票-半价 endl; } };2.3.1 虚函数重写的特例 虚函数的重写有两个特例 协变—–重写的虚函数的类型可以不一样但是要是父子类关系的指针或者引用 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;} };B这个类是A类的子类Student类是Person的子类且都有虚函数f()但是这两个虚函数的类型分别是AB父子类的指针这就是协变。 析构函数的重写 —–父子类的析构函数会被统一成destuctor如果不加virtual构成重写则会构成隐藏不会调用到父类的析构函数进而造成内存泄漏子类的资源没有释放完 class Person { public:virtual ~Person() {cout ~Person() endl;} }; class Student : public Person { public:virtual ~Student() {cout ~Student() endl;} }; // 只有派生类Student的析构函数重写了Person的析构函数下面的delete对象调用析构函 //数才能构成多态才能保证p1和p2指向的对象正确的调用析构函数。 int main() {Person* p1 new Person;Person* p2 new Student;delete p1;delete p2;return 0; }虚函数重写时父类加了virtual而子类不加virtual也构成重写建议加上 class Person { public:virtual void BuyTicket() {cout 买票-全价 endl; } }; class Student : public Person { public:void BuyTicket() {cout 买票-半价 endl; } };2.3.2 override和final C中对于重写的要求比较严格所以有了这两个关键字来检测是否重写 现在有这样一个问题如何实现一个类让其不能被继承 有两种办法 让父类的构造函数私有以为子类的构造要用到父类的构造但是这样会让子类不能实例化出对象用final修饰为最终类 final也可以修饰虚函数修饰后不能被重写 override加在派生类后面检查是否完成重写 2.4 重载和重写覆盖和重定义隐藏的区别 重载我们在前面学过重写在原理层面也叫覆盖上一节讲的隐藏也叫重定义。 看下面的图我们可以看到三者的区别 其实更深层次来看重写就是一种特殊的重定义 三抽象类 3.1 概念 我们先来看什么是纯虚函数就是在虚函数后面加上 0
virtual void fun () 0包含纯虚函数的类叫抽象类接口类并且抽象类不能实例化对象。 抽象类就像某类事物抽象出来的一个特征不是一个具体的东西。例如车是一个抽象类但是像宝马奥迪奔驰是车这个抽象类继承的具体的可实例化的类。 抽象类的派生类必须重写虚函数否则不能实例化因为不重写子类仍然时抽象类间接强制子类重写虚函数 3.2 接口继承和实现继承 普通函数的继承是一种实现继承派生类继承基类函数继承了实现为了复用 虚函数的继承是一种接口继承继承了父类的接口为了重写实现达成多态。 四多态的原理 普通函数和虚函数都是存在代码段的谈到多态的原理我们就不得不说下类对象的存储设计 如下图 一个类中存放着一个指向类成员函数表的指针而这个表中存放的是函数的地址多态的原理就和这种存储结构息息相关。 4.1 虚函数表 先来试想一下如何计算一个有虚函数的类的大小 class Base { public:virtual void Func1(){cout Func1() endl;} private:int _b 1; }; int main() {Base b;cout sizeof(b) endl;return 0; }运行后我们可以发现 这是为什么呢 这是因为Base这个类中除了_b这个成员外还有一个指针_vfptr这个指针是虚函数表指针虚表指针指向的是虚函数指针数组。 那么这个指针指向的表是干嘛的呢我们继续来分析我们让派生类Derive去继承Base类并且增加虚函数。 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; }经过调试我们可以看到 在Base和Derive类中都有_vfptr指针指向了一张表里面貌似存放了虚函数。而且Derive的这个表里第一个存放的是重写的虚函数第二个存放的是Base的第二个虚函数。 其实这个表是虚函数表virtual function table虚表中存储的是虚函数的地址指针。 派生类的虚函数表继承自父类的虚函数表但是会用其自己的虚函数覆盖虚表中第一个位置所以虚函数的重写也叫覆盖 重写时语法层面的覆盖是原理层面的 虚表以空结尾并且虚函数存放的顺序和声明的顺序一致 派生类有两部分一部分是父类的一部分是自己的派生类没有自己单独的虚表而是继承的父类的拷贝父类的虚函数表并覆盖自己重写的虚函数 知道了虚表的存在后我们继续探索。 如果派生类有一个自己的虚函数呢 会在虚表里怎么存放 虚函数表是存放在常量区的在编译时生成好的虚表指针的初始化是在构造函数初始化列表最前面所有对象初始化之前。 同类型的对象共享一个虚函数表 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(); } int main() {Person Mike;Func(Mike);Student Johnson;Func(Johnson);return 0; } 运行后可以看到 由上面的图可知指向父类时会在父类的虚函数表中查找对应的虚函数。 指向子类时会在切割后的父类子类中完成对父类虚函数重写的虚函数表中查找已经被覆盖的对应的子类的虚函数 总结一下就是多态调用就是在运行时去虚函数表中找虚函数的地址来进行调用所以可以达到指向父类调父类指向子类调子类虚函数。 如果去掉 virtual 则是普通调用在编译时通过调用者的类型确定函数的地址 4.3 静态绑定和动态绑定 简单来说静态就是编译时动态就是运行时 静态绑定是在编译时确定程序的行为也叫静态多态函数重载 动态绑定是在程序运行期间确定程序行为 五单继承和多继承的虚函数表 在单继承和多继承关系中我们关注的是派生类对象的虚表模型因为基类的虚表模型前面我们已经看过了没什么需要特别研究的。 5.1 单继承的虚函数表 看下面的代码 class Base { public:virtual void func1() { cout Base::func1 endl; }virtual void func2() { cout Base::func2 endl; } private:int a; }; 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 b; }; 单继承就是将基类的虚函数表拷贝下来将自己重写的虚函数覆盖。 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; }virtual void func3() { cout Derive::func3 endl; } private:int d1; };int main() {Derive d;return 0; }派生类继承了两个基类的虚表所以说有两张虚表并且同时覆盖了重写的虚函数地址如果派生类有自己的虚函数那么这个虚函数的地址放在继承的第一张虚表中。 六继承和多态的常见问题 内联函数也可以是虚函数当内联函数是普通调用时其内联属性还在当多态调用时会失去其内联属性。静态成员函数不能是虚函数因为没有this指针无法访问虚函数表。构造函数不能是虚函数因为虚表指针是在构造函数初始化列表之前初始化的