多态和多态模型
来源:互联网 发布:列举常用的查找算法 编辑:程序博客网 时间:2024/06/03 09:40
1:什么是多态?
1.1 概念回顾
虚函数:类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数重写:当在子类中定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
多态:当使用基类的指针或引用调用重写的虚函数时,当指向父类调用父类的虚函数,当指向子类调用子类的虚函数。
注:
1)派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2)基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3)只有类的成员函数才能定义为虚函数。
4)静态成员函数不能定义为虚函数。
5)如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
6)构造函数不能为虚函数,虽然可以将operator=定义为虚函数,但是最后不要将operator=定义为虚函数,因为容易使用时引起混肴。
7)不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
8)最好不要把基类的析构函数声明为虚函数。(因为,析构函数比较特殊,派生类的析构函数与基类的析构函数名称不一样,但由于编译器的特殊处理会把析构函数的名称都翻译为deconstruct,就构成覆盖。)
c++中虚函数的主要作用就是去实现多态。即父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象调用父类的虚函数,指向子类调用子类的虚函数。
1.2探索虚函数表
虚函数表是通过一块连续内存来存储函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数函数。
class Base{ public: virtual void func1() {} virtual void func2() {} private: int a; };void Test1(){ Base b1; cout<<sizeof(b1)<<endl;}
分析如下:
由上图可知,Base类的大小由成员变量b1的内存大小和虚表指针所占内存大小决定,故而在32位程序中,b1占8个字节,在64位程序中,b1占12个字节。
1.3 多态的分类——动/静态多态
多态是多种形态,C++的多态分为静态多态和动态多态。
1)静态多态就是重载,因为在编译期间决议确定的,所以称为静态多态。
2)动态多态就是通过继承重写基类的虚函数实现的多态,因为是在运行时决议确定的,所以称为动态多态。
#include<iostream>using namespace std;class Base{public: virtual void func1() { cout << "Base::func1" << endl; } virtual void func2() { cout << "Base::func2" << endl; } void func3() { cout << "Base::func3" << endl; } void func3(int n) { cout <<n<<"->" "Base::func3" << endl; }private: int a;};class Derive :public Base{public: virtual void func1() { cout << "Derive::func1" << endl; }private: int b;};void Test(Base& p){ p.func1(); p.func3(); p.func3(100);}int main(){ Base b; Derive d; Test(b); Test(d); system("pause"); return 0;}
注意:
基类指针或引用+虚函数=动态联编
动态联编+虚函数重写=多态
动态联编效率不及静态联编,动态联编指令多。
例题:
Base* p=NULL;p->func1();//动态联编,要根据p指向的地址寻找虚表,对p解引用了,崩p->func3();//静态联编,没有对p解引用p->a=100;//对p解引用了,崩
2:多态的对象模型–单继承&多继承?
2.1 单继承:
//单继承多态对象模型#include<iostream>using namespace std;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;};typedef void(*FUNC)();void PrintVTable(int** VTable){ cout << "====================================" << endl; cout << "虚表地址>" << VTable << endl; for (size_t i = 0; VTable[i] != 0; ++i) { printf("Vfunc[%d]:%p->", i, VTable[i]); FUNC f = (FUNC)VTable[i]; f(); } cout << "====================================" << endl;}void Test1(){ Base b1; Derive d1; int** VTable1 = (int**)(*(int**)&b1); int** VTable2 = (int**)(*(int**)&d1); PrintVTable(VTable1); PrintVTable(VTable2);}int main(){ Test1(); system("pause"); return 0;}
运行结果如下:
分析如下图:
2.2 多继承:
//多继承多态对象模型#include<iostream>using namespace std;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 d;};typedef void(*FUNC)();void PrintVTable(int** VTable){ cout << "====================================" << endl; cout << "虚表地址>" << VTable << endl; for (size_t i = 0; VTable[i] != 0; ++i) { printf("Vfunc[%d]:%p->", i, VTable[i]); FUNC f = (FUNC)VTable[i]; f(); } cout << "====================================" << endl;}void Test1(){ Derive d1; int** VTable2 = (int**)(*(int**)&d1); PrintVTable(VTable2); //Base2虚表在对象Base1后 VTable2 = (int**)(*((int**)&d1+sizeof(Base1)/4)); PrintVTable(VTable2);}int main(){ Test1(); system("pause"); return 0;}
运行结果如下:
分析图如下:
3:多态的对象模型–菱形继承和菱形虚拟继承?(选做)
3.1 菱形继承
#include<iostream>using namespace std;class Base{public: virtual void func1() { cout << "Base::func1" << endl; } virtual void func2() { cout << "Base::func2" << endl; }private: int b;};class Base1:public Base{public: virtual void func1() { cout << "Base1::func1" << endl; } virtual void func3() { cout << "Base1::func3" << endl; } virtual void func4() { cout << "Base1::func4" << endl; }private: int b1;};class Base2 :public Base{public: virtual void func1() { cout << "Base2::func1" << endl; } virtual void func5() { cout << "Base2::func5" << endl; } virtual void func6() { cout << "Base2::func6" << 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; } virtual void func5() { cout << "Derive::func5" << endl; } virtual void func7() { cout << "Derive::func7" << endl; }private: int d;};typedef void(*FUNC)();void PrintVTable(int** VTable){ cout << "====================================" << endl; cout << "虚表地址>" << VTable << endl; for (size_t i = 0; VTable[i] != 0; ++i) { printf("Vfunc[%d]:%p->", i, VTable[i]); FUNC f = (FUNC)VTable[i]; f(); } cout << "====================================" << endl;}void Test1(){ Derive d1; int** VTable2 = (int**)(*(int**)&d1); PrintVTable(VTable2); //Base2虚表在对象Base1后 VTable2 = (int**)(*((int**)&d1 + sizeof(Base1) / 4)); PrintVTable(VTable2);}int main(){ Test1(); system("pause"); return 0;}
分析图如下:
我们可以看见,最顶端的父类Base其成员变量b存在于Base1和Base2中,并被Derive给继承下去了。而在Derive中,其有Base1和Base2的实例,于是Base的成员在Derive的实例中存在两份,一份是Base1继承而来的,另一份是Base2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:
Derive d;d.b = 0; //二义性错误d.Base1::d = 1; //正确d.Base2::d = 2; //正确
注意,上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误,但Base类在Derive中还是有两个实例,这种继承造成了数据的重复,我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++引入了虚基类的概念。
3.2 菱形虚拟继承
上述的“菱形继承”只需要把Base1和Base2继承Base的语法中加上virtual 关键,就成了菱形虚拟继承。结构如上图,其省略后的源码如下所示:
class Base{……};class Base1 : virtual public Base1{……};class Base2: virtual public Base2{……};class D : public B1, public B2{ …… };
Base是基类,里有虚函数,故而一定有虚表;Base1和Base2当有虚函数时才有虚表,按继承顺序放置,当Base1和Base2只有一个有虚函数,则有虚函数的在前。
菱形虚继承打印虚表的运行结果如下:
菱形虚继承的虚基表如下所示:
菱形继承和菱形虚继承的虚基表区别如下图:
采用虚继承解决了菱形继承的冗余和二义性问题。
- 多态和多态对象模型
- 多态和多态模型
- C++对象模型和多态
- python中的多态和鸭子模型
- 多态中的模型
- 多态对象模型
- C++中的多态和多态对象模型
- c++中的多态和多态对象模型
- 多态&多态对象模型
- 多态&多态对象模型
- 多态及其对象模型
- 多态&多态对象模型
- 多态的对象模型
- 多态&多态对象模型
- 多态&多态对象模型
- 多态&多态对象模型
- C++多态&多态模型
- 多态&多态对象模型
- 组件化app第1课
- OpenSSL中的SSL_connect函数究竟做了哪些事?
- 链表基础1
- 剑指offer-定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
- 索引
- 多态和多态模型
- 使用VS调试Dump文件
- Paint调用setAlpha值没有看到透明效果
- 【面向JS--DOM 递归API】
- hdu1162 Eddy's picture(Prim算法模板)
- idea使用笔记
- 数据清洗
- 快速幂板子
- mysql乐观锁总结