c++之多态
来源:互联网 发布:微商城 知乎 编辑:程序博客网 时间:2024/06/03 15:21
多态:顾名思义就是具有多种形态。是C++的三大特性之一。
静态多态:编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。
如下面代码为静态多态,根据实参类型调用函数。
#include <iostream>using namespace std;int add(int left, int right){ return left + right;}double add(double left, double right){ return left + right;}int main(){ cout << add(1, 2) << endl; cout << add(1.0, 2.0) << endl; return 0;}
动态多态:亦称动态绑定,是在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现该虚函数。编译器实现动态绑定。
如下面代码为动态多态,根据其实际类型调用函数。
#include <iostream>using namespace std;class B{public: virtual void FunTest() { cout << "B::FunTest()" << endl; }};class D :public B{public: virtual void FunTest() { cout << "D::FunTest()" << endl; }};void FunTest(B& b){ b.FunTest();}int main(){ B b; D d; FunTest(b); FunTest(d); return 0;}
【动态绑定条件】
1、必须是虚函数
2、通过基类类型的引用或者指针调用虚函数
静态多态是通过函数重载和运算符重载实现的。动态多态是通过继承和虚函数重写实现的。下面我们来看下继承体系同名函数关系。
上图说了协变除外,那什么是协变呢?
协变是指在派生类中重新实现虚函数时,返回值类型可以和基类不同,但基类和派生类中的返回值类型必须为该类型指针,仅此一项不同其余均与函数重写条件一致构成协变。
#include <iostream>using namespace std;class CBase{public: virtual CBase* GetPtr() { cout << "CBase::GetPtr()" << endl; return this; }};class CDerived :public CBase{public: virtual CDerived* GetPtr() { cout << "CDerived::GetPtr()" << endl; return this; }};void FunTest(CBase* p){ p->GetPtr();}int main(){ CBase b; CDerived d; FunTest(&b); FunTest(&d); return 0;}
纯虚函数
在成员函数的形参列表后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化对象。纯虚函数在派生类中重新定义以后,派生类才能实例化对象。
class B{public: virtual void FunTest() = 0; // 纯虚函数};// D也是一个抽象类class D :public B {public: virtual void FunTest() {}};
总结:
1、派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)。
2、基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3、只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。友元函数不属于类的成员函数,不能被定义为虚函数。
4、如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
5、构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆。
6、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为。
7、最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)。
8、虚表是所有类对象实例共用的。
#include <iostream>using namespace std;class CTest{public: CTest() { iTest = 10; cout << "this=" << this << endl; } virtual~CTest(){};private: int iTest;};int main(){ CTest test; cout << sizeof(CTest) << endl; return 0;}
对于有虚函数的类,编译器都会维护一张虚表,在里面存放各个虚函数的地址,最后以0结尾,而对象的前四个字节就是指向虚表的指针。如上图中虚表指针地址为0x001bfbf8,虚函数存放于虚表指针所指向地址0x00b5dc74中。
虚函数在多态中的调用次序:
单继承:
(1).首先调用基类中的虚函数,按在基类中声明的顺序调用。
(2).派生类先调用基类中继承的虚函数,基类中继承的虚函数调用完后,按照在派生类中声明的顺序调用派生类自己的虚函数,放在基类中继承的虚函数的后面。如果派生类重写了基类中的某个虚函数,就会替换相同偏移量位置的基类虚函数。
多继承:
(1)基类中虚函数参照单继承中基类虚函数声明的顺序调用。
(2)派生类先调用基类中继承的虚函数,基类中继承的虚函数调用完后,自己的虚函数也参照单继承中派生类虚函数声明的顺序调用。派生类中重写的虚函数替换相同偏移量位置的基类虚函数。
(3)派生类自己的虚函数放在第一个继承基类的虚表后面调用。
补充说明:
1虚表在编译时产生。
2.相同类型对象共用一张虚表。
3.若调用声明为虚函数的析构函数时,总是先调用派生类的析构函数,再调用基类的析构函数。
- OOP之多态 【C#】
- objective-c之多态
- objective-c之多态
- 【C#】面向对象之多态
- C#——面向对象之多态
- Linux C之多线程
- Objective-c 特性之多态、动态类型和动态绑定
- Objective-c 特性之多态、动态类型和动态绑定
- Objective-c 特性之多态、动态类型和动态绑定
- C+学习之多态篇(虚函数)
- linux c sockset之多播
- linux c sockset之多播
- java之多态,
- 面向对象之多态
- 面向对象之多态
- c++规范之多态
- C++学习之多态
- 面向对象之多态
- MySQL函数大全及用法示例
- 65. Valid Number
- 解析Json对象数组
- C# 中的委托和事件(详解)
- 编码问题导致python脚本运行时出错: 没有那个文件或目录
- c++之多态
- 2641 2016东莞市特长生考试 游戏问题
- find命令
- Linux面试题汇总
- zTree取消某个节点的选中状态
- ActiveMQ入门实例
- Bootstrap和JQuery.validate表单校验
- tomcat 7无法进入管理页面
- 读取数量不定的输入数据