一:错误理解下的多态
#include <iostream> using namespace std; class Parent { public: Parent() { cout << "Parent construct" << endl; } void overrideFunc() { cout << "Parent override func" << endl; } }; class Child:public Parent //1.有继承 { public: Child() { cout << "Child construct" << endl; } void overrideFunc() //2.有函数重写 { cout << "Child override func" << endl; } }; void main() { Parent* p = new Child(); //3.父类指针指向子类对象 p->overrideFunc(); //4.进行多态调用 system("pause"); }
这里是根据指针类型来判断调用那个函数执行(这是不对的)--->虽然也叫作多态
二:错误讨论 :使用函数来讨论到底出错在哪里?
(一)再次回顾对象的产生(重点)(对于一个含有无参构造方法的类的对象的生成)
class Test { public: Test() { cout << "Test()" << endl; } Test(int a) { cout << "Test(int a)" << endl; } void getInfo() { cout << "use getinfo" << endl; } }; void main() { Test t; //会调用无参构造函数产生一个对象,若是不存在无参构造函数,则这里编译报错 Test t2(5); //会调用有参构造函数进行产生对象 t.getInfo(); t2.getInfo(); system("pause"); }
重点:Test t3();是错误的!!!--->虽然声明不会报错,使用的时候会出错
原因://warning C4930: “Test t3(void)”: 未调用原型函数(是否是有意用变量定义的?)
会认为该语句表示声明一个名为a的函数,返回类型是Test。
(二)详细错误案例指出《重点了解》
class Parent { public: Parent() { cout << "Parent construct" << endl; } void overrideFunc() { cout << "Parent override func" << endl; } }; class Child01:public Parent { public: Child01() { cout << "Child01 construct" << endl; } void overrideFunc() { cout << "Child01 override func" << endl; } }; class Child02 :public Parent { public: Child02() { cout << "Child02 construct" << endl; } void overrideFunc() { cout << "Child02 override func" << endl; } }; void testMulStatus(Parent* p) //想通过一个父类指针实现对子类对象方法的动态调用 { p->overrideFunc(); //想实现对象方法的动态调用 } void main() { Child01 c1; Child02 c2; testMulStatus(&c1); testMulStatus(&c2); system("pause"); }
本意是通过同一个父类指针,实现对不同的子类的方法的调用
三:真正的多态实现《重点》
(一)多态原则
1.要有继承 2.要有virtual函数重写 3.要有父类指针指向子类对象
(二)代码测试
class Parent { public: Parent() { cout << "Parent construct" << endl; } virtual void overrideFunc() { cout << "Parent override func" << endl; } }; class Child01:public Parent { public: Child01() { cout << "Child01 construct" << endl; } virtual void overrideFunc() { cout << "Child01 override func" << endl; } }; class Child02 :public Parent { public: Child02() { cout << "Child02 construct" << endl; } virtual void overrideFunc() { cout << "Child02 override func" << endl; } }; void testMulStatus(Parent* p) //想通过一个父类指针实现对子类对象方法的动态调用 { p->overrideFunc(); //想实现对象方法的动态调用 } void main() { Child01 c1; Child02 c2; testMulStatus(&c1); testMulStatus(&c2); system("pause"); }
四:原理探析(虚函数表指针VPtr)
(一)多态中的虚函数表
当类中声明虚函数时,编译器就会在类中生成一个虚函数表 虚函数表是一个存储类成员虚函数指针的数据结构 虚函数表由编译器自动生成和维护 virtual成员函数会被编译器放入虚函数表中 存在虚函数时,每个类中都会有一个指向虚函数表的指针(vptr指针)
注意:虽然定义为虚函数,但是占用空间和原来成员函数大小是一样的,所以在内存上面唯一的区别在于多了一个虚函数指针(大小4字节)
(二)证明虚函数指针的存在
class Test { public: int a; int b; public: Test() { } void getInfo() { cout << "getInfo" << endl; } void setInfo() { cout << "setInfo" << endl; } }; class Test02 { public: int a; int b; public: Test02() { } virtual void getInfo() { cout << "getInfo" << endl; } virtual void setInfo() { cout << "setInfo" << endl; } }; void main() { cout << sizeof(Test) << endl; cout << sizeof(Test02) << endl; system("pause"); }
无论是一个虚函数,还是两个或者多个虚函数,最终内存大小相差4字节。所以一个成员函数--->虚成员函数是不增加空间大小的。多的那4字节是由于虚函数表指针的增加导致的
(三)多态上虚函数调用步骤
每个对象会含有一个虚函数指针vptr,指向虚函数表,当父类指针调用重写虚函数时:首先会去找vptr,根据vptr去找对应的子类虚函数表,从而找到对应对象的方法进行调用
(四)虚函数和普通函数调用的区别
1.若是普通成员函数,编译器可以直接确定被调用成员函数(静态编链) 2.若是虚函数:编译器要根据对象的vptr指针,找到虚函数表,找到对应的虚函数进行调用(动态编链) 3.所以虚函数的调用效率要低于普通成员函数,所以出于效率,没有必要全部定义为虚函数