[C++]成员函数的重载(overload)、覆盖(override)和隐藏(hide)

本文详细介绍了C++中成员函数的重载、覆盖和隐藏规则。重载要求在同一作用域内,函数名相同但参数列表不同;覆盖发生在派生类与基类中,函数名、参数列表完全相同且基类函数为虚函数。隐藏则是派生类函数参数列表与基类不同或基类非虚函数导致基类函数在派生类中不可见。解决隐藏问题可以使用using声明或调整派生类定义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++成员函数的重载、覆盖和隐藏

ref: https://blog.csdn.net/wanghuiqi2008/article/details/28419645
ref: https://blog.csdn.net/zgbsoap/article/details/566120
ref: 《高质量程序设计指南 C++/C语言 第3版》

1. 重载与覆盖

成员函数被重载的特征是:

  • 具有相同的作用域(即同一个类定义中);
  • 函数名字相同;
  • 参数类型、顺序或数目不同(包括);
  • virtual 关键字可有可无。

覆盖是指派生类重新实现(或者改写)了基类的成员函数,其特征是:

  • 不同的作用域(分别位于派生类和基类中);
  • 函数名称相同;
  • 参数列表完全相同;
  • 基类函数必须是虚函数。
#pragma once

#include <iostream>

using namespace std;

// 展示了 Overload 和 Override

class Base
{
public:
    void f(int x) { cout << "Base::f(int) " << x << endl; }
    // overload Base::f(int x)
    void f(float x) { cout << "Base::f(float) " << x << endl; }
    virtual void g(void) { cout << "Base::g(void)" << endl; }
};
class Derived : public Base
{
public:
    // override Base::g(void)
    virtual void g(void) { cout << "Derived::g(void)" << endl; }
};

void test()
{
    Derived d;
    Base *pb = &d;
    pb->f(42);      // Base::f(int) 42
    pb->f(3.14f);   // Base::f(float) 3.14
    pb->g();        // Derived::g(void)
}

注:

  1. virtual 关键字告诉编译器,派生类中相同的成员函数应该放到 vtable 中,并替换基类相应成员函数的槽位;
  2. 虚函数的覆盖有两种方式:完全重写和扩展。扩展是指派生类虚函数首先调用基类的虚函数,然后在增加新的功能。

2. 令人迷惑的隐藏规则

隐藏是指派生类的成员函数遮蔽了与其同名的基类成员函数,具体规则如下:

  • 派生类的函数与基类的函数同名,但是参数列表有所差异,此时,不论有无 virtual 关键字,基类的函数在派生类中将被隐藏(注意别于重载混淆);
  • 派生类的函数与基类的函数同名,参数列表也相同,但是基类函数没有 virtual 关键字。此时,基类的函数在派生类中将被隐藏(注意别与覆盖混淆)。
#pragma once

#include <iostream>

using namespace std;

// 展示了 Override 和 Hide

class Base
{
public:
    virtual void f(float x) { cout << "Base::f(float) " << x << endl; }
    void g(float x) { cout << "Base::g(float) " << x << endl; }
    void h(float x) { cout << "Base::h(float) " << x << endl; }
};
class Derived : public Base
{
public:
    // override Base::f()
    virtual void f(float x) { cout << "Derived::f(float) " << x << endl; }
    // hide Base::g()
    void g(int x) { cout << "Derived::g(int) " << x << endl; }
    // hide Base::h()
    void h(float x) { cout << "Derived::h(float) " << x << endl; }
};

void test()
{
    Derived d;
    Base *pb = &d;
    Derived *pd = &d;
    // Good : behavior depends solely on type of the object
    pb->f(3.14f); // Derived::f(float) 3.14
    pd->f(3.14f); // Derived::f(float) 3.14
    // Bad : behavior depends on type of the pointer
    pb->g(3.14f); // Base::g(float) 3.14
    pd->g(3.14f); // Derived::g(int) 3 (surprise!)
    // Bad : behavior depends on type of the pointer
    pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
    pd->h(3.14f); // Derived::h(float) 3.14
}

注:如果你确实想使用所谓的“跨越类边界的重载”,可以在派生类定义中的任何地方显示地使用 using 关键字,如下:

class Derived: public Base{
public:
    using Base::g;
    void g(int x) {...}
}

3. 摆脱隐藏

隐藏规则引起不少麻烦。下面代码中,程序员可能期望语句 pd->f(10) 调用 Base::f(int),但是 Base::f(int) 不幸被Derived::f(char) 隐藏了。由于数字 10 不能被隐式地转换为字符串类型,所以在编译时出错。

#pragma once

#include <iostream>

using namespace std;

// 展示了 Hide 所带来的麻烦

class Base
{
public:
    void f(int x) { cout << x << endl; }
};
class Derived : public Base
{
public:
    // hide Base::f(int)
    void f(char *str) { cout << str << endl; }

    // 摆脱隐藏法一
    //using Base::f;

    // 摆脱隐藏法二
    //void f(int x) { Base::f(x); }
};

void test(void)
{
    Derived *pd = new Derived;
    pd->f(10); // error 编译时报错,因为Base中函数被隐藏
}

如果语句 pd->f(10) 确实想调用函数 Base::f(int),那么有两个办法:其一就是使用 using 声明;其二就是把类 Derived 修改为如下的样子。

class Derived : public Base
{
public:
    // hide Base::f(int)
    void f(char *str) { cout << str << endl; }
    // 调用传递
    void f(int x) { Base::f(x); }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值