29.面向对象关联知识点-多继承时的同名虚函数
29.1 知识点
知识回顾
- 多重继承时的同名成员问题
- 如果多个父类中存在同名成员,子类中需要通过
父类名::成员
的方式明确作用域 - 如果子类中声明了同名方法,则可以直接点出
多继承时的同名虚函数
- 多重继承时,如果多个父类中存在相同函数(函数名、参数列表完全一致),子类只需要重写一次,便可以统一覆盖所有父类版本的虚函数
#pragma once
#include <iostream>
using namespace std;
class Father
{
public:
virtual void Eat()
{
cout << "Father Eat" << endl;
}
};
#pragma once
#include <iostream>
using namespace std;
class Mother
{
public:
virtual void Eat()
{
cout << "Mother Eat" << endl;
}
};
#pragma once
#include "Father.h"
#include "Mother.h"
class Son :
public Father, public Mother
{
public:
void Eat() override
{
Father::Eat();
Mother::Eat();
}
};
Son* s = new Son();
s->Eat();
//Father Eat
//Mother Eat
s->Mother::Eat();//Mother Eat
s->Father::Eat();//Father Eat
内部原理
- 两个虚函数表中的函数指针指向的都是子类重写的函数
29.2 知识点代码
Father.h
#pragma once
#include <iostream>
using namespace std;
class Father
{
public:
virtual void Eat()
{
cout << "Father Eat" << endl;
}
};
Mother.h
#pragma once
#include <iostream>
using namespace std;
class Mother
{
public:
virtual void Eat()
{
cout << "Mother Eat" << endl;
}
};
Son.h
#pragma once
#include "Father.h"
#include "Mother.h"
class Son :
public Father, public Mother
{
public:
void Eat() override
{
Father::Eat();
Mother::Eat();
}
};
Lesson29_面向对象关联知识点_多继承时的同名虚函数.cpp
#include <iostream>
using namespace std;
#include "Son.h"
int main()
{
#pragma region 知识回顾
//多重继承时的同名成员问题
//如果多个父类中存在同名成员,子类中需要
//通过 父类名::成员 的方式 明确作用域
//如果子类中声明了同名方法,则可以直接点出
#pragma endregion
#pragma region 知识点一 多继承时的同名虚函数
//多重继承时,如果多个父类中存在相同函数(函数名、参数列表完全一致)
//子类只需要重写一次,便可以统一覆盖所有父类版本的虚函数
Son* s = new Son();
s->Eat();
//Father Eat
//Mother Eat
s->Mother::Eat();//Mother Eat
s->Father::Eat();//Father Eat
#pragma endregion
#pragma region 知识点二 内部原理
//两个虚函数表中的函数指针指向的都是子类重写的函数
#pragma endregion
}
29.3 练习题
多重继承中同名虚函数的正确说法是哪一项?
选项:
A. 如果两个基类有同名虚函数,派生类不能同时重写它们。
B. 只有通过虚继承才能正确处理同名虚函数。
C. 如果两个基类的虚函数签名相同,派生类中定义的同名虚函数可以同时覆盖它们。
D. 同名虚函数在多重继承中会引发语法错误,必须改名。
正确答案:C
解析:
选项 C 是正确的。如果两个基类中拥有签名完全一致(即函数名、参数类型、返回值等都一样)的虚函数,派生类只需定义一次同名同签名的虚函数,即可同时覆盖这两个基类中的虚函数。
- A 错误:派生类可以重写两个基类的同名虚函数,只要它们签名相同。
- B 错误:虚继承用于解决菱形继承中共享基类子对象的问题,和同名虚函数的处理无关。
- D 错误:签名相同的虚函数不会引发语法错误,也不需要强制改名。
下列代码中 Derived::show()
能否同时重写 Base1::show()
和 Base2::show()
?
class Base1 { public: virtual void show(); };
class Base2 { public: virtual void show(); };
class Derived : public Base1, public Base2 {
public:
void show() override;
};
选项:
A. 不能,必须分别写两个函数。
B. 可以,但只会覆盖第一个基类的。
C. 可以,并且同时重写两个基类的虚函数。
D. 会引发编译错误。
正确答案:C
解析:
只要 Base1
和 Base2
中的 show()
函数签名完全相同(名称、参数、const 等一致),那么 Derived
中只需写一个重写函数即可,它会同时覆盖两个基类中的虚函数。这是 C++ 多重继承中允许的语法。
派生类中如果不显式重写同名虚函数,通过基类指针调用会发生什么?
选项:
A. 编译器无法判断要调用哪个函数,会报错。
B. 会调用基类中的对应版本。
C. 会调用默认构造函数。
D. 会调用一个未定义的行为。
正确答案:B
解析:
当派生类没有重写基类的虚函数时,调用将退化为使用基类的版本。虚函数机制依旧有效,但由于没有重写,因此会向上追溯调用基类中的定义。不会报错也不会调用未定义内容。
设类 D 同时继承了 B 和 C,B 和 C 分别继承自 A,并重写了 print()
。D 没有重写。通过 B 或 C 指针指向 D 调用 print()
,结果是?
选项:
A. 会调用 D::print()
B. 会调用 A::print()
C. 会分别调用 B::print() 和 C::print()
D. 会产生二义性,无法通过编译
正确答案:C
解析:
由于 B 和 C 分别继承自 A,并分别重写了 print()
,所以当 D 继承 B 和 C 时,D 拥有两个独立的 print()
实现版本。如果通过 B 指针访问,会调用 B 的 print()
,通过 C 指针访问则调用 C 的 print()
。这种行为称为“重复继承”导致的成员函数重复。
- A 错误:D 没有实现
print()
。 - B 错误:B 和 C 已经重写了
print()
,不会退回调用 A。 - D 错误:只有调用时不明确才会有二义性,这里是通过特定指针调用,不存在二义性。
虚函数的重写是基于函数签名,而不是函数名
正确答案:正确
解析:
虚函数重写的本质是“函数签名一致”,不仅要求函数名一致,还必须满足参数列表、返回值类型、const
修饰符等全部一致。仅函数名一致并不构成重写,会被认为是函数重载。编译器只有在完全匹配签名的前提下才认为是有效的重写。使用 override
关键字可以辅助检查。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com