当前位置: 首页 > news >正文

第二十九天:重载、重写和覆盖

重载(Overloading)、重写(Overriding,也叫覆盖)

  • 重载(Overloading)、重写(Overriding,也叫覆盖)是重要的概念,它们在函数多态性和继承体系中有不同的表现和用途:

重载(Overloading)详解

  • 定义:函数重载(Function Overloading)是静态多态(编译时多态)的典型体现,它允许程序员在同一个作用域(如类作用域或命名空间)内定义多个具有相同名称但不同参数列表的函数。编译器会根据调用时传入的实际参数来决定调用哪个具体实现。具体区分标准包括:

    • 参数个数不同(如func(int)func(int, int)
    • 参数类型不同(如func(int)func(double)
    • 参数顺序不同(如func(int, double)func(double, int)

    重要注意事项:

    • 仅返回类型不同(如int func()void func())不能作为重载依据,会导致编译错误
    • 对于成员函数,const修饰可以形成重载(如void func() constvoid func()
    • 典型用途是为功能相似但需要处理不同数据类型的操作提供统一接口
  • 典型应用场景

    1. 数学运算函数

      • 实现支持多种数值类型的运算,如:
        int add(int a, int b);
        double add(double a, double b);
        
      • 可以扩展支持复数、矩阵等特殊类型的运算
    2. 构造函数重载

      • 提供多种对象初始化方式,如:
        class Person {
        public:Person();  // 默认构造Person(string name);  // 单参数构造Person(string name, int age);  // 多参数构造
        };
        
    3. 输入/输出操作

      • 处理不同类型数据的I/O,如C++的<<运算符重载:
        ostream& operator<<(ostream& os, int i);
        ostream& operator<<(ostream& os, double d);
        
      • 可扩展到自定义类型的输出格式化
    4. 工具类方法

      • 如字符串处理函数支持不同参数形式:
        String substring(int begin);
        String substring(int begin, int end);
        
    5. 图形绘制API

      • 支持多种绘制方式:
        def draw(shape: Circle):
        def draw(shape: Rectangle):
        
  • 完整示例代码

#include <iostream>
#include <string>class Printer {
public:// 重载print函数void print(int i) {std::cout << "Printing int: " << i << std::endl;}void print(double f) {std::cout << "Printing float: " << f << std::endl;}void print(const std::string& s) {std::cout << "Printing string: " << s << std::endl;}// const成员函数重载void display() const {std::cout << "Const display" << std::endl;}void display() {std::cout << "Non-const display" << std::endl;}
};int main() {Printer prt;prt.print(5);             // 调用print(int)prt.print(3.14);          // 调用print(double)prt.print("Hello");       // 调用print(string)const Printer cprt;cprt.display();           // 调用const版本prt.display();            // 调用非const版本return 0;
}

重写(Overriding,覆盖)详解

  • 详细定义:重写(Override)是面向对象编程中实现动态多态(运行时多态)的核心机制,它允许派生类重新定义基类的虚函数实现。必须严格满足以下技术条件:

    1. 继承关系:必须存在于具有明确继承关系的类体系中(如 class Derived : public Base)
    2. 虚函数声明:基类中的目标函数必须使用virtual关键字显式声明(例如:virtual void draw() const)
    3. 函数签名一致性:派生类中的重写函数必须保持与基类完全相同的:
      • 函数名称(区分大小写)
      • 参数列表(参数类型、数量和顺序)
      • 返回类型(除协变返回类型特例外)
    4. 访问权限灵活性:虽然语法允许派生类修改访问限定符(如基类protected改为public),但会破坏设计一致性,因此不推荐这种实践
  • 深入解析重要特性

    • 协变返回类型

      • 允许派生类重写函数返回基类返回类型的派生类
      • 典型应用场景:克隆模式
      class Base {
      public:virtual Base* clone() const { return new Base(*this); }
      };
      class Derived : public Base {
      public:Derived* clone() const override { return new Derived(*this); } // 协变返回
      };
      
    • final关键字(C++11引入)

      • 用法示例:
      class Base {
      public:virtual void api() final; // 禁止所有派生类重写
      };
      class Derived : public Base {// void api() override; // 编译错误
      };
      
      • 设计意义:保护关键接口不被意外修改,增强代码稳定性
    • 纯虚函数(抽象函数)

      • 语法特征:在声明后添加=0标记(virtual void pure() = 0)
      • 强制实现要求:使类成为抽象类,派生类必须实现所有纯虚函数才能实例化
      • 典型设计模式应用:作为接口类的核心实现方式(如策略模式中的策略接口)
  • 补充注意事项

    1. 与重载(overload)的本质区别:重写是运行时行为,重载是编译期行为
    2. override关键字(C++11):显式标记重写关系,增强代码可读性并防止意外隐藏(name hiding)
    3. 动态绑定的实现原理:通过虚函数表(vtable)实现运行时函数地址解析
  • 完整示例代码

#include <iostream>
#include <memory>class Shape {
public:virtual ~Shape() = default;virtual double area() const = 0;  // 纯虚函数virtual void draw() const {std::cout << "Drawing shape" << std::endl;}virtual Shape* clone() const = 0;  // 用于协变示例
};class Circle : public Shape {double radius;
public:explicit Circle(double r) : radius(r) {}// 重写纯虚函数double area() const override {return 3.14159 * radius * radius;}// 重写虚函数void draw() const override final {  // 使用final禁止后续重写std::cout << "Drawing circle with radius " << radius << std::endl;}// 协变返回类型示例Circle* clone() const override {return new Circle(*this);}
};class Rectangle : public Shape {double width, height;
public:Rectangle(double w, double h) : width(w), height(h) {}double area() const override {return width * height;}void draw() const override {std::cout << "Drawing rectangle " << width << "x" << height << std::endl;}Rectangle* clone() const override {return new Rectangle(*this);}
};int main() {// 多态调用示例std::unique_ptr<Shape> shapes[] = {std::make_unique<Circle>(5.0),std::make_unique<Rectangle>(4.0, 6.0)};for (const auto& shape : shapes) {shape->draw();std::cout << "Area: " << shape->area() << std::endl;// 克隆测试协变auto cloned = shape->clone();cloned->draw();delete cloned;}return 0;
}

重载与重写的深入对比

特性重载(Overloading)重写(Overriding)
作用域同一作用域(类内或全局)继承体系中的不同类
函数关系同名函数派生类函数与基类虚函数
参数要求必须不同必须相同
返回类型可以不同必须相同(允许协变)
virtual关键字不需要基类函数必须为virtual
多态时机编译时决定运行时决定
const修饰可以构成重载不能仅靠const构成重写
默认参数可以有不同默认值必须保持相同默认值
访问权限可以不同可以不同(但不建议)
异常规范可以不同派生类不能比基类抛出更多异常

实际开发建议

  1. 对于重载:

    • 保持重载函数的功能一致性,确保所有重载版本都实现相同的核心功能,只是参数不同。例如,一个计算面积的函数可能有多个重载版本,但都应该返回面积值。
    • 避免过度重载导致代码混淆。建议一个函数名最多不超过5个重载版本,否则应考虑使用不同的函数名或重构代码结构。
    • 在重载函数间保持参数顺序和类型的一致性,例如所有重载版本都把必选参数放在前面。
  2. 对于重写:

    • 从C++11开始,始终使用override关键字明确表示函数重写。这不仅提高代码可读性,还能让编译器检查重写是否正确。
    • 如果基类可能被继承,必须将析构函数声明为virtual,确保通过基类指针删除派生类对象时能正确调用派生类的析构函数。
    • 对于不应被进一步重写的函数,使用final修饰符。这在设计框架类时特别有用,可以锁定关键接口的实现。
    • 重写时要遵循Liskov替换原则,确保派生类对象可以替代基类对象使用而不会产生意外行为。
  3. 在大型项目中:

    • 使用静态分析工具(如Clang-Tidy、Coverity等)定期检查重写是否正确实现。这些工具可以检测出忘记override关键字、签名不匹配等问题。
    • 建立代码审查机制,特别关注重写函数的实现是否符合预期。
    • 考虑使用单元测试来验证重写行为,特别是多态调用时的表现。
    • 在文档中明确记录哪些函数可以被重写,以及重写时需要遵循的约束条件。
http://www.dtcms.com/a/354315.html

相关文章:

  • 【网络】iptables MASQUERADE作用
  • 机器学习与Backtrader结合量化交易
  • 无人机抗干扰技术要点解析
  • O2OA移动办公 × Flutter:一次开发,跨平台交付
  • 【C++】深入解析构造函数初始化
  • Docker 镜像重命名【打上新的标签】
  • AI应用图文解说--百度智能云实现语音聊天
  • Python爬虫获取1688商品列表与图片信息
  • 【免驱】一款基于AI8H2K08U芯片的USB转RS485模块,嵌入式工程师调试好帮手!
  • Web 自动化测试常用函数实战(一)
  • 如何防御安全标识符 (SID) 历史记录注入
  • 嵌入式学习day38
  • 怎样选择合适的报表系统?报表系统的主要功能有什么
  • PLC_博图系列☞基本指令”S_PULSE:分配脉冲定时器参数并启动“
  • PyTorch闪电入门:张量操作与自动微分实战
  • Wxml2Canvas在组件中的渲染获取不到元素问题
  • vue 海康视频插件
  • Java Spring Boot 集成淘宝 SDK:实现稳定可靠的商品信息查询服务
  • AI鱼塘,有你画的鱼吗?
  • 代码随想录刷题Day44
  • IDEA连接阿里云服务器中的Docker
  • 嵌入式学习日志————DMA直接存储器存取
  • 微信开发者工具中模拟调试现场扫描小程序二维码功能
  • Centos 7.6离线安装docker
  • 元宇宙+RWA:2025年虚拟资产与真实世界的金融融合实验
  • aiohttp模块如何使用
  • 开发避坑指南(39):Java List全null元素引发的空判断无效处理方案
  • 用LightRAG打造智能问答系统:知识图谱与RAG的融合应用实践
  • 如何在升级到S/4HANA后关闭SAP旧系统?
  • 3-4〔OSCP ◈ 研记〕❘ WEB应用攻击▸Burp Suite工具