【C++】重载、重写和重定义的区别详解

目录

一、概念对比总览

二、详细解析

[1. 重载 (Function Overloading)](#1. 重载 (Function Overloading))

定义

特点

示例代码

重要规则

[2. 重写/覆盖 (Function Overriding)](#2. 重写/覆盖 (Function Overriding))

定义

特点

示例代码

[C++11 override关键字](#C++11 override关键字)

[3. 隐藏/重定义 (Function Hiding)](#3. 隐藏/重定义 (Function Hiding))

定义

特点

示例代码

三、对比分析

[1. 参数相同但非虚函数](#1. 参数相同但非虚函数)

[2. 虚函数但不构成重写](#2. 虚函数但不构成重写)

四、记忆技巧与面试考点

记忆口诀

常见面试题

[1. 选择题:以下代码输出什么?](#1. 选择题:以下代码输出什么?)

[2. 选择题:以下代码输出什么?](#2. 选择题:以下代码输出什么?)

[3. 综合题](#3. 综合题)

五、最佳实践建议

总结


一、概念对比总览

特性 重载 (Overload) 重写/覆盖 (Override) 隐藏/重定义 (Hide)
作用域 同一作用域 不同作用域(继承关系) 不同作用域(继承关系)
函数名 相同 相同 相同
参数列表 必须不同 必须相同 可以不同
返回类型 可以不同 必须相同(协变除外) 可以不同
virtual关键字 不需要 基类必须有virtual 不需要
访问权限 可以不同 可以不同 可以不同
多态性 编译时多态 运行时多态 无多态

二、详细解析

1. 重载 (Function Overloading)

定义

在同一作用域内,函数名相同但参数列表不同的多个函数。

特点
  • 发生在同一类中或全局作用域

  • 根据参数类型、个数、顺序不同来区分

  • 编译器根据调用时的实参选择匹配的函数

  • 属于编译时多态

示例代码

cpp

复制代码
#include <iostream>
using namespace std;

class Calculator {
public:
    // 重载add函数
    int add(int a, int b) {
        return a + b;
    }
    
    double add(double a, double b) {
        return a + b;
    }
    
    int add(int a, int b, int c) {
        return a + b + c;
    }
    
    // 参数类型顺序不同也可以重载
    void print(int a, double b) {
        cout << "int, double: " << a << ", " << b << endl;
    }
    
    void print(double a, int b) {
        cout << "double, int: " << a << ", " << b << endl;
    }
};

// 全局函数重载
void show(int x) {
    cout << "int: " << x << endl;
}

void show(double x) {
    cout << "double: " << x << endl;
}

int main() {
    Calculator calc;
    cout << calc.add(1, 2) << endl;       // 调用int版本
    cout << calc.add(1.5, 2.5) << endl;   // 调用double版本
    cout << calc.add(1, 2, 3) << endl;    // 调用三参数版本
    
    calc.print(1, 2.5);   // 调用(int, double)版本
    calc.print(1.5, 2);   // 调用(double, int)版本
    
    show(10);     // 调用int版本
    show(3.14);   // 调用double版本
    
    return 0;
}
重要规则
  1. 不能仅通过返回类型不同来重载

    cpp

    复制代码
    // 错误:仅返回类型不同
    int func() { return 0; }
    double func() { return 0.0; }  // 编译错误
  2. const成员函数和非const成员函数可以重载

    cpp

    复制代码
    class Test {
    public:
        void display() { cout << "non-const" << endl; }
        void display() const { cout << "const" << endl; }  // 可以重载
    };

2. 重写/覆盖 (Function Overriding)

定义

派生类中重新定义基类的虚函数,要求函数签名完全相同。

特点
  • 发生在继承关系中

  • 基类函数必须声明为virtual

  • 实现运行时多态

  • 通过基类指针/引用调用时,根据实际对象类型决定调用哪个函数

示例代码

cpp

复制代码
#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() {  // 虚函数
        cout << "动物叫" << endl;
    }
    
    virtual void eat() {
        cout << "动物吃东西" << endl;
    }
    
    void sleep() {  // 非虚函数
        cout << "动物睡觉" << endl;
    }
};

class Dog : public Animal {
public:
    virtual void speak() override {  // 重写基类虚函数
        cout << "汪汪汪" << endl;
    }
    
    virtual void eat() override {
        cout << "狗吃骨头" << endl;
    }
    
    void sleep() {  // 隐藏基类函数,不是重写
        cout << "狗趴着睡" << endl;
    }
};

class Cat : public Animal {
public:
    virtual void speak() override {
        cout << "喵喵喵" << endl;
    }
};

void testAnimal(Animal& animal) {
    animal.speak();  // 动态绑定:根据实际对象类型调用
    animal.eat();    // 动态绑定
    animal.sleep();  // 静态绑定:总是调用Animal::sleep()
}

int main() {
    Dog dog;
    Cat cat;
    
    cout << "=== 通过对象调用 ===" << endl;
    dog.speak();  // 输出:汪汪汪
    dog.sleep();  // 输出:狗趴着睡
    
    cout << "\n=== 通过基类引用调用(多态) ===" << endl;
    testAnimal(dog);  
    // speak(): 汪汪汪 (动态绑定)
    // eat(): 狗吃骨头 (动态绑定)
    // sleep(): 动物睡觉 (静态绑定)
    
    testAnimal(cat);
    // speak(): 喵喵喵 (动态绑定)
    // eat(): 动物吃东西 (动态绑定)
    // sleep(): 动物睡觉 (静态绑定)
    
    cout << "\n=== 通过基类指针调用 ===" << endl;
    Animal* ptr = &dog;
    ptr->speak();  // 输出:汪汪汪 (动态绑定)
    ptr->sleep();  // 输出:动物睡觉 (静态绑定)
    
    return 0;
}
C++11 override关键字

cpp

复制代码
class Base {
public:
    virtual void func(int) {}
};

class Derived : public Base {
public:
    virtual void func(int) override {}     // 正确:明确表示重写
    // virtual void func(double) override {} // 错误:不是重写,编译报错
};

3. 隐藏/重定义 (Function Hiding)

定义

派生类定义了与基类同名的函数,无论参数是否相同,都会隐藏基类的同名函数。

特点
  • 发生在继承关系中

  • 基类函数被隐藏,无法通过派生类对象直接访问

  • 可以使用作用域解析运算符::显式调用基类函数

  • 无多态性,根据指针/引用的静态类型决定调用哪个函数

示例代码

cpp

复制代码
#include <iostream>
using namespace std;

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

class Derived : public Base {
public:
    // 隐藏了基类的所有display函数
    void display() {
        cout << "Derived::display()" << endl;
    }
    
    // 新增函数,与基类show参数不同
    void show(int x) {  // 隐藏Base::show()
        cout << "Derived::show(int): " << x << endl;
    }
};

int main() {
    Derived d;
    
    d.display();    // 调用Derived::display()
    // d.display(10); // 错误:Base::display(int)被隐藏
    
    d.show(5);      // 调用Derived::show(int)
    // d.show();     // 错误:Base::show()被隐藏
    
    // 使用作用域解析运算符访问被隐藏的函数
    d.Base::display();     // 调用Base::display()
    d.Base::display(10);   // 调用Base::display(int)
    d.Base::show();        // 调用Base::show()
    
    // 通过基类指针/引用
    Base* ptr = &d;
    ptr->display();    // 调用Base::display()(静态绑定)
    ptr->display(20);  // 调用Base::display(int)
    ptr->show();       // 调用Base::show()
    
    return 0;
}

三、对比分析

1. 参数相同但非虚函数

cpp

复制代码
class Base {
public:
    void func() { cout << "Base" << endl; }  // 非虚函数
};

class Derived : public Base {
public:
    void func() { cout << "Derived" << endl; }  // 隐藏,不是重写
};

int main() {
    Derived d;
    Base* ptr = &d;
    
    d.func();      // 输出:Derived
    ptr->func();   // 输出:Base(静态绑定,因为不是虚函数)
    
    return 0;
}

2. 虚函数但不构成重写

cpp

复制代码
class Base {
public:
    virtual void func(int) { cout << "Base::func(int)" << endl; }
};

class Derived : public Base {
public:
    virtual void func(double) { cout << "Derived::func(double)" << endl; }
    // 这是新虚函数,不是重写Base::func(int)
    // Base::func(int)被隐藏
};

int main() {
    Derived d;
    Base* ptr = &d;
    
    d.func(10.5);      // 调用Derived::func(double)
    // d.func(10);     // 错误:Base::func(int)被隐藏
    ptr->func(10);     // 调用Base::func(int)
    
    return 0;
}

四、记忆技巧与面试考点

记忆口诀

  • 重载:同域同名不同参

  • 重写:异域同名同参需虚函

  • 隐藏:异域同名就隐藏,虚不虚函都无妨

常见面试题

1. 选择题:以下代码输出什么?

cpp

复制代码
class A {
public:
    virtual void f() { cout << "A"; }
};

class B : public A {
public:
    void f() { cout << "B"; }
};

void test(A& a) {
    a.f();
}

int main() {
    B b;
    test(b);
    return 0;
}

答案:B(动态绑定)

2. 选择题:以下代码输出什么?

cpp

复制代码
class A {
public:
    void f() { cout << "A"; }
};

class B : public A {
public:
    void f() { cout << "B"; }
};

void test(A& a) {
    a.f();
}

int main() {
    B b;
    test(b);
    return 0;
}

答案:A(静态绑定,隐藏)

3. 综合题

cpp

复制代码
class Base {
public:
    virtual void vfunc() { cout << "Base vfunc" << endl; }
    void func() { cout << "Base func" << endl; }
};

class Derived : public Base {
public:
    virtual void vfunc() override { cout << "Derived vfunc" << endl; }
    void func() { cout << "Derived func" << endl; }
    void func(int) { cout << "Derived func(int)" << endl; }
};

int main() {
    Derived d;
    Base* p = &d;
    
    p->vfunc();    // 输出:Derived vfunc(重写,动态绑定)
    p->func();     // 输出:Base func(隐藏,静态绑定)
    d.func();      // 输出:Derived func
    d.func(10);    // 输出:Derived func(int)
    // p->func(10); // 错误:Base没有func(int)
    
    return 0;
}

五、最佳实践建议

  1. 使用override关键字

    • C++11开始,在派生类重写虚函数时使用override

    • 让编译器检查是否正确重写,避免隐藏错误

  2. 虚析构函数

    • 多态基类应该声明虚析构函数

    • 确保通过基类指针删除派生类对象时正确调用析构函数

  3. 避免函数隐藏

    • 使用using声明引入基类函数

    cpp

    复制代码
    class Derived : public Base {
    public:
        using Base::display;  // 引入Base的所有display函数
        void display() { /* ... */ }
    };
  4. 明确设计意图

    • 需要多态时使用虚函数重写

    • 只是提供不同实现时考虑函数重载

    • 避免无意中的函数隐藏

总结

理解重载、重写和隐藏的区别对于掌握C++面向对象编程至关重要:

  • 重载提供同一功能的不同接口(编译时决定)

  • 重写实现运行时多态,是继承体系的核心

  • 隐藏往往是无意中造成的错误,需要特别注意

正确使用这些特性可以让代码更清晰、更健壮,同时充分利用C++的多态能力。

相关推荐
许杰小刀2 小时前
Python网络请求库,从 requests 到 httpx
开发语言·python·httpx
量子炒饭大师2 小时前
【OpenClaw修炼宝典】——【Windows安装篇】想玩《爪子船长》复刻版却卡在安装?OpenClaw 从零环境搭建与编译全攻略 (小白避坑指南)
windows·openclaw
历程里程碑2 小时前
1 . Git本地操作:版本控制 跨平台协作 仓库核心
java·开发语言·数据结构·c++·git·gitee·github
小欣加油2 小时前
leetcode 42 接雨水
c++·算法·leetcode·职场和发展
ZXF_H2 小时前
VSCode C/C++函数Ctrl+鼠标点击无法跳转的解决方法
c++·ide·vscode
tankeven2 小时前
动态规划专题(14):石子合并问题(未完待续)
c++·算法·动态规划
humors2212 小时前
一些安全类网站(不定期更新)
linux·网络·windows·安全·黑客·白帽
xianluohuanxiang2 小时前
高精度气象:极端天气一来,零售最先出问题的不是客流,而是补货体系和损失控制
开发语言·人工智能·深度学习·机器学习·零售
KKKlucifer2 小时前
零信任融合 4A 平台,构建全域身份动态可信管控体系
开发语言·php