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

C++代码优化(三): 决不要重新定义继承而来的缺省参数值

目录

1.为什么?

2.示例说明

3.替代方案

4.总结


1.为什么?

        在C++中,当你从一个基类继承一个带有默认参数的函数时,如果在派生类中重新定义这个函数并且改变了默认参数值,这可能会导致代码难以理解和维护。这是因为派生类中的函数签名和基类中的函数签名在默认参数值上不一致,这可能会引起混淆和潜在的错误。在 C++ 代码优化中,一个常见的建议是不要在子类中重新定义继承而来的默认参数值

        原因有以下几点:

1) 默认参数值是静态绑定的:默认参数值在编译时被确定,并且与函数调用的静态类型相关联,而不是动态类型。这意味着即使在运行时使用子类对象,默认参数的值仍然是基类中的值,而不会使用子类的默认参数值。这个行为可能会引发意外的结果。

2) 可能造成混乱:重定义默认参数值看起来似乎提供了灵活性,但实际上,由于它的行为与虚函数的多态性不一致,可能会引起误解和混淆。函数的多态性只体现在函数体的动态绑定上,而默认参数值仍是静态的,这不符合大多数开发者的预期。

3) 违背直觉:默认参数通常被认为是函数接口的一部分,当在继承层次中被重定义时,很容易产生不符合预期的行为。如果子类重新定义了基类的默认参数值,调用时的行为可能与开发者的直觉相悖。

2.示例说明

下面是一个示例来说明这个问题:

#include <iostream>

class Base {
public:
    // 基类中的虚函数,带有默认参数值
    virtual void printMessage(int x = 10) {
        std::cout << "Base class, x = " << x << std::endl;
    }
};

class Derived : public Base {
public:
    // 重写基类函数,并试图改变默认参数值
    void printMessage(int x = 20) override {
        std::cout << "Derived class, x = " << x << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();

    // 使用基类指针调用函数
    basePtr->printMessage(); // 输出:Base class, x = 10

    delete basePtr;
    return 0;
}

        即使 basePtr 实际指向的是 Derived 对象,默认参数值仍然是基类中的 10,而不是子类中重新定义的 20。这是因为默认参数值在编译时绑定,与指针的静态类型(即 Base*)有关,而不是动态类型(Derived*)。

        如果你希望在子类中改变默认参数的行为,应该避免在继承体系中使用默认参数,转而使用显式的参数传递。

#include <iostream>

class Base {
public:
    virtual void printMessage(int x) {
        std::cout << "Base class, x = " << x << std::endl;
    }
};

class Derived : public Base {
public:
    void printMessage(int x) override {
        std::cout << "Derived class, x = " << x << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();

    // 显式传递参数
    basePtr->printMessage(20); // 输出:Derived class, x = 20

    delete basePtr;
    return 0;
}

在这种实现中,没有使用默认参数值,函数的行为完全由传递的参数决定,这样可以避免不必要的混淆。

3.替代方案

方案 1:使用重载

在派生类中重载函数,而不是重新定义它:

#include <iostream>

class Base {
public:
    void display(int x = 10) {
        std::cout << "Base display: " << x << std::endl;
    }
};

class Derived : public Base {
public:
    // 重载函数,不改变默认参数值
    void display(int x) {
        std::cout << "Derived display: " << x << std::endl;
    }

    // 可选:提供一个不带参数的版本,调用重载版本
    void display() {
        display(20);
    }
};

int main() {
    Derived d;
    d.display();  // 输出: Derived display: 20

    Base* bPtr = &d;
    bPtr->display();  // 输出: Base display: 10

    return 0;
}

方案 2:使用不同的函数名

如果逻辑上合理,可以使用不同的函数名来避免混淆:

#include <iostream>

class Base {
public:
    void display(int x = 10) {
        std::cout << "Base display: " << x << std::endl;
    }
};

class Derived : public Base {
public:
    void show(int x = 20) {  // 使用不同的函数名
        std::cout << "Derived show: " << x << std::endl;
    }
};

int main() {
    Derived d;
    d.show();  // 输出: Derived show: 20
    d.display();  // 输出: Base display: 10

    Base* bPtr = &d;
    bPtr->display();  // 输出: Base display: 10

    return 0;
}

4.总结

        避免在派生类中重新定义继承而来的函数的默认参数值,可以提高代码的可读性和可维护性,减少潜在的错误。如果确实需要改变行为,可以考虑使用函数重载或不同的函数名。


http://www.kler.cn/a/386519.html

相关文章:

  • 障碍检测与避障控制 ROS2机器人
  • 【C++】 C++游戏设计---五子棋小游戏
  • Java中的面向对象编程基础——定义类、对象、方法和属性
  • C++ 语言实现读写.csv文件.xls文件
  • 【项目开发】RESTful架构及RESTful API设计指南
  • 【Leecode】Leecode刷题之路第46天之全排列
  • Android中桌面小部件的开发流程及常见问题和解决方案
  • Chrome与傲游在隐私保护上的表现
  • linux centos新机器容器中创建虚拟环境
  • jmeter常用配置元件介绍总结之安装插件
  • 重塑商业未来:探索SAAS服务的无限可能
  • 基于STM32的自动化植物浇灌系统教学
  • 谷粒商城-高级篇-认证服务
  • 云计算在esxi 主机上创建 4g磁盘,同时在此磁盘上部署linux
  • .NET使用TDengine时序数据库和SqlSugar操作TDengine
  • 如何在 Django 中生成 Excel 文件并上传至 FastDFS
  • Python基础学习-02转义、输入、函数
  • C语言--结构体的大小与内存对齐,位段详解
  • Go 指针的使用
  • C#中lock(变量)与lock(this)的区别
  • 鸿蒙应用开发:断点下载
  • LinkedList和单双链表。
  • Linux的Shell脚本1
  • 智谱AI:ChatGLM强大的生成式语言模型
  • Flutter 插件 sliding_up_panel 实现从底部滑出的面板
  • 华为OD技术一面手撕题