目录
知识点1【多态的概述】(了解)
1、多态的分类:静态多态、动态多态
2、虚函数的引入
3、动态多态(重要)
4、虚函数的原理(了解)
知识点2【纯虚函数】(了解)
知识点3【重载、重定义(隐藏)、重写(覆盖)】(了解)
知识点4【多态案例】(了解)
知识点5【虚析构函数】(了解)
1、知识点的引入
2、虚析构(解决上述问题)
3、虚析构的原理(了解)
总结:虚函数和虚析构的区别?
虚函数:
虚析构:
知识点6【纯虚析构函数】(了解)
虚析构:
纯虚析构:
纯虚析构 函数和非纯析构函数不同点:
静态多态(静态联编、编译时多态):在编译的时候 就确定了函数的入口地址。(函数重载、运算符重载)
动态多态(动态联编、运行时多态):在运行的时候 就确定了函数的入口地址。(虚函数)
#include <iostream> using namespace std; class Animal { public: void speak(void) { cout<<"动物在唱歌"<<endl; } }; class Dog:public Animal { public: void speak(void) { cout<<"狗在汪汪"<<endl; } }; void test01() { //基类指针 指向 子类对象空间 Animal *p = new Dog; p->speak();//结果是动物在唱歌 } int main(int argc, char *argv[]) { test01(); return 0; } //动物在唱歌
1、有继承
2、基类指针 指向 子类空间
3、基类的"同名"函数必须虚函数
4、子类重写(同名、同参、同返回值类型,不同函数功能)基类的虚函数。
这样 才能使用基类指针 操作 子类的"同名函数"
#include <iostream> using namespace std; class Animal { public: //虚函数 virtual void speak(void) { cout<<"动物在唱歌"<<endl; } }; class Dog:public Animal { public: //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略) virtual void speak(void) { cout<<"狗在汪汪"<<endl; } }; void test01() { //基类指针 指向 子类对象空间 Animal *p = new Dog; //必须要用基类指针 操作子类的同名函数 p->speak(); } int main(int argc, char *argv[]) { test01(); return 0; } //狗在汪汪
#include <iostream> using namespace std; class Animal { public: //虚函数 virtual void speak(void) { cout<<"动物在唱歌"<<endl; } }; class Dog:public Animal { public: //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略) virtual void speak(void) { cout<<"狗在汪汪"<<endl; } }; class Cat:public Animal { public: //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略) virtual void speak(void) { cout<<"猫在喵喵"<<endl; } }; class Mouse:public Animal { public: //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略) virtual void speak(void) { cout<<"老鼠在吱吱"<<endl; } }; void animalSpeak(Animal &ob) { ob.speak(); } void test02() { Dog dog; Cat cat; Mouse mouse; animalSpeak(dog); animalSpeak(cat); animalSpeak(mouse); } int main(int argc, char *argv[]) { test02(); return 0; } //狗在汪汪 //猫在喵喵 //老鼠在吱吱
1、分析Animal类
class Animal { public: //虚函数 virtual void speak(void) { cout<<"动物在唱歌"<<endl; } };
当拥有虚函数的基类 不涉及到继承时,会产生一个vfptr(virtual function ptr)虚函数指针,该虚函数指针指向了一张虚函数表(vftable), vftable保存基类的同名函数入口地址。
2、分析Dog类:
当涉及到继承时:基类的虚函数指针、虚函数表 都会被继承到子类中,如果子类重写 基类的虚函数。会将虚函数表中的入口地址 更改成 子类的“同名”函数。所以这时使用基类指针调用的函数 是子类的同名函数。
拓展:
在基类中 不实现虚函数的函数体。这样的虚函数 叫纯虚函数。
class Animal { public: //纯虚函数 =0 不实现函数体 virtual void speak(void)=0; };
如果一个类中 只要有一个纯虚函数 那么这个类 是抽象类。
抽象类 不能实例化对象。
Animal ob;//err 抽象类 不能实例化对象
抽象基类 派生出子类 ,子类必须重写基类的所有纯虚函数。否则子类 也是抽象类。
#include <iostream> using namespace std; class Animal { public: //纯虚函数 =0 不实现函数体 virtual void speak(void)=0; }; class Dog:public Animal { public: //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略) virtual void speak(void) { cout<<"狗在汪汪"<<endl; } }; void test01() { //Animal ob;//err 抽象类不能实例化对象 //基类指针 指向 子类对象空间 Animal *p = new Dog; //必须要用基类指针 操作子类的同名函数 p->speak(); } int main(int argc, char *argv[]) { test01(); return 0; }
重载:没有继承、函数重载、运算符重载(静态多态)
重定义:必须有继承,子类重定义父类的非虚函数(参数类型、个数、顺序,函数的返回值类型都可以不同)。(静态多态)
重写:必须有继承,子类重写父类的虚函数(参数类型、个数、顺序,返回值类型 必须一致)(动态多态)
#include <iostream> using namespace std; //抽象制作饮品 class AbstractDrinking{ public: //烧水 virtual void Boil() = 0; //冲泡 virtual void Brew() = 0; //倒入杯中 virtual void PourInCup() = 0; //加入辅料 virtual void PutSomething() = 0; //规定流程 void MakeDrink(){ this->Boil(); Brew(); PourInCup(); PutSomething(); } }; class Coffee : public AbstractDrinking{ public: //烧水 virtual void Boil(){ cout << "煮农夫山泉!" << endl; } //冲泡 virtual void Brew(){ cout << "冲泡咖啡!" << endl; } //倒入杯中 virtual void PourInCup(){ cout << "将咖啡倒入杯中!" << endl; } //加入辅料 virtual void PutSomething(){ cout << "加入牛奶!" << endl; } }; //制作茶水 class Tea : public AbstractDrinking{ public: //烧水 virtual void Boil(){ cout << "煮自来水!" << endl; } //冲泡 virtual void Brew(){ cout << "冲泡茶叶!" << endl; } //倒入杯中 virtual void PourInCup(){ cout << "将茶水倒入杯中!" << endl; } //加入辅料 virtual void PutSomething(){ cout << "加入食盐!" << endl; } }; //业务函数 void DoBussiness(AbstractDrinking* drink){ drink->MakeDrink(); delete drink; } void test(){ DoBussiness(new Coffee); cout << "--------------" << endl; DoBussiness(new Tea); } int main(int argc, char *argv[]) { test(); return 0; }
#include <iostream> using namespace std; class Animal { public: Animal() { cout<<"Animal的无参构造"<<endl; } //纯虚函数 =0 不实现函数体 virtual void speak(void)=0; ~Animal() { cout<<"Animal析构函数"<<endl; } }; class Dog:public Animal { public: Dog() { cout<<"Dog的无参构造"<<endl; } //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略) virtual void speak(void) { cout<<"狗在汪汪"<<endl; } ~Dog() { cout<<"Dog的析构函数"<<endl; } }; void test01() { //Animal ob;//err 抽象类不能实例化对象 //基类指针 指向 子类对象空间 Animal *p = new Dog; //必须要用基类指针 操作子类的同名函数 p->speak(); delete p; } int main(int argc, char *argv[]) { test01(); return 0; }
#include <iostream> using namespace std; class Animal { public: Animal() { cout<<"Animal的无参构造"<<endl; } //纯虚函数 =0 不实现函数体 virtual void speak(void)=0; virtual ~Animal() { cout<<"Animal析构函数"<<endl; } }; class Dog:public Animal { public: Dog() { cout<<"Dog的无参构造"<<endl; } //子类的同名、同参、同返回值类型的函数 默认也是virtual (子类的virtual可以省略) virtual void speak(void) { cout<<"狗在汪汪"<<endl; } ~Dog() { cout<<"Dog的析构函数"<<endl; } }; void test01() { //Animal ob;//err 抽象类不能实例化对象 //基类指针 指向 子类对象空间 Animal *p = new Dog; //必须要用基类指针 操作子类的同名函数 p->speak(); delete p; } int main(int argc, char *argv[]) { test01(); return 0; }
Animal的类的布局:
Dog的类的布局
虚函数一般指的是虚成员函数,成员函数名前加virtual修饰,需要实现函数体,子类必须重写 父类的虚函数(函数的参数个数、顺序、类型,返回值类型必须完全一直)。 用户可以使用父类指针 操作 子类重写的虚函数。
父类的析构函数名前加virtual修饰,需要实现函数体,子类只需实现子类的析够函数即可,用户使用delete释放 父类指针 达到释放整个子类空间的效果。
class Base { public: virtual ~Base() { //函数体 } }
class Base//抽象类 { public: virtual ~Base()=0 { //必须有函数体 } }
纯虚析构函数使得基类是抽象类,不能创建基类的对象。