1.虚函数重写的三个例外
1.1协变(基类与派生类虚函数返回值类型不同)
①🍎协变的概念:
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
1.2析构函数的重写(基类与派生类析构函数的名字不同)
①🍎注意:如果派生类 new 出来了一个对象,申请了一段内存,必须重写子类的析构函数,并将基类的析构函数定义成为虚函数,不然子类申请的空间无法释放掉(内存泄露)。
子类的对象模型一部分是自己的,一部分是从父类继承的!所以析构的时候,“先子后父”(先把自己的释放再释放从父类继承的)❗
②🍎虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor
。
Person* p1 = new Person;
Person* p2 = new Student;
p1->destructor + operator delete(p1)
p2->destructor + operator delete(p2)
多态
问题❗:p1 和 p2 是什么类型,析构就调用什么类型(所以如果基类的析构函数不是虚函数的话,无法正确释放掉子类申请的空间,造成内存泄露)
期望:指向父类调用父类析构
期望:指向子类调用子类析构
结论:建议析构函数定义为虚函数,防止发生内存泄漏
1.3派生类可以不写 virtual
①🍎重写只需要重写派生类的实现方式就好了(意思就是子类的函数名、函数参数、返回值类型用的是父类的),只重写函数体内部的实现!子类已经在语法上面继承下来了,所以重写实现就可以了。
2.面试题✍
做这道题目之前首先要弄清楚下面几个问题👇
①🍎两个 func() 构成虚函数重写吗?
构成虚函数的重写,子类的 virtual 将其省掉了。
②🍎test() 函数内部的 this 指针是指向 A* 还是 B*?
B*p = new B;
p指向的是派生类,先在子类中查找有没有 test()函数,没有的话 ,就去父类查找,如果父类也没有的话,就会报错,所以派生类不是拷贝下来。
③🍎 虚函数重写,重写的是函数体的实现,函数体的结构部分(如函数名,函数参数,函数返回值)用的是父类的。
答案:B