首页 > 编程语言 >C/C++ 知识点:重载、覆盖和隐藏

C/C++ 知识点:重载、覆盖和隐藏

时间:2024-11-01 23:21:03浏览次数:3  
标签:知识点 函数 show C++ Base 基类 重载 隐藏 cout

文章目录


前言:

在C++中多态性是一个核心概念,它允许我们通过不同的方式实现相同的接口。重载(Overloading)、覆盖(Overriding,也被称为重写)和隐藏(Hiding)是实现多态性的三种主要手段。尽管它们名称相似,但在具体实现和用途上存在显著差异。下面将对这三种机制进行详细探讨。

一、重载、覆盖和隐藏

1、重载(overload)

1.1、定义

函数重载允许在同一作用域内声明多个同名但参数列表不同的函数的特性,是C++实现静态多态(编译期)的形式。这里的参数列表不同,可以是指参数的数量不同、参数的类型不同,或者参数的顺序不同。

注意: 函数的返回类型并不参与重载的区分。

1.2、使用 const 关键字

  • 普通函数使用const关键字:

    两个同名的函数,一个函数的参数类型是int,另一个函数的参数类型是int &,则这两个函数构成重载,例如:

    void show(const string &str)
    {
        cout << str << endl;
    }
    
    void show(string &str)
    {
        cout << str << endl;
    }
    
  • 成员函数使用const关键字

    两个同名的成员函数,具有相同的参数列表,但是这两个成员函数的const性不同,这两个成员函数不构成重载,例如:

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

1.3、实现原理

C++函数重载的实现原理主要依赖于编译器的名称修饰技术,编译器在编译阶段会根据函数的参数列表生成不同的修饰名,这些修饰名包含了函数的名称、参数类型、参数数量以及调用约定等信息。这样,即使函数名相同,但由于参数列表不同,编译器也能为它们生成唯一的修饰名,从而在链接阶段正确区分和调用相应的函数。

2、覆盖(override)

2.1、定义

覆盖是指派生类中的成员函数重新定义基类中的虚函数。当派生类中存在一个与基类虚函数同名、参数列表相同且返回类型相同的成员函数时,就发生了覆盖。例如:

class Base {  
public:  
    virtual void show() {  
        cout << "Base class" << endl;  
    }  
};  
  
class Derived : public Base {  
public:  
    void show() override { // 使用override关键字明确表示这是一个覆盖操作  
        cout << "Derived class" << endl;  
    }  
};  

2.2、覆盖的条件

  • 继承关系: 覆盖首先要求有继承关系,即派生类必须是从基类派生出来的。
  • 虚函数: 基类中的函数必须被声明为虚函数。
  • 函数签名相同: 派生类中的函数必须与基类中的虚函数具有相同的函数签名,包括函数名、参数列表以及返回类型。
  • 访问权限: 派生类中的覆盖函数可以有不同的访问权限(如从public变为protectedprivate),但这通常不是推荐的做法,因为它可能会影响代码的可读性和可维护性。

2.3、override 关键字

在C++11及以后的版本中,override关键字被引入,用于明确表示一个派生类成员函数是对基类虚函数的覆盖。使用override关键字可以帮助编译器检查函数签名的匹配性,从而避免一些常见的错误,如函数隐藏或参数不匹配等。例如:

#include <iostream>  
using namespace std;  
  
class Base {  
public:  
    virtual void show() {  
        cout << "Base class show function" << endl;  
    }  
  
    virtual int getValue() const {  
        return 42;  
    }  
};  
  
class Derived : public Base {  
public:  
    void show() override { // 明确表示这是对Base类中show函数的覆盖  
        cout << "Derived class show function" << endl;  
    }  
  
    int getValue() const override { // 明确表示这是对Base类中getValue函数的覆盖  
        return 100;  
    }  
  
    // 如果下面这个函数没有使用override关键字,并且Base类中有一个名为print的非虚函数,  
    // 则它不会覆盖Base::print,而是会隐藏它。但在这个例子中,我们假设Base类没有print函数。  
    // void print() override { // 错误:Base类中没有名为print的虚函数来覆盖  
    //     cout << "Derived class print function" << endl;  
    // }  
};  
  
int main() {  
    Base* b = new Derived();  
    b->show(); // 调用Derived类中的show函数  
    cout << "Value: " << b->getValue() << endl; // 调用Derived类中的getValue函数  
    delete b;  
    return 0;  
}

3、隐藏(hiding)

3.1、定义

隐藏发生在继承关系中,当派生类中的函数或成员变量与基类中的函数或成员变量同名时,派生类的成员会隐藏基类的同名成员。这意味着,当通过派生类的对象、指针或引用来访问这些同名成员时,将只会看到派生类中的成员,而基类中的同名成员将被遮蔽。

3.2、隐藏的条件

  • 同名: 派生类中的成员与基类中的成员必须同名。
  • 不同作用域: 隐藏发生在不同的作用域中,即基类和派生类。
  • 函数参数列表可以不同: 对于函数来说,即使参数列表不同,只要函数名相同,也会导致基类函数被隐藏。这与重载不同,重载发生在同一作用域内,且要求参数列表不同。
  • 基类函数是否为虚函数: 无论基类函数是否为虚函数,只要满足上述条件,都可能导致隐藏。但需要注意的是,如果基类函数是虚函数且派生类希望覆盖它,则应该使用override关键字来明确指示,以避免隐藏的发生。

