29.多继承时的同名虚函数

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

解析:

只要 Base1Base2 中的 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

×

喜欢就点赞,疼爱就打赏