【命名空間 + 輸入&輸出 + 缺省參數 + 函數重載】目錄
- 前言:
- ---------------hello world---------------
- 比較C語言和C++的第一個程序:hello word
- ---------------命名空間---------------
- 什么是命名空間?
- 怎么使用命名空間?
- 怎么定義命名空間?
- 怎么訪問命名空間?
- C++為什么要引入命名空間?
- 命名空間怎么就解決了同名標識符互相干擾的難題?
- 命名沖突的本質是什么?
- C++的作用域的訪問順序是什么?
- 命名空間中可以放什么?
- 命名空間怎么進行嵌套使用?
- 命名空間的其他注意事項
- ---------------輸入&輸出---------------
- C++的輸入&輸出是什么?
- C++的cin和cout相較于C語言的輸入和輸出有什么優勢?
- 關于cin和cout還有哪些需要注意事情?
- ---------------缺省參數---------------
- 什么是缺省參數?
- 缺省參數的有哪幾種?
- 使用缺省參數時的注意事項有哪些?
- C++中引入缺省參數有什么好處?
- ---------------函數重載---------------
- 什么是函數重載?
- 滿足函數重載的條件有哪些?
- 還有哪些特殊的函數重載?
- 函數重載和缺省參數有什么不同?
往期《C++初階》回顧:
/------------ 入門基礎 ------------/
【C++的前世今生】
前言:
(。・ω・。)ノ?hi~ 親愛的小伙伴們!(≧?≦)ノ 當芒種的風?呼呼吹過,田間地頭滿是農人播種希望的身影,今天我們也迎來了正式學習 C++ 知識的新旅程了(★ω★)
那么,就讓我們像芒種時節播撒種子一樣,播撒 C++ 這顆種子,期待未來收獲滿滿的編程碩果 🍎?
---------------hello world---------------
比較C語言和C++的第一個程序:hello word
/*------------使用C語言編寫第一個 hello world 代碼------------*/
#include <stdio.h>int main()
{printf("hello world");return 0;
}/*------------使用C++編寫第一個 hello world 代碼------------*/
#include <iostream> //1.使用#include包含C++的庫
using namespace std; //2.使用命名空間int main()
{cout << "hello world" << endl; //3.使用輸出流對象cout向終端控制窗口(小黑窗口) 輸出字符串return 0;
}
---------------命名空間---------------
什么是命名空間?
命名空間(Namespace)
:是一種將標識符(如:變量、函數、類等)組織成不同作用域,用于組織代碼、避免命名沖突的機制
怎么使用命名空間?
使用命名空間整體上主要是分為兩步:
第一步:
定義
命名空間第二步:
訪問
命名空間
怎么定義命名空間?
namespace 命名空間名稱
{// 可包含的內容:// - 變量// - 函數// - 類/結構體// - 其他命名空間類型 變量名;返回類型 函數名(參數列表) { /* 函數體 */ }class 類名 { /* 類定義 */ };struct 結構體名 { /* 結構體定義 */ };namespace 子命名空間名 { /* ... */ }
}
怎么訪問命名空間?
在 C++ 中,命名空間(
namespace
)的使用方式非常靈活,以下是博主精心挑選的三個最實用的命名空間的使用方法,涵蓋從基礎到高級的所有場景:
- 直接通過作用域解析符
::
訪問- 使用
using
聲明(引入特定成員)- 使用
using namespace
指令(引入整個空間)
1. 直接通過作用域解析符
::
訪問:
- 適用場景:精確控制訪問路徑,避免歧義。
/*----------------------------命名空間的使用----------------------------*/ #include <iostream> #include <string>/*---------------第一步:命名空間的定義---------------*/ namespace my_space {string str = "這是我定義的命名空間中的字符串";void fun(){cout << "這是我定義的命名空間中的函數";} }int main() {/*---------------第二步:演示如何使用自定義的命名空間中的成員---------------*//*-------------第一種的使用方法:使用作用域解析符訪問-------------*/cout << my_space::str << endl;//printf("%s", my_space::str); 注意:這里你可別使用C語言的printf進行輸出C++風格的字符串//string是C++的字符串對象,printf不能輸出對象這種類型,(仔細回想一下C++中是沒用字符串這種類型的,C語言是使用字符數組存儲字符串的)printf("%s", my_space::str.c_str()); //如果你執意要使用printf進行輸出的話,可以將string通過的函數c_str()轉化為C風格的字符串return 0; }
2. 使用
using
聲明(引入特定成員):
- 適用場景:需要頻繁使用某個命名空間的少數成員
/*----------------------------命名空間的使用----------------------------*/#include <iostream> #include <string> using namespace std;/*---------------第一步:命名空間的定義---------------*/ namespace my_space {string str = "這是我定義的命名空間中的字符串";void fun(){cout << "這是我定義的命名空間中的函數";} }using my_space::str; // 注意:使用 using 聲明(引入特定成員)必須寫在自定義的命名空間的后面int main() {/*---------------第二步:演示如何使用自定義的命名空間中的成員---------------*//*-------------第二種的使用方法:使用 using 聲明(引入特定成員)-------------*/cout << str << endl;my_space::fun();return 0; }
3. 使用
using namespace
指令(引入整個空間):
- 適用場景:短代碼片段或源文件中(頭文件中禁止使用)
/*----------------------------命名空間的使用----------------------------*/#include <iostream> #include <string> using namespace std;/*---------------第一步:命名空間的定義---------------*/ namespace my_space {string str = "這是我定義的命名空間中的字符串";void fun(){cout << "這是我定義的命名空間中的函數";} }using namespace my_space; // 注意:使用 using namespace 指令(引入整個空間)必須寫在自定義的命名空間的后面int main() {/*---------------第二步:演示如何使用自定義的命名空間中的成員---------------*//*-------------第三種的使用方法:使用 using namespace 指令(引入整個空間)-------------*/cout << str << endl;fun();return 0; }
看到這里我相信有不少的小伙伴們會想到這行代碼:
using namespace std;
這行代碼官方的回答是:將
std
命名空間中的所有成員引入到當前作用域,允許直接使用這些標識符而無需添加std::
前綴簡單點說就是:我們使用namespace定義一個命名空間,就像是在全局作用域這片廣袤的大地上面使用圍墻圍起來一片屬于自己的土地,這片土地有了主人(命名空間的名字)
以后誰再想訪問圍墻里面的東西,必須得到這片土地的主人的同意(指定命名空間的名字進行訪問)
其中有一片土地簡直就是世外桃源,土地的主人名叫
std
,很多人想去哪里但是每次都要征得同意(添加std::
前綴),是不是很麻煩,因此我們使用using namespace std
的感覺就像是:偷了了std
的家——暴力將這片土地上的圍墻給拆除了(大型項目中不建議這么做,這將會導致命名空間失去意義),從此大家去世外桃源就是想去就去了!!!
C++為什么要引入命名空間?
當項目規模較大時,不同庫或模塊可能使用相同的名稱
(尤其是當兩名在同一個項目組的程序員合并他倆的代碼的時候,合并之后往往他倆有可能就會打起來),可能會導致命名沖突。
小案例:我寫的變量和庫函數的函數名引發命名沖突
#include <iostream>
#include <stdlib.h>
/*------------任務:定義變量 + 在終端上打印該變量存儲的值------------*/int rand = 100;
int main()
{printf("%d", rand); //error:“rand”: 重定義;以前的定義是“函數”return 0;
}
而為了讓大家能和睦相處,因此C++ 引入了命名空間機制,它通過邏輯隔離的方式,確保不同作用域內的同名標識符不會互相干擾,從而讓代碼協作更加和諧高效。
小案例:使用命名空間避免命名沖突
#include <iostream>
#include <stdlib.h>
/*------------任務:定義變量 + 在終端上打印該變量存儲的值------------*/namespace my_space
{int rand = 100;
}int main()
{printf("%d", rand); //error:“rand”: 重定義;以前的定義是“函數”return 0;
}
命名空間怎么就解決了同名標識符互相干擾的難題?
為了回答這個問題,我們要對
作用域
有一定的理解,那先從C語言說起吧:關于C語言的作用域有哪些,不同的人會給出不同的劃分種類,但是從本質上理解C語言就兩個作用域:
函數局部作用域
和全局作用域
因為一個C程序中所有的變量、函數存在的位置無非就是:函數局部作用域和全局作用域
(其中函數只能存儲于全局作用域中)
而這時候我們就要仔細思考:是C語言的哪些不足導致了上面的命名沖突的情況,而使得C++不得不引入命名空間
回答:由于C語言的作用域過于單一,當項目規模較大時,會有大量的變量、函數都存在于全局作用域中,這樣的話就可能會導致很多命名沖突。
(在同一個函數作用域中一般我們不會使用相同的標識符進行命名)
所以C++的思路是:可不可以從全局作用域中再分出去一些域,所以就有了C++四大作用域:
函數局部作用域
命名空間作用域
類域
全局作用域
為解決這個問題,我們還要了解:
- 命名沖突的本質是什么?
- C++的作用域的訪問順序是什么?
命名沖突的本質是什么?
命名沖突(Name Conflict)
:在同一作用域
內使用相同的標識符
(如:變量名、函數名、類名等),導致編譯器無法區分它們的情況。
注意:這里我們要抓住核心的的名詞:
“同一作用域 + 相同的標識符”
所以這也就好理解為什么:C++又在C語言的基礎上又創建了兩個作用域
C++的作用域的訪問順序是什么?
在 C++ 中,當訪問一個標識符時,編譯器會按照特定的順序查找該標識符的定義,這個過程稱為
名稱查找(Name Lookup)
理解這個順序對于避免命名沖突和理解代碼行為至關重要。
名稱查找順序:
局部作用域(Block Scope)
:首先在當前函數的局部作用域中查找標識符,包括在當前塊(如:if 語句、for 循環等)中定義的變量。類作用域(Class Scope)
:如果在局部作用域中沒有找到標識符,那么會在包含當前函數的類的成員作用域中查找。(如果當前函數是類的成員函數的話)命名空間作用域(Namespace Scope)
:如果在類作用域中沒有找到標識符,那么會在當前命名空間中查找。(如果定義的有命名空間的話)全局作用域(Global Scope)
:如果在上面的作用域中都沒有找到標識符,那么就會在全局作用域中查找。std 命名空間
:在 C++ 中,std 是一個特殊的命名空間,包含了標準庫的所有內容。如果在全局作用域中沒有找到標識符,那么會檢查 std 命名空間。
綜上所述:這樣做我們就是實現了:對標識符的名稱進行本地化,以避免命名沖突或名字污染。也就是說:雖然咱倆寫的標識符的名稱相同,但是代碼合并后重名的標識符不再是在同一個作用域:
全局作用域
,而是現在在不同的作用域:不同的命名空間作用域
,所以不構成命名沖突。
所以合并完代碼之后咱倆還是好朋友🐶
命名空間中可以放什么?
/*------------C++的命名空間使用------------*/
#include <stdio.h>
#include <stdlib.h>/*------------定義一個的命名空間:my_utils------------*/
namespace my_utils
{//1.命名空間中可以定義:變量int rand = 100; //定義變量(故意與標準庫rand函數同名)//2.命名空間中可以定義:函數int add(int left, int right){return left + right;}//3.命名空間中可以定義:自定義數據類型struct SingleListNode{int data;struct SingleListNode* next;};
}
int main()
{//1.使用默認訪問(全局命名空間訪問):這里訪問的是全局的標準庫中rand函數地址printf("%p\n", rand);printf("%p\n", ::rand);//2.使用指定域(my_utils命名空間)訪問:這里訪問是my_utils命名空間的rand變量的值printf("%d\n", my_utils::rand);return 0;/* * 關鍵點說明:* 1. ::rand - 訪問全局命名空間的rand(標準庫函數)* 2. my_utils::rand - 訪問my_utils命名空間的rand變量* 3. 實際開發中應避免與標準庫同名*/
}
命名空間怎么進行嵌套使用?
/*------------命名空間的嵌套使用------------*/
#include <stdio.h>/*------------定義一個的命名空間:school------------*/
//1.外層命名空間:school
namespace school
{//2.內層命名空間1:teacher_anamespace teacher_a{//2.1:定義變量int rand = 50;//2.2:定義函數int add(int left, int right){return left + right;}}//2.內層命名空間2:teacher_bnamespace teacher_b{//2.1:定義變量int rand = 100;//2.2:定義函數int add(int left, int right){return (left + right) * 10; //將結果放大原來的10倍}}
}
int main()
{/*------------訪問嵌套命名空間中的成員------------*/// 1. 訪問不同命名空間中的同名變量printf("teacher_a的rand值: %d\n", school::teacher_a::rand); // 輸出50printf("teacher_b的rand值: %d\n", school::teacher_b::rand); // 輸出100// 2. 調用不同命名空間中的同名函數printf("teacher_a的加法結果(1+2): %d\n",school::teacher_a::add(1, 2)); // 輸出3(1+2)printf("teacher_b的加法結果(1+2): %d\n",school::teacher_b::add(1, 2)); // 輸出30((1+2)*10)return 0;
}
命名空間的其他注意事項
命名空間還有很多可以單獨拎出來講的東西:博主這里就精挑一些我們平時使用命名空間需要注意事項來講一下:
命名空間與頭文件的關系
避免在頭文件中使用
using namespace
:可能導致命名沖突擴散到包含該頭文件的所有代碼。// myheader.h (錯誤示例!) using namespace std; // ? 污染所有包含該頭文件的源文件
命名空間的全局可見性
不同文件的同名命名空間會自動合并:
// File1.cpp namespace Shared {void func1(); }// File2.cpp namespace Shared //與File1中的Shared合并 { void func2(); }
---------------輸入&輸出---------------
C++的輸入&輸出是什么?
cin
和cout
:是 C++ 標準庫中用于標準輸入輸出的流對象,用于實現控制臺程序的輸入和輸出操作。
- 它們定義在
<iostream>
頭文件中。- 它們屬于
std
命名空間。
標準輸入流 (
cin
):對應鍵盤輸入,類型為istream
cin
通過 提取運算符 (>>
) 從鍵盤讀取數據到變量標準輸出流 (
cout
):對應控制臺輸出,類型為ostream
cout
通過 插入運算符 (<<
) 將數據輸出到控制臺它們是面向對象的 I/O 方式,比 C 語言的
scanf
和printf
更安全、更靈活。
C++的cin和cout相較于C語言的輸入和輸出有什么優勢?
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;int main()
{//1.定義各種類型的變量int a;double b;char c;printf("-------------使用C風格進行輸入&輸出-------------\n");//2.使用C風格進行輸入賦值scanf("%d %lf %c ", &a, &b, &c);//3.使用C風格進行輸出printf("%d %lf %c\n", a, b, c);cout << "-------------使用C++風格進行輸入&輸出-------------" << endl;//4.使用C++風格進行輸入賦值cin >> a >> b >> c;//5.使用C++風格進行輸出cout << a << " " << b << " " << c << " " << endl;return 0;
}
特性 | scanf / printf | cin / cout |
---|---|---|
類型安全 | ? 需手動匹配格式符 | ?? 自動類型推導 |
擴展性 | ? 僅支持基本類型 | ?? 支持自定義類型的 << 和 >> |
性能 | ?? 更快 | ?? 稍慢(同步問題) |
關于cin和cout還有哪些需要注意事情?
<<
: 運算符可連續使用,按順序輸出表達式的值
endl
: 是一個函數,流插入輸出時,相當于插入一個換行符,同時刷新輸出緩沖區(立即顯示內容)
\n
: 僅換行,不強制刷新緩沖區(性能略優,但需注意輸出時機)
>>
:自動跳過空白字符(空格、制表符、換行符),并嘗試將輸入轉換為目標類型。
- 讀取字符串時,
cin
默認在遇到空格時停止,因此無法讀取包含空格的完整名稱(如:Alice Smith
會被截斷為Alice
)
功能 | 語法 | 說明 |
---|---|---|
輸出 | cout << value; | 使用 << 插入運算符,支持鏈式調用 |
輸入 | cin >> variable; | 使用 >> 提取運算符,跳過空白字符 |
讀取整行 | getline(cin, str); | 讀取包含空格的字符串 |
格式化輸出 | cout << setprecision(2); | 需要 <iomanip> 頭文件 |
cout
和cin
為保證類型安全,性能略低于 C 的printf
和scanf
可通過以下三句話可以將cin和cout的輸入輸出速度提升2-3倍
ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
下面是算法競賽中常用的解綁三件套的模板和詳細的注釋:
#include <iostream>
using namespace std;int main()
{/* ==============================================* 【IO性能優化關鍵代碼】* 以下三行用于提高C++標準IO流的執行效率* 適用于需要高速IO的場景(如算法競賽)* ==============================================*//*----------第一步:關閉C++標準流與C標準庫的同步----------*/// - 默認情況下,C++的cin/cout與C的stdin/stdout保持同步以保證混用時的安全性// - 關閉后可以提高速度,但不能再混用C/C++的IO函數(如:printf/cin混用)ios_base::sync_with_stdio(false);/*----------第二步:解綁cin與cout的關聯----------*/// - 默認情況下,cin操作會先自動刷新cout的緩沖區(保證交互式程序的正確性)// - 解綁后進一步提升速度,但需要手動控制緩沖區刷新(如:用endl或flush)cin.tie(nullptr);/*----------第三步:解綁cout與cerr/clog的關聯(非必須,但某些編譯器需要)----------*/// - 進一步減少不必要的緩沖區同步cout.tie(nullptr);/* ==============================================* 【注意事項】* 使用這些優化后:* 1. 禁止混用C/C++的IO函數(如不能用printf + cin)* 2. 需要顯式刷新緩沖區(如cout << endl;)* 3. 適用于純C++ IO且數據量大的場景(如:10萬+次讀寫)* ==============================================*/return 0;
}
---------------缺省參數---------------
什么是缺省參數?
缺省參數(Default Arguments)
:是指在函數聲明
或定義
時為參數指定一個默認值。
- 當函數調用時,如果沒有為該參數傳遞實際參數,就會使用這個默認值。
缺省參數的語法:
返回類型 函數名(類型 參數名 = 默認值);
缺省參數的使用案例:
/*--------------------------C++的缺省參數--------------------------*/
#include <iostream>
using namespace std;/*------------簡單的了解缺省參數------------*/void Func(int a = 0) // 參數a的默認值為0
{// 輸出參數a的值cout << "a = " << a << endl;
}int main()
{//情況1:不傳遞參數,使用默認值cout << "調用Func():" << endl;Func(); // 等效于Func(0)// 輸出結果:a = 0//情況2:傳遞明確參數值cout << "\n調用Func(10):" << endl;Func(10); // 覆蓋默認值,a被賦值為10// 輸出結果:a = 10return 0;
}
缺省參數的有哪幾種?
全缺省參數
:函數的所有參數都有默認值
半缺省參數
:函數的部分參數有默認值,且有默認值的參數必須放在形參列表的右側
- 從右往左依次連續缺省,不能間隔跳躍給缺省值
全缺省參數和半缺省參數的使用案例:
/*------------------全缺省參數and半缺省參數------------------*//*** @brief 演示全缺省參數的函數* @param a 第一個參數,默認值為10* @param b 第二個參數,默認值為20* @param c 第三個參數,默認值為30* @note* - 全缺省參數:所有參數都有默認值* - 調用時可傳遞0~3個參數* - 缺省參數必須從右向左連續定義*/
void Func1(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a;cout << " b = " << b;cout << " c = " << c << endl;
}/*** @brief 演示半缺省參數的函數* @param a 必須傳入的參數(無默認值)* @param b 可選參數,默認值為10* @param c 可選參數,默認值為20* @note* - 半缺省參數:部分參數有默認值* - 必須從右向左連續缺省* - 調用時至少傳遞1個參數(對應a)*/
void Func2(int a, int b = 10, int c = 20)
{cout << "a = " << a;cout << " b = " << b;cout << " c = " << c << endl;
}int main()
{// 測試Func1的全缺省調用cout << "----- Func1全缺省測試 -----" << endl;Func1(); // 使用全部默認值:a=10, b=20, c=30Func1(1); // a=1, b和c用默認值:b=20, c=30Func1(1, 2); // a=1, b=2, c用默認值:c=30Func1(1, 2, 3); // 覆蓋所有默認值:a=1, b=2, c=3// 測試Func2的半缺省調用cout << "----- Func2半缺省測試 -----" << endl;Func2(100); // a必須傳值:a=100, b和c用默認值:b=10, c=20Func2(100, 200); // a=100, b=200, c用默認值:c=20Func2(100, 200, 300); // 覆蓋所有參數:a=100, b=200, c=300return 0;
}
使用缺省參數時的注意事項有哪些?
特性 | 說明 | 示例 |
---|---|---|
從右向左連續 | 缺省參數必須從參數列表最右側開始連續定義 | void func(int a, int b=1, int c=2) ??void func(int a=0, int b) ? |
調用時從左匹配 | 實參按從左到右順序匹配形參,不能跳過參數 | func(10) → a=10,b=1,c=2 func(10,20) → a=10,b=20,c=2 |
單次定義原則 | 如果函數在頭文件中聲明并指定了缺省參數,在源文件中定義時不能再重復指定缺省參數,否則會導致重復定義錯誤。 | 頭文件:void foo(int x=5); 源文件: void foo(int x) {...} |
C++中引入缺省參數有什么好處?
疑問:可能有很多的小伙伴們在學習了缺省參數后,會感慨它有什么用處呢?
有這樣的疑問是很正常的,接下來博主將使用C++的缺省參數重新實現一下我們在《數據結構初階》中學習的
順序棧
的其中的一個接口函數:順序棧的初始化
,帶你領略一下缺省參數的牛逼厲害之處。
------------------------------Stack.h-------------------------------
#include <iostream>
#include <assert.h>
using namespace std;typedef int STKDataType;
typedef struct Stack
{STKDataType* a;int top;int capacity;
}STK;void STKInit(STK* pstk, int n = 4); //這里稍微注意一下;由于單次定義原則所以我們一般在函數聲明時指定缺省參數------------------------------Stack.cpp-----------------------------#include "Stack.h"void STKInit(STK* pstk, int n) //缺省參數不能聲明和定義同時給,所以定義時候我們沒有給缺省參數
{assert(pstk);assert(n > 0);pstk->a = (STKDataType*)malloc(n * sizeof(STKDataType));pstk->top = 0;pstk->capacity = n;
}------------------------------Test.cpp-----------------------------#include "Stack.h"int main()
{STK s1;STKInit(&s1);//確定知道要插入1024個數據, 初始化時一把開好, 避免擴容STK s2;STKInit(&s2, 1024);return 0;
}
通過閱讀上面的代碼,小伙伴們你們是不是已經體會到缺省參數的好處了呢?
是不是在之前無論我們是否知道棧的初始空間的大小,棧的空間都要經過從4、8、16、32、……、二倍二倍的進行擴容的一個過程。
但是如果我們事先已經知道了棧的存儲空間,例如是1024字節,那么是不是我們可以在定義棧的時候就一次性的將空間開辟好,也就相當于省去了10次擴容的時間消耗對吧,所以說缺省參數是很有價值的。
---------------函數重載---------------
什么是函數重載?
函數重載(Function Overloading)
:是指在同一個作用域內,可以有多個同名函數,但是這些函數的參數列表(參數個數
、參數類型
、參數順序
)必須不同 。
- 編譯器根據函數調用時提供的實參信息來確定具體調用哪個函數。
- 函數的返回值類型不能作為函數重載的依據。
滿足函數重載的條件有哪些?
規則 | 說明 | 正確案例 |
---|---|---|
參數個數不同 | 在同一作用域內,同名函數的參數個數必須有差異,這樣編譯器才能根據調用時傳入參數的數量來區分調用哪個函數 | int add(int a, int b); int add(int a, int b, int c); |
參數類型不同 | 同名函數的參數類型至少有一個不一樣,編譯器依據實參類型匹配對應的函數版本 | int add(int a, int b); double add(double a, double b); |
參數順序不同 | 同名函數的參數順序不一致,也可構成重載,編譯器會根據參數傳入順序判斷調用哪個函數 | void print(int num, char ch); void print(char ch, int num); |
注意事項:僅返回值類型不同不能構成函數重載
三種函數重載的示例:
/*-------------------------三種函數重載的案例-------------------------*/
#include <iostream>
using namespace std;/*------------------重載案例1:參數個數不同------------------*//*** @brief 無參版本函數*/
void f()
{cout << "調用 f()" << endl;
}/*** @brief 單參版本函數* @param a 整型參數*/
void f(int a)
{cout << "調用 f(int a)" << endl;
}/*------------------重載案例2:參數類型不同------------------*/
/*** @brief 函數重載示例2:參數類型不同* @param left 第一個整數操作數* @param right 第二個整數操作數* @return 兩數之和* @note 與下面的double版本構成重載*/
int add(int left, int right)
{cout << "調用 int add(int left, int right)" << endl;return left + right;
}/*** @brief 函數重載示例2:參數類型不同* @param left 第一個浮點數操作數* @param right 第二個浮點數操作數* @return 兩數之和* @note 與上面的int版本構成重載*/
double add(double left, double right)
{cout << "調用 double add(double left, double right)" << endl;return left + right;
}/*------------------重載案例3:參數順序不同------------------*/
/*** @brief 參數順序:int在前,char在后*/
void f(int a, char b)
{cout << "調用 f(int a, char b)" << endl;
}/*** @brief 參數順序:char在前,int在后*/
void f(char b, int a)
{cout << "調用 f(char b, int a)" << endl;
}int main()
{// 測試參數個數不同的重載f(); // 調用無參版本f(10); // 調用int參數版本// 測試類型不同的重載add(10, 20); // 調用int版本add(10.1, 20.2); // 調用double版本// 測試參數順序不同的重載f(10, 'a'); // 調用(int, char)版本f('a', 10); // 調用(char, int)版本return 0;
}
還有哪些特殊的函數重載?
規則 | 說明 | 正確案例 |
---|---|---|
const 修飾符差異 | 函數參數的頂層 const(如const int )不能構成重載,但底層 const(如int* const vs const int* )可以 | void func(int* ptr); void func(const int* ptr); (底層 const 不同) |
指針類型差異 | 函數參數為不同類型的指針(如普通指針與常量指針)可以構成重載 | void func(int* ptr); void func(int* const ptr); (頂層 const 不同) |
引用與值類型差異 | 函數參數為值類型和引用類型可以構成重載 | void func(int a); void func(int& a); |
成員函數的 const 重載 | 類的成員函數是否為 const 可以構成重載,用于區分對常量對象和非常量對象的調用 | int getValue() const; int getValue(); |
模板參數實例化不同 | 函數模板通過不同的模板參數實例化可以生成重載函數 | template<typename T> void func(T a); template<> void func(int a); (顯式特化) |
注意:對于初學C++的萌新這里可以先作為了解,不作為掌握的內容。
函數重載和缺省參數有什么不同?
函數重載與缺省參數二者都能實現靈活調用函數,但原理不同。
函數重載:是多個同名函數,參數列表不同。
缺省參數:是一個函數,參數有默認值。
特性 | 缺省參數 | 函數重載 |
---|---|---|
實現方式 | 單一函數擴展參數 | 多個同名函數 |
適用場景 | 參數有明確默認值 | 參數類型/數量有本質差異 |
靈活性 | 行為一致,僅參數值變化 | 可完全改變函數行為 |