【C++】多态 - 日理万妓 2024-04-26 11:01 52阅读 0赞 ![在这里插入图片描述][6ac7346dc5944908b5dd227e55fab217.gif_pic_center] > ?write in front? > ?所属专栏: [C++学习][C] > ?️博客主页:[睿睿的博客主页][Link 1] > ?️代码仓库:?[VS2022\_C语言仓库][VS2022_C] > ?您的点赞、关注、收藏、评论,是对我最大的激励和支持!!! > **关注我,关注我,关注我**,你们将会看到更多的优质内容!! ![在这里插入图片描述][85d8f838a7524c8bb8bed80b7ac059c1.gif_pic_center] #### 文章目录 #### * 一.多态的概念: * 二.多态的定义及实现: * * 1.多态的构成条件: * * 1.1虚函数: * 1.2重写: * 1.3指针调用或引用调用: * 2.多态的一些特殊情况: * * 2.1.重新虚函数virtual: * 2.2.协变: * 2.3.析构函数的重写: * 3.C++11 override 和 final: * * 3.1override: * 3.2.final: * 设计一个不想被继承的类,如何设计: * * 1.基类构造函数(或者析构函数)私有 (C++98) * 2.在基类加个final (C++11) * 4. 重载、覆盖(重写)、隐藏(重定义)的对比 * 三.多态的原理: * * 1.虚函数表 * 2.多态原理: * 动态绑定与静态绑定: * 五.单继承和多继承关系的虚函数表 * * 1.单继承: * 2.多继承: * 总结: ## 一.多态的概念: ## 通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。比如下面这两个例子,不同对象去完成同一件事情,结果是不一样的: ![在这里插入图片描述][97d7101f6fc749f0b5e6af82dda4e284.png] ## 二.多态的定义及实现: ## ### 1.多态的构成条件: ### 在继承中要构成多态还有两个条件: 1. 被调用的函数必须是**虚函数**,且派生类必须对基类的虚函数进行**重写** 2. 必须通过**基类的指针**或者**引用调用**虚函数 #### 1.1虚函数: #### 先来说第一个,虚函数的构成就是在一个**类成员函数**(必须是一个类的成员函数才可以,普通函数加了会报错)前面加`virtual`即可,但是这个`virtual`和**虚拟继承的virtual**是没有关系的,一定不要搞混。 class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl;} }; #### 1.2重写: #### 派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。 这里一定一定不要和继承的隐藏搞混!!! 举个例子: class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl; } }; class Student : public Person { public: virtual void BuyTicket() { cout << "买票-半价" << endl; } 这种情况下,`Student`的`BuyTicket`函数就对`Person`的`BuyTicket`函数进行了重写。 #### 1.3指针调用或引用调用: #### 要形成多态,就只能通过指针或引用来调用,不然和之前的普通调用没什么区别: 多态调用: class Person { public: virtual void BuyTicket() const { cout << "买票-全价" << endl; return 0; } }; class Student : public Person { public: virtual void BuyTicket() const { cout << "买票-半价" << endl; return 0; } }; void func(const Person& p) { p.BuyTicket(); } int main() { func(Person()); func(Student()); } **结果:** ![在这里插入图片描述][c0c6a3e5fdd544128c2b51d1a7be181d.png] 普通调用(非指针引用): class Person { public: virtual void BuyTicket() const { cout << "买票-全价" << endl; return 0; } }; class Student : public Person { public: virtual void BuyTicket() const { cout << "买票-半价" << endl; return 0; } }; void func(Person p) { p.BuyTicket(); } int main() { func(Person()); func(Student()); } **结果:** ![在这里插入图片描述][76d7c4894d2146aaa370f30a2aaae140.png] 在这里我们就会发现,多态调用时,不同的对象传过去,调用不同的函数。 * 多态调用函数看的是**指向的的对象** * 普通调用函数看到是**当前类型**!! ### 2.多态的一些特殊情况: ### 我们知道,虚函数的重写的要求就是虚函数+三同,但是下面的情况有点特殊: #### 2.1.重新虚函数virtual: #### 派生类的重写虚函数可以不加`virtual`(最好加上) #### 2.2.协变: #### 协变,返回的值可以不同,但是要求返回值必须是父子关系指针和引用,并且父类只能返回父类指针,子类只能返回子类指针,并且必须同时返回指针或同时返回引用: class A{ }; class B: public A { }; class Person { public: virtual A* BuyTicket() const { cout << "买票-全价" << endl; return 0; } }; class Student : public Person { public: virtual B* BuyTicket() const { cout << "买票-半价" << endl; return 0; } }; void func(Person p) { p.BuyTicket(); } int main() { func(Person()); func(Student()); } #### 2.3.析构函数的重写: #### 编译器对**析构函数**的名称做了特殊处理,编译后析构函数的名称统一处理成`destructor`。 但是为什么要这样处理呢? 其实就是为了让析构函数构成重写。 但是为什么要对其进行重写呢? 我们来看看下面这个例子就懂了: class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl; } ~Person() { cout << "~Person()" << endl; } }; class Student : public Person { public: virtual void BuyTicket() { cout << "买票-半价" << endl; } ~Student() { cout << "~Student()" << endl; delete[] ptr; } protected: int* ptr = new int[10]; }; int main() { Person* a = new Person; delete a; Person* b = new Student; delete b; } 运行这段代码的结果如下: ![在这里插入图片描述][51f255c7c19547d2a4f32bedcf02dfb9.png] 很显然,这里造成了内存泄漏,因为我们堆里面的`Student`对象没有清除掉。我们来看看重写析构函数后的样子: class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl; } virtual ~Person() { cout << "~Person()" << endl; } }; class Student : public Person { public: virtual void BuyTicket() { cout << "买票-半价" << endl; } virtual ~Student() { cout << "~Student()" << endl; delete[] ptr; } protected: int* ptr = new int[10]; }; int main() { Person* a = new Person; delete a; Person* b = new Student; delete b; } 运行结果: ![在这里插入图片描述][c2c367af94944bd3a93e15c901977f70.png] 这就是对析构函数重写的意义所在,当我们希望对一个对象进行多态调用,在有动态内存管理的情况下调用析构函数,如果不重写,就会造成内存泄漏。 ### 3.C++11 override 和 final: ### 在C++11标准中引入的 override 和 final 关键字,用于增强面向对象编程的语法和语义,帮助开发者在继承和多态性方面更加清晰地表达意图和管理代码。它们分别用于指示虚函数的重写和禁止派生类进一步继承或函数的重写 #### 3.1override: #### 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错: class Car{ public: virtual void Drive(){ } }; class Benz :public Car { public: virtual void Drive() override { cout << "Benz-舒适" << endl;} }; #### 3.2.final: #### 修饰虚函数,表示该虚函数不能再被重写 class Car { public: virtual void Drive() final { } }; class Benz :public Car { public: virtual void Drive() { cout << "Benz-舒适" << endl;} }; #### 设计一个不想被继承的类,如何设计: #### ##### 1.基类构造函数(或者析构函数)私有 (C++98) ##### 在C++98里面,我们将构造函数或析构函数放在`private`成员里面,在继承时没有对`private`的访问权,就无法调用父类的构造函数,就无法继承: class A { A() { } public: static A CreateObj() { return A(); } }; class B:public A { }; int main() { //B bb; //想创建A对象如何创建? A a = A::CreateObj(); } ##### 2.在基类加个final (C++11) ##### 在基类后面直接加final就无法继承了: class A final { }; class B:public A { }; ![在这里插入图片描述][62f896adc37944289c9a72bb097b8e9c.png] ### 4. 重载、覆盖(重写)、隐藏(重定义)的对比 ### ![在这里插入图片描述][95211f96e2ba46629000c9b718452b6c.png] ## 三.多态的原理: ## ### 1.虚函数表 ### 我们先来看看一道经典面试题:sizeof(Base)是多少? class Base { public: virtual void Func1() { cout << "Func1()" << endl; } private: int _b = 1; }; ![在这里插入图片描述][00a5d17bf41c4f15b87067537b9e2f78.png] 通过观察测试我们发现b对象是8bytes,除了`_b`成员,还多一个`__vfptr`放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表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 _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; } 通过观察和测试,我们发现了以下几点问题: 1. **派生类对象d中也有一个虚表指针**,d对象由两部分构成,一部分是**父类继承**下来的成员,虚表指针也就是存在部分的另一部分是**自己的**成员。 2. 基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,**覆盖是原理层的叫法**。 3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是**不是虚函数**,所以**不会放进虚表**。 4. 虚函数表本质是一个存虚函数指针的**指针数组**,**一般情况这个数组最后面放了一个`nullptr`**。 5. 总结一下派生类的虚表生成: a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己**新增加的虚函数**按其在派生类中的**声明次序**增加到派生类虚表的**最后** 但是,虚函数表是存在哪里的呢?我们知道,虚函数存在代码段,通过下面这段代码可以验证,虚函数表存在**常量区**: ![在这里插入图片描述][895c74338561469ea24e760d3c844e44.png] ![在这里插入图片描述][2e02e513de544d52a3f03d9ca21e954a.png] ### 2.多态原理: ### 说了这么久,多态形成的原理到底是什么呢?我们以下面代码为例子: class Person { public: virtual void BuyTicket() { cout << "买票-全价" << endl; } int _a = 1; }; class Student : public Person { public: virtual void BuyTicket() { cout << "买票-半价" << endl; } int _b = 1; }; void Func(Person& p) { // 符合多态,运行时到指向对象的虚函数表中找调用函数的地址 p.BuyTicket(); } int main() { Person Mike; Func(Mike); Student Johnson; Func(Johnson); return 0; } 通过监视窗口可以看到,他们两个有不同的虚函数表指针,这是很关键的一点。 ![在这里插入图片描述][3f7fb5f4f51e4593894911173c087e37.png] 我们知道,构成多态有两个条件: 1. 必须有虚函数,并且重新了父类虚函数 2. 父类指针或引用调用虚函数。 其实**第一个条件**,是为了让这个类里面的虚函数和普通函数不一样,**虚函数**会产生**虚表指针**,要调用虚函数时是通过这个虚表指针来找到虚表,在通过虚表调用虚函数;而普通函数不会,普通函数只是普通调用代码段里面的函数。而这个虚表指针就非常的重要,他就可以帮助我们形成多态。那么怎么帮助呢?**第二个条件**的价值就来了,我们通过父类的指针或引用指向子类,这个时候指针或引用要访问子类虚函数的时候,还是**通过这个虚表指针**来**访问那个虚函数**,这样才形成了多态。 说到这我们就不得不在说一下**普通函数**和**虚函数**调用时候的区别了: * 不符合多态的(普通函数):**编译时期**就已经从符号表确认了函数的地址,直接call 地址。(这个地址也正好在虚表里面,至于为什么可以直接知道,我也不清楚) * 符合多态的 : **运行时期**才到指向对象的虚函数表中找调用函数的地址,call的是`eax`,而`eax`存的就是虚函数表指针,所以多态调用不是在编译时确定。 ![在这里插入图片描述][cdf4a118495945cfb3d3f5a9f5e1e621.png] 那么为什么必须是父类指针或引用呢? 子类赋值给父类对象切片,不会拷贝虚表指针。如果拷贝了虚表指针,那么父类对象虚表指针是子类虚表指针,一个父类对象调用子类的函数,就非常的奇怪。 ### 动态绑定与静态绑定: ### 1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为**静态(编译时)的多态**,比如:**函数重载** 2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态(运行时)的多态,比如**继承的函数重写实现的多态**。 其实上面的多态函数的调用和普通调用就很好的说明这一点。 ## 五.单继承和多继承关系的虚函数表 ## ### 1.单继承: ### class Person { public: virtual void Func1() { cout << "Person::Func1()" << endl; } virtual void Func2() { cout << "Person::Func2()" << endl; } //protected: int _a = 0; }; class Student : public Person { public: virtual void Func3() { //_b++; cout << "Student::Func3()" << endl; } protected: int _b = 1; }; 观察下图中的监视窗口中我们发现看不见func3。这里是编译器的监视窗口故意隐藏了这两个函数,也可以认为是他的一个小bug。![在这里插入图片描述][c755f12156d64219b12e0c6e66955a59.png] 那么我们如何查看d的虚表呢?下面我们使用代码打印出虚表中的函数 typedef void(*Func_PTR)(); class Person { public: virtual void Func1() { cout << "Person::Func1()" << endl; } virtual void Func2() { cout << "Person::Func2()" << endl; } //protected: int _a = 0; }; class Student : public Person { public: virtual void Func3() { //_b++; cout << "Student::Func3()" << endl; } protected: int _b = 1; }; void PrintVFT(Func_PTR* table) { for (int i = 0; table[i]; i++) { printf("[%d]->%p",i, table[i]); Func_PTR p = table[i]; p(); } printf("\n"); } int main() { Student ss; int ptr1 = *((int*)&ss); PrintVFT((Func_PTR*)ptr1); Person pp; int ptr2 = *((int*)&pp); PrintVFT((Func_PTR*)ptr2); } 取出`Person`对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面放了一个nullptr 1. 先取对象的地址,强转成一个int\*的指针 2. 再解引用取值,就取到了对象头4bytes的值,这个值就是指向虚表的指针 3. 再强转成VFPTR\*,因为虚表就是一个存VFPTR类型(虚函数指针类型)的数组。 4. 虚表指针传递给PrintVTable进行打印虚表 5. 需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最后面没有放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的-生成-清理解决方案,再编译就好了 **结果:** ![结果][dbc2be7728fc4fa1a9263ce003b31639.png] ### 2.多继承: ### 那么同样的问题在多继承里面会发生什么呢? typedef void(*Func_PTR)(); 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; }; void PrintVFT(Func_PTR* table) { for (int i = 0; table[i]; i++) { printf("[%d]->%p",i, table[i]); Func_PTR p = table[i]; p(); } printf("\n"); } int main() { Derive dd; int ptr1 = *((int*)&dd); //int ptr2= *((int*)(char*)&dd+sizeof(Base1)); Base2* pp = ⅆ int ptr2= *((int*)pp); PrintVFT((Func_PTR* )ptr1); PrintVFT((Func_PTR* )ptr2); } ![在这里插入图片描述][cad57b68b4154a79982114268b34fcb6.png] 我们会发现,派生类继承两个类,并且这两个类都有多态,那么他就会有**两个虚函数表指针**,如果在派生类里面多加一个虚函数,会加在第一个虚函数表里面。 并且我们会发现,对于两个父类在子类中同时构成多态的函数,子类可以重写,但是在重写之后会发现,这两个虚函数表里面重写的这个函数在代码段里面好像地址不统一? 其实原因很简单,我们通过以下代码来解释: ![在这里插入图片描述][e99376fbb8af46428025896602f35d4c.png] 当用父类Base2指针来调用虚函数形成多态的时候,此时调用的是Derive的函数,要访问Derive的所有成员,就必须用Derive的`this`指针,而此时如果直接通过`Base2`指针调用`func1`,`this`指针传的就是`Base2`类型的,所以要将其地址指向`this`指针。这就是其指向其他位置的原因,为了修正其位置,看汇编就会知道,最后调用的函数地址和Base1一样。而`Base1`就不用,因为`this`指针指向的和`Base1`指向的是同一个位置。 这里我们就要知道,**内存里面不分类型,只分数据。** ## 总结: ## 更新不易,辛苦各位小伙伴们动动小手,?三连走一走?? ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正! > **专栏订阅:** > [每日一题][Link 2] > [C语言学习][C 1] > [算法][Link 3] > [智力题][Link 4] > [初阶数据结构][Link 5] > [Linux学习][Linux] > [C++学习][C] > 更新不易,辛苦各位小伙伴们动动小手,?三连走一走?? ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正! ![在这里插入图片描述][55270e2552774a17a711aa978da84193.gif_pic_center] [6ac7346dc5944908b5dd227e55fab217.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/d19654d2599143c394476c67d0aa055a.gif [C]: https://blog.csdn.net/qq_74310471/category_12289978.html [Link 1]: https://blog.csdn.net/qq_74310471?spm=1000.2115.3001.5343 [VS2022_C]: https://gitee.com/zhangxuruihhhh/c-language-learning [85d8f838a7524c8bb8bed80b7ac059c1.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/ac2af61fe4d64b9d97a06a020051c845.gif [97d7101f6fc749f0b5e6af82dda4e284.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/669d00ad7a314a4ca6283fc6560ae774.png [c0c6a3e5fdd544128c2b51d1a7be181d.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/4f09ed84ba6547a1a3ae801be4784c1f.png [76d7c4894d2146aaa370f30a2aaae140.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/4d37e6d8ece74f4e89fff3b437040270.png [51f255c7c19547d2a4f32bedcf02dfb9.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/63619c59cd3040348e424b606b4b9dcf.png [c2c367af94944bd3a93e15c901977f70.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/78bc47d6f0a24b7689ce583e14146332.png [62f896adc37944289c9a72bb097b8e9c.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/ec25bb098b9643d4bce4e6e0b9bdca61.png [95211f96e2ba46629000c9b718452b6c.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/40c1fc37d7e34371ab510c0caf085d21.png [00a5d17bf41c4f15b87067537b9e2f78.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/50af1d1a06ac42638fc9b01ad9005775.png [895c74338561469ea24e760d3c844e44.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/39cdd927cbf147f78f3a5031d59c42fd.png [2e02e513de544d52a3f03d9ca21e954a.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/3521d09d9a904f4aac1e0bf754f30d95.png [3f7fb5f4f51e4593894911173c087e37.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/3bcc23fd75b24ca1af042072df7405a8.png [cdf4a118495945cfb3d3f5a9f5e1e621.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/16aa2b29b4de4c8ba3346725bc3f408b.png [c755f12156d64219b12e0c6e66955a59.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/abaa15ff51fa4c34899f1a14f8234dfd.png [dbc2be7728fc4fa1a9263ce003b31639.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/b88dad9ba5774c8391eba53440e67089.png [cad57b68b4154a79982114268b34fcb6.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/e16ca3d99d0449838a551ebd8bf152a6.png [e99376fbb8af46428025896602f35d4c.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/264bbae2bb4b46569527239f63933ce3.png [Link 2]: https://blog.csdn.net/qq_74310471/category_12166401.html?spm=1001.2014.3001.5482 [C 1]: https://blog.csdn.net/qq_74310471/category_12157207.html?spm=1001.2014.3001.5482 [Link 3]: https://blog.csdn.net/qq_74310471/category_12144452.html?spm=1001.2014.3001.5482 [Link 4]: https://blog.csdn.net/qq_74310471/category_12166402.html [Link 5]: https://blog.csdn.net/qq_74310471/category_12216969.html?spm=1001.2014.3001.5482 [Linux]: https://blog.csdn.net/qq_74310471/category_12291120.html [55270e2552774a17a711aa978da84193.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/30a8a2761f004ae99f03fe7917b459bc.gif
相关 c++多态 class Parent \{ public: virtual void fun() \{ cout << "Parent" << endl; \} 怼烎@/ 2024年02月17日 20:52/ 0 赞/ 118 阅读
相关 C++ 多态 C++ 多态 一、多态性 1、多态性的定义 2、多态性的分类及实现方式 3、动态多态的作用 4、虚函数的注意事 拼搏现实的明天。/ 2023年07月24日 08:03/ 0 赞/ 72 阅读
相关 C++多态 [浅谈C++多态性][C] [虚函数和纯虚函数的区别][Link 1] [虚函数][Link 2] [C++虚函 我就是我/ 2022年09月24日 12:25/ 0 赞/ 281 阅读
相关 【C#】 多态 多态是面向对象编程中三大机制之一,其原理建立在"从父类继承而来的子类可以转换为其父类"这个规则之上,换句话说,能用父类的地方,就能用该类的子类.当从父类派生了很多子 深碍√TFBOYSˉ_/ 2022年08月18日 15:24/ 0 赞/ 239 阅读
相关 C++多态 多态意思是”多种形态“。多态性是面向对象编程的关键思想。 C++通过以下方式支持多态 (1)经由一组隐式的转化操作。例如把一个派生类的指针转化为一个指向公共基类的指针 £神魔★判官ぃ/ 2022年08月06日 01:17/ 0 赞/ 286 阅读
相关 【c++】多态 多态 多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。 C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执 男娘i/ 2022年07月11日 13:13/ 0 赞/ 267 阅读
相关 C#(多态) 1: 在C\语言中,重载和重写的区别 (1)重写: 重写是子类的方法覆盖父类的方法,要求方法名和参数都相同 (2)重载:重载是在同一个类中的 偏执的太偏执、/ 2021年12月11日 03:11/ 0 赞/ 318 阅读
相关 C++多态 C++多态 多态概念 字面意思,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。 举个例子:假如你要去看电影,有的影院也许有这样的规定 我不是女神ヾ/ 2021年11月27日 06:52/ 0 赞/ 393 阅读
还没有评论,来说两句吧...