C++ 重載、隱藏、覆蓋的區(qū)別小結(jié)
結(jié)合這C++ 代碼,深入理解重載(Overload)、隱藏(Hide) 和覆蓋(Override,也叫重寫) 這三個(gè)核心概念,用這段代碼作為核心案例,由淺入深地拆解它們的區(qū)別和本質(zhì)。
一、先明確三個(gè)概念的核心定義
這三個(gè)概念都是 C++ 中函數(shù)名復(fù)用的場(chǎng)景,但適用范圍、規(guī)則和目的完全不同,先看一張對(duì)比表建立整體認(rèn)知:
| 概念 | 中文名稱 | 適用范圍 | 核心特征 | 本質(zhì)目的 |
|---|---|---|---|---|
| Overload | 重載 | 同一個(gè)作用域內(nèi) | 函數(shù)名相同,參數(shù)列表(個(gè)數(shù)/類型/順序)不同,返回值不影響 | 方便同一功能的不同參數(shù)調(diào)用 |
| Override | 覆蓋/重寫 | 子類與父類之間 | 子類重寫父類的虛函數(shù),函數(shù)簽名(名+參數(shù)+const)完全一致 | 實(shí)現(xiàn)多態(tài)(運(yùn)行時(shí)動(dòng)態(tài)綁定) |
| Hide | 隱藏/遮蔽 | 子類與父類之間 | 子類函數(shù)名覆蓋父類同名函數(shù)(無論參數(shù)/虛函數(shù)與否),編譯期靜態(tài)屏蔽 | 作用域優(yōu)先原則導(dǎo)致的“遮蔽” |
覆蓋是特殊的隱藏。
二、結(jié)合代碼逐個(gè)拆解
逐個(gè)分析這三個(gè)概念,并用擴(kuò)展代碼演示效果。
1. 重載(Overload)—— 同一作用域內(nèi)的“同名不同參”
核心規(guī)則:
- 必須在同一個(gè)類/同一個(gè)作用域中;
- 函數(shù)名完全相同;
- 參數(shù)列表必須不同(個(gè)數(shù)、類型、順序,三者至少一個(gè)不同);
- 返回值、是否是虛函數(shù)不影響重載(但僅返回值不同不算重載)。
代碼中的重載案例:
在 Base 類中,兩個(gè) show 函數(shù)就是典型的重載:
class Base
{
public:
Base(int a):ma(a) {}
// 重載1:無參 show
virtual void show() {cout<<"Base::show()"<<endl;}
// 重載2:帶int參數(shù)的 show —— 與上面構(gòu)成重載
virtual void show(int i) {cout<<"Base::show(int)"<<endl;}
private:
int ma;
};
- 它們?cè)?
Base這個(gè)同一作用域內(nèi); - 函數(shù)名都是
show; - 參數(shù)列表不同(一個(gè)無參,一個(gè)int參數(shù));
- 都是虛函數(shù)(但即使不是虛函數(shù),依然是重載)。
重載的調(diào)用示例:
int main() {
Base b(10);
b.show(); // 調(diào)用 Base::show() —— 匹配無參版本
b.show(5); // 調(diào)用 Base::show(int) —— 匹配int參數(shù)版本
return 0;
}
編譯期編譯器會(huì)根據(jù)實(shí)參類型自動(dòng)匹配對(duì)應(yīng)的重載函數(shù),這是靜態(tài)綁定(編譯期確定調(diào)用哪個(gè))。
2. 覆蓋/重寫(Override)—— 子類對(duì)父類虛函數(shù)的“精準(zhǔn)替換”
核心規(guī)則:
- 必須在子類與父類之間;
- 父類函數(shù)必須是虛函數(shù)(virtual);
- 子類函數(shù)的函數(shù)簽名完全一致(函數(shù)名 + 參數(shù)列表 + const/volatile 屬性完全相同);
- 子類函數(shù)可以加
override關(guān)鍵字顯式聲明(C++11 后推薦,編譯器會(huì)檢查是否真的覆蓋)。
代碼中的覆蓋案例:
Derive 類中的 show() 覆蓋了 Base 類中的無參 show():
class Derive : public Base
{
public:
Derive(int a, int b):Base(a), mb(b) {}
// 覆蓋:與 Base::show() 簽名完全一致,且父類是虛函數(shù)
void show() override {cout<<"Derive::show()"<<endl;} // 加override更規(guī)范
private:
int mb;
};
- 父類
Base::show()是虛函數(shù); - 子類
Derive::show()函數(shù)名、參數(shù)列表(無參)完全一致; - 這就是“覆蓋”,目的是實(shí)現(xiàn)多態(tài)。
覆蓋的多態(tài)調(diào)用示例:
int main() {
Base* ptr = new Derive(1, 2); // 父類指針指向子類對(duì)象
ptr->show(); // 調(diào)用 Derive::show() —— 運(yùn)行時(shí)動(dòng)態(tài)綁定(多態(tài))
delete ptr;
return 0;
}
輸出:Derive::show()
這里因?yàn)楦采w了虛函數(shù),運(yùn)行時(shí)會(huì)根據(jù)對(duì)象的實(shí)際類型(Derive)調(diào)用對(duì)應(yīng)的函數(shù),而不是指針類型(Base),這是動(dòng)態(tài)綁定。
3. 隱藏/遮蔽(Hide)—— 子類對(duì)父類同名函數(shù)的“編譯期屏蔽”
核心規(guī)則:
- 必須在子類與父類之間;
- 子類定義了與父類同名的函數(shù)(無論參數(shù)是否相同、是否是虛函數(shù));
- 編譯器在編譯子類作用域時(shí),會(huì)屏蔽父類所有同名函數(shù),即使父類是重載/虛函數(shù);
- 這是編譯期靜態(tài)行為,與多態(tài)無關(guān)。
代碼中的隱藏案例(重點(diǎn)!):
你的代碼中,Derive 類定義了 show(),會(huì)隱藏父類 Base 中所有名為 show 的函數(shù)(包括 show() 和 show(int)):
int main() {
Derive d(1, 2);
d.show(); // 正常:調(diào)用 Derive::show()
// d.show(5); // 編譯錯(cuò)誤!父類的 show(int) 被隱藏了
return 0;
}
為什么會(huì)報(bào)錯(cuò)?
因?yàn)樽宇?Derive 中有 show() 函數(shù),編譯器在解析 d.show(5) 時(shí),會(huì)優(yōu)先在 Derive 作用域找 show,發(fā)現(xiàn)只有無參的 show(),沒有 show(int),且不會(huì)去父類找(被隱藏了),因此編譯失敗。
隱藏的其他場(chǎng)景(補(bǔ)充理解):
即使子類函數(shù)參數(shù)與父類不同,依然會(huì)隱藏:
class Derive : public Base
{
public:
Derive(int a, int b):Base(a), mb(b) {}
// 子類定義 show(double) —— 與父類 show(int) 不同參,但依然隱藏父類所有 show
void show(double d) {cout<<"Derive::show(double)"<<endl;}
private:
int mb;
};
int main() {
Derive d(1,2);
// d.show(5); // 編譯錯(cuò)誤!父類 show(int) 被隱藏,子類只有 show(double)
d.show(5.0); // 正常:調(diào)用 Derive::show(double)
return 0;
}
如何解除隱藏?
如果想在子類中調(diào)用父類被隱藏的函數(shù),有兩種方式:
- 用
using聲明,把父類函數(shù)引入子類作用域:
class Derive : public Base
{
public:
using Base::show; // 解除父類所有 show 函數(shù)的隱藏
Derive(int a, int b):Base(a), mb(b) {}
void show() override {cout<<"Derive::show()"<<endl;}
private:
int mb;
};
// 此時(shí)調(diào)用正常
int main() {
Derive d(1,2);
d.show(); // 調(diào)用 Derive::show()
d.show(5); // 調(diào)用 Base::show(int) —— 隱藏被解除
return 0;
}
- 顯式指定父類作用域調(diào)用:
int main() {
Derive d(1,2);
d.Base::show(5); // 直接調(diào)用父類的 show(int),不受隱藏影響
return 0;
}
三、容易混淆的點(diǎn)對(duì)比
1. 覆蓋 vs 重載
| 維度 | 覆蓋(Override) | 重載(Overload) |
|---|---|---|
| 作用域 | 跨類(父類→子類) | 同作用域(同一類) |
| 函數(shù)簽名 | 必須完全一致 | 必須不同(參數(shù)列表) |
| 虛函數(shù)要求 | 父類必須是虛函數(shù) | 無要求(可虛可非虛) |
| 綁定方式 | 運(yùn)行時(shí)動(dòng)態(tài)綁定 | 編譯期靜態(tài)綁定 |
2. 覆蓋 vs 隱藏
| 維度 | 覆蓋(Override) | 隱藏(Hide) |
|---|---|---|
| 前提 | 父類是虛函數(shù) + 簽名一致 | 只要子類有同名函數(shù)(無論參數(shù)/虛函數(shù)) |
| 本質(zhì) | 多態(tài)的實(shí)現(xiàn)方式 | 作用域優(yōu)先導(dǎo)致的屏蔽 |
| 綁定方式 | 運(yùn)行時(shí)動(dòng)態(tài)綁定 | 編譯期靜態(tài)屏蔽 |
| 父類函數(shù)可用性 | 父類虛函數(shù)被“替換”但可通過作用域調(diào)用 | 父類同名函數(shù)被屏蔽,需顯式調(diào)用 |
四、完整演示代碼
把上述所有場(chǎng)景整合,可以直接運(yùn)行測(cè)試:
#include <iostream>
using namespace std;
class Base
{
public:
Base(int a):ma(a) {}
// 重載1:無參虛函數(shù)
virtual void show() {cout<<"Base::show()"<<endl;}
// 重載2:帶int參數(shù)的虛函數(shù)(與上面構(gòu)成重載)
virtual void show(int i) {cout<<"Base::show(int) "<<i<<endl;}
private:
int ma;
};
class Derive : public Base
{
public:
using Base::show; // 解除父類show的隱藏(注釋掉這行看隱藏效果)
Derive(int a, int b):Base(a), mb(b) {}
// 覆蓋:重寫父類無參show
void show() override {cout<<"Derive::show()"<<endl;}
// 新增:子類獨(dú)有的show(double)(會(huì)隱藏父類show,除非加using)
void show(double d) {cout<<"Derive::show(double) "<<d<<endl;}
private:
int mb;
};
int main() {
// 1. 測(cè)試Base的重載
Base b(10);
b.show(); // Base::show()
b.show(5); // Base::show(int) 5
// 2. 測(cè)試多態(tài)(覆蓋)
Base* ptr = new Derive(1, 2);
ptr->show(); // Derive::show() —— 動(dòng)態(tài)綁定
ptr->show(6); // Base::show(int) 6 —— 子類未覆蓋該版本,調(diào)用父類
delete ptr;
// 3. 測(cè)試子類的隱藏與解除
Derive d(1, 2);
d.show(); // Derive::show()
d.show(7); // Base::show(int) 7(加using后生效,否則編譯錯(cuò)誤)
d.show(3.14); // Derive::show(double) 3.14
d.Base::show(); // 顯式調(diào)用父類show()
return 0;
}
總結(jié)
- 重載是同一作用域內(nèi)“同名不同參”,編譯期靜態(tài)匹配,目的是簡(jiǎn)化調(diào)用;
- 覆蓋是子類重寫父類虛函數(shù)(簽名一致),運(yùn)行時(shí)動(dòng)態(tài)綁定,是多態(tài)的核心;
- 隱藏是子類同名函數(shù)屏蔽父類所有同名函數(shù)(編譯期行為),需用
using或作用域顯式調(diào)用父類函數(shù)。
這三個(gè)概念的核心區(qū)別在于作用域和綁定時(shí)機(jī):重載是“同作用域+編譯期”,覆蓋是“跨類+運(yùn)行期”,隱藏是“跨類+編譯期屏蔽”。
補(bǔ)充
#include <iostream>
using namespace std;
class Base // Base base(10);
{
public:
Base(int a):ma(a) {}
// 虛函數(shù)
virtual void show() {cout<<"Base::show()"<<endl;}
virtual void show(int i) {cout<<"Base::show(int)"<<endl;}
private:
int ma;
};
class Derive : public Base
{
public:
Derive(int a, int b):Base(a), mb(b) {}
void show() {cout<<"Derive::show"<<endl;}
private:
int mb;
};
補(bǔ)充說明
虛函數(shù)與重寫:
Base類中定義了兩個(gè)虛函數(shù)show()和show(int i)。Derive類重寫了無參版本show(),但沒有重寫帶參版本show(int i)。- 由于 C++ 的名稱隱藏規(guī)則,
Derive類中定義的show()會(huì)隱藏掉基類中所有名為show的函數(shù)(包括show(int i))。
典型調(diào)用行為:
int main() { Derive d(1, 2); d.show(); // 正確,調(diào)用 Derive::show() // d.show(10); // 編譯錯(cuò)誤!基類的 show(int) 被隱藏了 return 0; }如果想在
Derive中也能調(diào)用Base::show(int),需要在Derive中添加using Base::show;來解除名稱隱藏。
到此這篇關(guān)于C++ 重載、隱藏、覆蓋的區(qū)別小結(jié)的文章就介紹到這了,更多相關(guān)C++ 重載、隱藏、覆蓋內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在vs2010中,輸出當(dāng)前文件路徑與源文件當(dāng)前行號(hào)的解決方法
本篇文章是對(duì)在vs2010中,輸出當(dāng)前文件路徑與源文件當(dāng)前行號(hào)的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C++ 將一個(gè)文件讀入數(shù)組再讀出數(shù)組的方法
今天小編就為大家分享一篇C++ 將一個(gè)文件讀入數(shù)組再讀出數(shù)組的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07
成員初始化列表與構(gòu)造函數(shù)體中的區(qū)別詳細(xì)解析
無論是在構(gòu)造函數(shù)初始化列表中初始化成員,還是在構(gòu)造函數(shù)體中對(duì)它們賦值,最終結(jié)果是相同的。不同之處在于,使用構(gòu)造函數(shù)初始化列表的版本初始化數(shù)據(jù)成員,沒有定義初始化列表的構(gòu)造函數(shù)版本在構(gòu)造函數(shù)體中對(duì)數(shù)據(jù)成員賦值2013-09-09
C++中priority_queue模擬實(shí)現(xiàn)的代碼示例
在c++語言中數(shù)據(jù)結(jié)構(gòu)中的堆結(jié)構(gòu)可以通過STL庫中的priority_queue 優(yōu)先隊(duì)列來實(shí)現(xiàn),這樣做極大地簡(jiǎn)化了我們的工作量,這篇文章主要給大家介紹了關(guān)于C++中priority_queue模擬實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2021-08-08
VSstudio中scanf返回值被忽略的原因及解決方法(推薦)
這篇文章主要介紹了VSstudio中scanf返回值被忽略的原因及其解決方法,scanf返回值被忽略,接下來我就告訴大家該如何解決這個(gè)問題,需要的朋友可以參考下2022-09-09

