LiSheng's blog LiSheng's blog
首页
笔记
个人简历
随笔集
GitHub (opens new window)
首页
笔记
个人简历
随笔集
GitHub (opens new window)
  • golang

  • cplus

    • 内存相关

    • 面向对象

      • 相关问题
      • 构造、析构顺序
      • 对象内存布局
      • 静态、动态多态
      • 虚函数表
      • 重载、重写、覆盖
        • 一、重载(Overloading)
        • 二、重写(Overriding)
        • 三、隐藏(Hiding)
        • 总结区别
      • 构造函数不能是虚函数
      • CRTP模式的原理和用途
      • 移动语义move
      • 构造函数初始化列表
      • 静态成员变量和函数
      • 多重继承
      • 菱形问题与虚继承
      • 对象池
    • STL相关

    • 内置数据结构
    • 数据结构示例
    • go和c++对比
    • 关键字

  • leetcode

  • 存储技术

  • 分布式系统

  • 计算机网络

  • Linux操作系统

  • Redis

  • 其他

  • 笔记
  • cplus
  • 面向对象
lisheng
2024-09-10
目录

重载、重写、覆盖

在C++中,重载(Overloading)、**重写(Overriding)和隐藏(Hiding)**是三种不同的函数行为机制,它们在函数名相同的情况下,通过不同的方式实现多态性或覆盖。下面详细解释它们之间的区别:


# 一、重载(Overloading)

定义: 重载是指在同一作用域内,函数名称相同但参数列表不同(参数类型或数量不同)的多个函数共存。

特点:

  • 发生在同一作用域内。
  • 函数名相同,参数列表不同。
  • 返回类型可以相同也可以不同。
  • 通过参数列表的不同来区分调用,属于编译时多态性。

示例:

void print(int i) {}
void print(double d) {}
void print(const std::string& s) {}

1
2
3
4

# 二、重写(Overriding)

定义: 重写是指在派生类中重新定义基类中已经存在的虚函数,函数签名(名称、参数列表、返回类型)必须完全相同。

特点:

  • 发生在继承关系中。
  • 函数必须被声明为 virtual。
  • 函数签名完全相同,包括名称、参数列表和返回类型。
  • 实现运行时多态性,通过基类指针或引用调用时,会执行派生类的实现。

示例:

#include <iostream>

class Base {
public:
    virtual void display() {
        std::cout << "Base display" << std::endl;
    }
};

class Derived : public Base {
public:
    void display() override { // 使用 override 关键字可以让编译器检查是否正确重写
        std::cout << "Derived display" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived();
    ptr->display(); // 调用的是 Derived 的 display
    delete ptr;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 三、隐藏(Hiding)

定义: 隐藏是指在派生类中定义了与基类同名的成员(函数或变量),此时基类中的同名成员在派生类作用域中被隐藏。

特点:

  • 发生在继承关系中。
  • 函数名相同,但参数列表可以相同也可以不同。
  • 即使参数列表不同,基类的同名函数也会被隐藏。
  • 如果需要访问基类的被隐藏成员,可以使用作用域解析符 :: 或 using 声明。

示例1:函数隐藏

#include <iostream>

class Base {
public:
    void show() {
        std::cout << "Base show" << std::endl;
    }
};

class Derived : public Base {
public:
    void show(int x) {
        std::cout << "Derived show with int: " << x << std::endl;
    }
};

int main() {
    Derived d;
    d.show(10);       // 调用 Derived::show(int)
    // d.show();      // 错误:Base::show 被隐藏,无法直接调用
    d.Base::show();   // 正确:使用作用域解析符调用 Base::show
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

示例2:使用 using 声明解决函数隐藏

#include <iostream>

class Base {
public:
    void show() {
        std::cout << "Base show" << std::endl;
    }
};

class Derived : public Base {
public:
    using Base::show; // 引入 Base::show,使其在 Derived 中可见
    void show(int x) {
        std::cout << "Derived show with int: " << x << std::endl;
    }
};

int main() {
    Derived d;
    d.show(10);    // 调用 Derived::show(int)
    d.show();      // 调用 Base::show
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 总结区别

特性 重载(Overloading) 重写(Overriding) 隐藏(Hiding)
发生范围 同一作用域内 继承关系中 继承关系中
函数签名 函数名相同,参数列表不同,返回类型可同可不同 函数名、参数列表、返回类型完全相同,且基类函数需为 virtual 函数名相同,参数列表可同可不同
多态性 编译时多态性 运行时多态性 不涉及多态性,可能导致基类成员不可见
关键字 无需特殊关键字 基类函数需声明为 virtual,派生类中可使用 override 关键字 可使用 using 声明或作用域解析符 :: 访问被隐藏的基类成员
调用方式 通过参数列表区分调用 通过基类指针或引用调用,实际执行派生类的实现 同名的基类成员在派生类作用域中被隐藏,需显式指定访问基类成员

注意事项:

  • 使用 override 关键字可以让编译器检查函数是否正确重写,避免意外的函数隐藏。
  • 在发生函数隐藏时,可以通过 using 声明将基类的同名函数引入派生类作用域,或者使用 作用域解析符 :: 明确指定调用基类的成员。
  • 理解这三者之间的区别对于编写健壮和可维护的代码非常重要,尤其是在复杂的继承结构中。

希望以上解释能帮助你清晰地理解重载、重写和隐藏之间的区别!

编辑 (opens new window)
上次更新: 2024/09/13, 11:59:12
虚函数表
构造函数不能是虚函数

← 虚函数表 构造函数不能是虚函数→

最近更新
01
ceph分布式存储-对象存储(RGW)搭建
10-27
02
ceph分布式存储-集群客户端连接
10-27
03
ceph分布式存储-管理crushmap
10-27
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式