C++特性之多态
来源:互联网 发布:python 企业级架构 编辑:程序博客网 时间:2024/05/18 07:46
多态:向不同的对象发送同一个消息,不同的对象在接收时产生不同的行为(方法)。
在C++中,就是:不同功能的函数可以用同一函数名,这样就可以实现用一个函数名调用不同内容的函数。
例:学校校长通知:9.1日开学。不同的对象会做出不同的反应:教师需备好课;学生需按时到校上课;家长需备好学费 。由于事先对各种人的任务已做了规定,因此,在得到同一个消息时,各种人都知道怎么做。这就是多态。
一、对象的类型
1、静态类型:对象声明时的类型,在编译时确定。
2、动态类型:目前所指对象的类型,在运行时确定。
class Base{}; class C:public Base{}; void Funtest(){Base b;C d;Base*pb1=&b;//编译器编译时就知道b的类型pb1= &d;//只有程序运行后才知道d的类型} int main(){Funtest();system("pause");return 0;}
二、多态类型
1、静态多态性(静态链编,早绑定):通过函数重载实现,要求程序在编译时就知道调用函数的全部信息。故又称为编译时多态性。
优点:静态多态性的函数调用速度快、效率高
缺点:缺乏灵活性,在程序运行前就已经决定了执行的函数和方法。
例:通过函数重载来实现:
int Add(int left, int right){return left + right;} float Add(float left, float right){return left + right;} void Funtest(){cout << Add(4, 6) << endl;//两个整数相加cout << Add(2.32f,3.45f) << endl;//两个浮点数相加}
如上代码,Add(4,6)和Add(2.32f,3.45f)被分别编译为对Add(int ,int)和Add(float,float)的调用。编译器会根据不同的参数列表对同名函数进行名字重整,之后这些同名函数就会变成拥有不同功能的函数。
运行结果如下:
带变量的宏也可以实现多态:
#define Add(a,b) (a)+(b)int main(){int x = 4;int y = 6;float e = 2.32f;float f = 3.45f;cout << Add(x, y) << endl;cout << Add(e, f) << endl;system("pause");return 0;}
当程序被编译时,表达式Add(x,y)和Add(e,f)被自动替换为两个整数相加和两个浮点数相加的具体表达式。运行结果如下:
动态多态性(动态链编,晚绑定):在程序执行期间判断所引用对象的实际类型,根据实际类型调用相应的方法。故又称为运行时的多态性。
实现动态多态的条件:
(1)、派生类必须重写基类的虚函数。
(2)、通过基类的指针/引用调基类的虚函数(该虚函数必须在派生类中被重写)
class B{public:virtual void test1(){cout << "B::test1()" << endl;}virtual void test2(){cout << "B::test2()" << endl;} void test3(){cout << "B::test3()" << endl;}/*virtual void test4(int){cout << "B::test4(int)" << endl;}*/virtual int test5(){cout << "B::test5()" << endl;}};class C :public B{public:virtual void test1(){cout << "C::test1()" << endl;} void test2()//重写虚函数,基类中的函数必须有virtual,派生类可以不加virtual{cout << "C::test2()" << endl;}virtual void test3()//没有构成重写{cout << "C::test3()" << endl;}/*virtual void test4()//重写:函数名、返回值类型、参数列表必须相同{cout << "C::test4()" << endl;}*/virtual void test5()//重写虚函数返回类型有误(协变){cout << "C::test5()" << endl;}};void Funtest(B &b){b.test1();b.test2();b.test3();//b.test4();b.test5();}
调用test4时编译器提示错误如下:
调用test5时编译器提示错误如下:
小结:
类中哪些可以作为虚函数?
(1)、普通函数 (2)、析构函数 (3)、赋值运算符重载(但最好不要)
以下均不可作为虚函数:
(1)、静态成员函数,没有this指针,只有一份
(2)、构造函数(默认内联)
(3)、友元函数,不是类的成员函数,不能被继承
继承体系同名成员函数重载、重写(隐藏)、隐藏(重定义)的区别是什么?
重载必须是在同一作用域内,函数名相同,参数和返回值类型可以不同。
int Add(int left, int right){return left + right;} float Add(float left, float right){return left + right;}void Funtest(){Add(3,5);Add(4.56f, 8.65f);}
重写不在同一作用域内(分别在基类和派生类内),函数名、参数和返回值类型必须相同(协变除外),基类函数必须有关键字virtual,访问修饰符可以不同。
class B{public:virtual void test1(){cout<< "B::test1()" << endl;}};class C :public B{public:virtual void test1(){cout << "C::test1()" << endl;}};
协变:基类的成员返回基类的指针/引用,派生类的成员返回派生类的指针/引用。
class B{virtual B& operator=(B &b)//基类的成员返回基类的引用{return *this;}};class C :public B{C& operator=(B &c)//派生类的成员返回派生类的引用{return *this;}};class B{virtual B* Funtest()//基类的成员返回基类的指针{return this;}};class C :public B{C* Funtest()//派生类的成员返回派生类的指针{return this;}};
隐藏不在同一作用域内(分别在基类和派生类内),函数名相同,与参数和返回值类型无关,在基类和派生类中只要不构成重写就是隐藏。
lass B{public: void test1(){cout<< "B::test1()" << endl;}};class C :public B{public:void test1(){cout << "C::test1()" << endl;}};
建议:
(1)、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中对象是不完整的,可能胡出现为定义的行为。
(2)、最好将基类的析构函数声明为虚函数,因为析构函数比较特殊,派生类中的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理。
虚表剖析
例如:
class B{virtual void Funtest() = 0;//纯虚函数,该类称为抽象类(接口类)};
注:抽象类不能实例化对象,只有在派生类重写以后,派生类才能实例化出对象。
虚表:虚表指针存在于对象前4个字节。
class B{public:B()//基类构造函数{}virtual void test1()//基类虚函数{cout << "B::test1()" << endl;}virtual void test2(){cout << "B::test2()" << endl;}virtual void test3(){cout << "B::test3()" << endl;}virtual void test4(){cout << "B::test4()" << endl;}private:int _data;};
求该基类大小,sizeof(B)=8;在B类成员_data之前有一个指向四个虚函数的指针,该指针即为虚表指针。
派生类中,重写基类中的test1()和test3():
class C :public B{public:virtual void test1(){cout << "C:test1()" << endl;}virtual void test3(){cout << "C:test3()" << endl;}private:int _data1;};
求派生类大小sizeof(C)=12;派生类同样有一个虚表指针,只是其中的虚函数顺序不同于基类。派生类中的虚表:首先复制基类中的虚表,确定需对基类中的哪个虚函数重写,确定后,用派生类中重写后的替换基类中的虚函数。如下图所示:
单继承
class B{public:virtual void test1()//基类虚函数{cout << "B::test1()" << endl;}virtual void test2(){cout << "B::test2()" << endl;}virtual void test3(){cout << "B::test3()" << endl;} virtual void test4(){cout << "B::test4()" << endl;} int _data1;};class C :public B{public: virtual void test3()//重写基类test3(){cout << "C:test3()" << endl;}virtual void test5()//派生类自己的虚函数{cout << "C:test5()" << endl;}int _data2;};void Funtest(){C d;d._data1 = 5;d._data2 = 4;}
基类的大小sizeof(B)=8;派生类的大小sizeof(C)=12;
多继承
class B1{public:virtual void test1()//基类B1虚函数{cout << "B::test1()" << endl;}int _data1;};class B2{public:virtual void test2()//基类B2虚函数{cout << "B::test2()" << endl;}virtual void test3(){cout << "B::test3()" << endl;}int _data2;};class C :public B1, public B2{public:virtual void test4(){cout << "B::test4()" << endl;}int _data3;};void Funtest(){C d;d._data1 = 4;d._data2 = 5;d._data3 = 6;}
基类B1、B2和派生类C的大小分别为8,8,20
菱形继承
class B{public:virtual void test1(){cout << "B::test1()" << endl;}int _data1;};class C1:public B{public:virtual void test1(){cout << "C1::test2()" << endl;}virtual void test3(){cout << "B::test3()" << endl;}int _data2;};class C2 :public B{public:virtual void test4(){cout << "B::test4()" << endl;}int _data3;};class D :public C1, public C2{public:virtual void test4(){cout << "D::test4()" << endl;}virtual void test5(){cout << "D::test5()" << endl;}int _data4;};void Funtest(){D d;d.C1::_data1 = 2;d.C2::_data1 = 6;d._data2 = 3;d._data3 = 4;d._data4 = 5;}
B类、C1类、C2类、D类的大小分别为8、12、12、28
虚继承
class B{public:B(){}~B(){}virtual void test1(){cout << "B::test1()" << endl;}int _data1;};class C :virtual public B{public:virtual void test1(){cout << "C::test1()" << endl;}virtual void test2(){cout << "C::test2()" << endl;}int _data2;};void Funtest(){C d;d._data1 = 7;d._data2 = 9;}
B类和C类的大小分别为:8,24;
去掉C类中的构造函数和析构函数其大小分别为8,20
虚继承派生类构造函数做的事:
、填写偏移量表格地址
、调用基类的构造函数
、填写派生类虚表地址
、重写属于基类对象部分的虚表地址
、派生类对象和基类对象之间用0分割(只是猜测)
菱形虚拟继承
class B{public:virtual void test1(){cout << "B::test1()" << endl;}int _data1;};class C1 :virtual public B{public:virtual void test1(){cout << "C1::test2()" << endl;}virtual void test3(){cout << "B::test3()" << endl;}int _data2;};class C2 :virtual public B{public:virtual void test4(){cout << "B::test4()" << endl;}int _data3;};class D :public C1, public C2{public:virtual void test4(){cout << "D::test4()" << endl;}virtual void test5(){cout << "D::test5()" << endl;}int _data4;};void Funtest(){D d;d.C1::_data1 = 3;d.C2::_data1 = 4;d._data2 = 5;d._data3 = 6;d._data4 = 7;}
B类、C1类、C2类和D类的大小分别为:8,20,20,36
- C++特性之多态
- C++特性之多态
- java特性之多态
- Objective-c 特性之多态、动态类型和动态绑定
- Objective-c 特性之多态、动态类型和动态绑定
- Objective-c 特性之多态、动态类型和动态绑定
- Java特性之多态详解
- 三大特性之多态
- JAVA四大特性之多态
- OOP之多态 【C#】
- objective-c之多态
- objective-c之多态
- Objective-C基础学习笔记(五)-面向对象的三大特性之多态
- 面向对象三大特性之多态
- java的三大特性之多态
- 第五天面向对象之多态特性
- 学习日记-三大特性之多态
- java的三大特性之多态
- POJ 3617 Best Cow Line 贪心
- android4.4从系统图库无法加载图片的问题
- 一起来写ftp server 02 -- 一次失败的尝试
- usaco The Tamworth Two 两只塔姆沃斯牛
- Spring Cloud构建微服务架构(三)断路器
- C++特性之多态
- memcached安装方式一(yum安装)
- UNIX环境高级编程中头文件配置方法
- IOS升级后无法真机调试各类问题汇总
- 第一天
- stm32软件模拟I2C
- matlab中遗传算法基础
- (九)ArcGIS Server之介绍SOE(上)
- android-控制ExpandableListView某一项点击不展开