3.3、隐藏与覆盖的区别

  • 发生条件: 隐藏发生在同名成员之间,无论它们是否在同一作用域内。而覆盖则要求基类函数必须是虚函数,且派生类函数与基类函数具有相同的函数签名。
  • 作用域: 隐藏涉及不同作用域中的同名成员,而覆盖则发生在继承关系中,但要求基类函数是虚函数。
  • 编译器处理: 隐藏是在编译时确定的,编译器会根据作用域规则来选择要访问的成员。而覆盖则涉及运行时的动态绑定,即根据对象的实际类型来选择要调用的函数。

3.4、示例

#include <iostream>  
using namespace std;  
  
class Base {  
public:  
    void show() {  
        cout << "Base class show function" << endl;  
    }  
  
    void fun(double d) {  
        cout << "Base class fun function with double" << endl;  
    }  
};  
  
class Derived : public Base {  
public:  
    void show(int i) { // 隐藏了Base类中的show函数  
        cout << "Derived class show function with int" << endl;  
    }  
  
    void fun(int i) { // 隐藏了Base类中的fun函数  
        cout << "Derived class fun function with int" << endl;  
    }  
};  
  
int main() {  
    Derived d;  
    d.show(10); // 调用Derived类中的show函数(隐藏了Base类的show函数)  
    // d.show(); // 错误:没有匹配的函数来调用(因为Base类的show函数被隐藏了)  
  
    d.fun(3.14); // 错误:没有匹配的函数来调用(因为Base类的fun(double)函数被隐藏了)  
    d.fun(10); // 调用Derived类中的fun函数(隐藏了Base类的fun函数)  
  
    Base* b = &d;  
    b->show(); // 调用Base类中的show函数(因为通过基类指针调用)  
    // b->fun(3.14); // 正确:调用Base类中的fun函数(因为通过基类指针调用,且参数匹配)  
    // b->fun(10); // 错误:没有匹配的函数来调用(因为通过基类指针调用,且Base类中没有fun(int)函数)  
  
    return 0;  
}

标签:知识点,函数,show,C++,Base,基类,重载,隐藏,cout
From: https://blog.csdn.net/cloud323/article/details/143442377

相关文章

  • NoSQL数据库实习头歌实验知识点整理(二)-MongoDB部分
    文章目录1-1初识MongoDB1.1DOS(Windows)端启动MongoDB服务1.1.1配置环境变量1.1.2启动服务并进行相关配置1.2Linux端启动MongoDB服务1.2.1数据存放位置1.2.2日志文件1.2.3配置文件1.3启动客户端1.4退出客户端1.5关闭MongoDB服务1.5.1能连接到客户端时1......
  • c++:vector
    一、vector是什么?1.1vector的介绍vector是表示可变大小数组的序列容器。 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。本质......
  • C++详细笔记(五)
    1.类和对象1.1运算符重载(补)1.运算符重载中,参数顺序和操作数顺序是一致的。2.一般成员函数重载为成员函数,输入流和输出流重载为全局函数。3.由1和2只正常的成员函数默认第一个参数为this指针而重载中参数顺序和操作数顺序要一致,则导致使用时为d<<cout;(不符合使用习惯正常为......
  • CodeForces Dora and C++ (2007C) 题解
    CodeForcesDoraandC++(2007C)题解题意给一个数组\(c_{1...n}\),定义数组的\(range\)是最大值减最小值,即极差。给出两个值\(a\)和\(b\),每步操作中,可给数组中任一一个数增大\(a\)或增大\(b\)。问任意步操作后(可以是\(0\)步),极差的最小值。思路(要直接看答案可以跳......
  • 07C++选择结构(1)——教学
    一、基础知识1、关系运算符因为我们要对条件进行判断,必然会用到关系运算符:名称大于大于等于小于小于等于等于不等于符号>>=<<===!=关系表达式的值是一个逻辑值,即“真”(True)或“假”(False)。如果条件成立,其值为“真”;如果条件不成立,其值为“假”。2、逻......
  • C++写一个简单的JSON解析
    参考用C++编写一个简易的JSON解析器(1)写一个动态类型-知乎欢迎测试和反馈bug首先,json包含string,number,integer,object,array,bool,null这些类型对于object映射,使用map,对于array使用vector我们定义一个类Val用来存储,使用variant来存储具体的值std::variant-cppreferen......
  • C++对象模型:object
    一、objecttypedefstruct{floatx;floaty;floatz;}Point3d;可以有以下方法打印上述类型字段:定义函数voidprint_point3d(constPoint3d*pd){printf("(%g,%g,%g)",pd->x,pd->y,pd->z);}若要更有效率,可以定义一个宏函数#definePoint3d_print(pd)......
  • C++多线程:atomic
    在许多为了性能和效率的场景下,需要开发一些lock-free的算法和数据结构atomic_flag原子布尔类型,只支持test-and-set和clear操作构造函数atomic_flag()noexcept=default;atomic_flag(constatomic_flag&)=delete;只有默认构造函数,而不能从其他对象构造atomic_flag对象需......
  • C++对象模型:constructor
    构造函数constructorexplicit的引入,是为了能够制止“单一参数的constructor”被当作一个conversion运算符带有默认构造函数的对象成员若一个类中包含对象成员,且该对象有默认构造函数,此时:若该类没有构造函数则编译器会合成一个默认构造函数,且发生在真正调用时若该类有构造函......