【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++的多态能力。

相关推荐
winner88814 分钟前
从零吃透C++命名空间、std、#include、string、vector
java·开发语言·c++
AI人工智能+电脑小能手12 分钟前
【大白话说Java面试题】【Java基础篇】第26题:Java的抽象类和接口有哪些区别
java·开发语言·面试
bzmK1DTbd21 分钟前
SOLID原则在Java中的实践:单一职责与开闭原则
java·开发语言·开闭原则
Aurorar0rua23 分钟前
CS50 x 2024 Notes C - 07
c语言·学习方法
AI进化营-智能译站24 分钟前
ROS2 C++开发系列07-高效构建机器人决策逻辑,运算符与控制流实战
开发语言·c++·ai·机器人
爱编码的小八嘎24 分钟前
C语言完美演绎9-15
c语言
winner888126 分钟前
C++ 命名空间、虚函数、抽象类、protected 权限全套通俗易懂精讲(附与 Java 对比)
java·开发语言·c++
一袋米扛几楼9830 分钟前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
不会编程的懒洋洋36 分钟前
C# P/Invoke 基础
开发语言·c++·笔记·安全·机器学习·c#·p/invoke
直奔標竿36 分钟前
Java开发者AI转型第二十五课!Spring AI 个人知识库实战(四)——RAG来源追溯落地,拒绝AI幻觉
java·开发语言·人工智能·spring boot·后端·spring