欢迎光临
我们一直在努力

C++继承与多态知识点及其高频面试问题

本文整理自学习笔记,涵盖继承和多态的核心知识点、代码示例及面试高频问题

一、继承

1.1 基本概念

  • 定义:派生类(子类)继承基类(父类)的成员(数据 + 函数),实现代码复用和 is‑a 关系

  • 访问控制:

    • public 继承:基类的 public → 派生类的 public,protected → protected,private 不可见

    • protected 继承:基类的 public/protected → 派生类的 protected

    • private 继承:基类的 public/protected → 派生类的 private(常用于实现“用组合代替继承”的变体)

    • 转化为类外类内可不可用的问题

  • 默认方式:class 默认 private 继承,struct 默认 public 继承

1.2 构造函数与析构函数

  • 派生类构造函数必须调用基类构造函数(显式或隐式)

  • 构造顺序:先基类 → 后派生类

  • 析构顺序:先派生类 → 后基类

  • 基类析构函数通常声明为 virtual(见多态部分)

class Base {
public:
Base(int x) : _x(x) {}
virtual ~Base() {} // 虚析构
private:
int _x;
};

class Derived : public Base {
public:
Derived(int x, int y) : Base(x), _y(y) {}
private:
int _y;
};

1.3 同名隐藏

  • 派生类中定义与基类同名的函数(不管参数列表是否相同),会隐藏基类的所有同名函数(不构成重载)

  • 若需要基类版本,使用 using Base::func; 引入

  • 也可再调用时 进行显示调用

class Base {
public:
void f(int) {}
void f(double) {}
};
class Derived : public Base {
public:
void f(int) {} // 隐藏 Base::f(double)
// using Base::f; // 解除隐藏
};

1.4 继承方式对成员的访问权限

基类成员权限public 继承protected 继承private 继承
public public protected private
protected protected protected private
private 不可访问 不可访问 不可访问

private继承一般少见 实践中很少使用

1.5 继承与组合的选择–复用

  • is‑a → 继承(例如 Dog is a Animal)

  • has‑a → 组合(例如 queue的底层实现)

1.6 面试常见考点(继承)

  • 派生类构造函数如何初始化基类成员? 在初始化列表中显式调用基类构造函数

  • 什么是同名隐藏?如何访问被隐藏的基类成员? 派生类同名函数隐藏基类所有同名函数。用 Base::func() 调用,或者再类外显示调用

  • private 继承的意义是什么? 实现“用组合代替继承”的语法糖,通常用于适配器模式(std::stack 默认继承 deque 但使用私有继承)

  • 析构函数是否必须为虚? 见多态部分

  • 二、多态

    2.1 静态多态 vs 动态多态

    • 静态多态:函数重载、模板(编译期确定)

    • 动态多态:通过虚函数和基类指针/引用,运行时确定调用哪个函数

    2.2 虚函数-Virtual Function

    • 在基类中声明为 virtual,派生类可重写(override–检查重写是否正确)

    • 通过基类指针或引用调用虚函数时,会调用实际对象类型的版本

    • 虚函数表(vtable)实现细节

    class Animal {
    public:
    virtual void speak() const { cout << "???" << endl; }
    virtual ~Animal() {}
    };
    class Dog : public Animal {
    public:
    void speak() const override { cout << "Woof!" << endl; }
    };
    void makeSound(const Animal& a) { a.speak(); }
    Dog d;
    makeSound(d); // 输出 "Woof!"

    2.3 override 与 final 说明符(C++11)

    • override:显式声明重写基类虚函数,如果签名不匹配则编译错误

    • final:禁止派生类重写该虚函数,或禁止类被继承

    class Base {
    virtual void f() final;
    virtual void g();
    };
    class Derived : public Base {
    void g() override; // OK
    // void f() override; // 错误,f 是 final
    };

    2.4 虚析构函数

    • 基类析构函数应为 virtual,否则 delete 基类指针 时不会调用派生类析构函数,导致内存泄漏

    Base* p = new Derived();
    delete p; // 若 ~Base() 非虚,则不会调用 ~Derived()

    2.5 纯虚函数与抽象类

    • 纯虚函数:virtual void func() = 0;

    • 包含纯虚函数的类称为抽象类,不能实例化

    • 派生类必须实现所有纯虚函数才能成为非抽象类

    • 设置抽象类的一个目的是强制派生类 否则无法实例化对象

    2.6 虚函数表(vtable)与内存布局(理解即可)

    • 每个多态类有一个虚函数表,对象中有一个虚指针(_vptr)指向该表

    • 派生类虚表先复制基类虚表,再替换被重写的函数指针

    2.7 面试常见考点(多态)

  • 虚函数是如何实现动态绑定的? 每个对象有一个虚指针(vptr),指向虚函数表(vtable)调用虚函数时通过 vptr 找到对应的函数地址

  • 为什么基类析构函数需要是虚的? 避免派生类资源未释放。如果基类析构非虚,delete base_ptr 只会调用基类析构,派生类部分未析构

  • 构造函数可以是虚的吗? 不能。虚函数表在构造函数执行完后才建立

  • 静态函数可以是虚的吗? 不能。虚函数依赖对象,静态函数属于类

  • override 和 final 的作用? override 让编译器检查重写是否正确;final 禁止进一步重写或继承–具体情况依照位置

  • 什么是纯虚函数?抽象类的作用? 纯虚函数强制派生类提供实现,抽象类用于定义接口,不能创建对象

  • 能否在构造函数或析构函数中调用虚函数? 可以,但不会发生多态(调用的是当前类版本的函数,因为派生类部分尚未构造或已经析构)

  • 虚函数表在内存中位于哪个区? 通常位于只读数据段(.rodata)或代码段(.rdata),虚指针位于对象内存布局中

  • 三、总结要点

    • 继承用于 is‑a 关系,注意访问控制和构造函数调用顺序

    • 多态通过虚函数实现,基类析构函数需为虚

    • 区分重载、隐藏、重写(覆盖)

    • 抽象类用于定义接口,纯虚函数强制派生类实现

    • 面试常问虚函数实现原理、虚表位置、构造函数中能否调用虚函数等

    我的gitee仓库

    https://gitee.com/jiangmingpeng0716/c-learning-process

    赞(0)
    未经允许不得转载:171主机测评 » C++继承与多态知识点及其高频面试问题
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址