『C++成長記』C++入門—— 函數重載引用

?🔥博客主頁:小王又困了

📚系列專欄:C++

🌟人之為學,不日近則日退?

??感謝大家點贊👍收藏?評論??


目錄

一、函數重載

📒1.1函數重載的概念

📒1.2函數重載的種類

📒1.3 C++支持函數重載的原理

二、引用

📒2.1引用的概念

📒2.2引用的特性

📒2.3引用的使用場景

📒2.4傳值和引用性能比較

📒2.5常引用

📒2.6引用和指針的區別?


🗒?前言:

在上期的學習中,我們學習了命名空間和缺省參數,對C++有了初步的認識,本期我們將會學習函數重載和引用等新的概念。

一、函數重載

? ? ?自然語言中,一個詞可以有多重含義,人們可以通過上下文來判斷該詞真實的含義,即該詞被重載了。 比如:以前有一個笑話,國有兩個體育項目大家根本不用看,也不用擔心。一個是乒乓球,一個 是男足。前者是“誰也贏不了!”,后者是“誰也贏不了!”

📒1.1函數重載的概念

? ? ?函數重載是函數的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數,這些同名函數的形參列表(參數個數或類型或類型順序)不同,常用來處理實現功能類似數據類型不同的問題。

📒1.2函數重載的種類

  • 參數類型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}int main()
{cout << Add(1, 2) << endl;cout << Add(1.0, 2.0) << endl;
}

? ? ?上面的代碼定義了兩個同名的Add函數,但是它們的參數類型不同,第一個函數的兩個參數都是int型,第二個函數的兩個參數都是double型,在調用Add函數的時候,編譯器會根據所傳實參的類型自動判斷調用哪個函數。

  • 參數個數不同
void Fun()
{cout << "f()" << endl;
}void Fun(int a)
{cout << "f(int a)" << endl;
}int main()
{Fun();Fun(1);return 0;
}

  • 參數類型順序不同
void Text(int a, char b)
{cout << "Text(int a,char b)" << endl;
}void Text(char b, int a)
{cout << "Text(char b, int a)" << endl;
}int main()
{Text(1, 'a');Text('a', 1);return 0;
}
  • 有缺省參數的情況
void Fun()
{cout << "f()" << endl;
}void Fun(int a = 10)
{cout << "f(int a)" << endl;
}int main()
{Fun();     //無參調用會出現歧義Fun(1);    //調用的是第二個return 0;
}

? ? ?上面代碼中的兩個Fun函數構成函數重載,編譯可以通過,因為第一個沒有參數,第二個有一個整型參數,屬于上面的參數個數不同的情況。但是Fun函數存在一個問題:在沒有參數調用的時候會產生歧義,因為有缺省參數,所以對兩個Fun函數來說,都可以不傳參。

注意:返回值的類型與函數是否構成重載無關。

📒1.3 C++支持函數重載的原理

? ? ?在C/C++中,一個程序要運行起來,需要經歷以下幾個階段:預處理、編譯、匯編、鏈接。

? ? ?我們想理解清楚函數重載,還要了解函數簽名的概念,函數簽名包含了一個函數的信息,包括函數名、它的參數類型、他所在的類和名稱空間以及其他信息。函數簽名用于識別不同的函數。?C++編譯器和鏈接器都使用符號來標識和處理函數和變量,所以對于不同函數簽名的函數,即使函數名相同,編譯器和鏈接器都認為他們是不同的函數。

Linux環境下采用C語言編譯器編譯后結果

? ? ?可以看出經過gcc編譯后,函數名字的修飾沒有發生改變。這也就是為什么C語言不支持函數重
載,因為同名函數沒辦法區分。

?采用C++編譯器編譯后結果

?其中_Z是固定的前綴;3表示函數名的長度;Add就是函數名;i是int的縮寫,兩個i表示兩個參數都是int類型,d是double的縮寫,兩個d表示兩個參數都是double類型。C++就是通過函數修飾規則來區分,只要參數不同,修飾出來的名字就不一樣,就支持了重載。通過分析可以發現,修飾后的名稱中并不包含任何于函數返回值有關的信息,因此也驗證了上面說的返回值的類型與函數是否構成重載無關。

總結:

  1. C語言之所以沒辦法支持重載,是因為同名函數沒辦法區分。而C++是通過函數修飾規則來區分,只要參數不同,修飾出來的名字就不一樣,就支持了重載。
  2. 如果兩個函數函數名和參數是一樣的,返回值不同是不構成重載的,因為調用時編譯器沒辦法區分。
    ?

二、引用

📒2.1引用的概念

? ? ?引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會為引用變量開辟內存空 間,它和它引用的變量共用同一塊內存空間

  • 類型& 引用變量名(對象名) = 引用實體
int main()
{int a = 0;int& b = a;//定義引用類型,b是a的引用return 0;
}

注意:引用類型必須和引用實體是同種類型的

📒2.2引用的特性

  • 引用在定義時必須初始化
int main()
{int a = 10;int& b;    //錯誤的return 0;
}

在使用引用時,我們必須對變量進行初始化。int& b = a;,這樣的代碼才是被允許的。

  • 一個變量可以有多個引用
int main()
{int a = 10;int& b = a;int& c = a;return 0;
}

上面代碼中,b和c都是a的別名。就像孫悟空一樣,孫行者、悟空也都是他的名字,所以一個變量也可以同時有多個引用。

  • 引用不能改變指向
int main()
{int a = 10;int b = 20;int& c = a;c = b;return 0;
}

? ? ?我們可以看到b和c的地址不同,所以c = b表示的不是c是b引用,而是是把b變量的值賦值給c引用的實體,c依舊是a的引用,所以引用一旦引用一個實體,再不能引用其他實體,也就是引用不能改變指向。

📒2.3引用的使用場景

🎀做參數

引用做參數的意義:

  • 做輸出型參數,即要求形參的改變可以影響實參
  • 提高效率,自定義類型傳參,用引用可以避免拷貝構造,尤其是大對象和深拷貝對象

交換兩個整型變量:?

void Swap(int& num1, int& num2)
{int tmp = num1;num1 = num2;num2 = tmp;
}int main()
{int a = 5;int b = 10;Swap(a,b);return 0;
}

? ? ? 如上代碼,我們可以使用引用做參數實現了兩個數的交換,num1 a 的引用,和?a?在同一塊空間,對num1的修改也就是對?a 修改,?b 同理,所以在函數體內交換num1num2實際上就是交換?a b 以前交換兩個數的值,我們需要傳遞地址,還要進行解引用,相對繁瑣。

交換兩個指針變量:

void Swap(int*& p1, int*& p2)
{int* tmp = p1;p1 = p2;p2 = tmp;
}int main()
{int a = 5;int b = 10;int* pa = &a;int* pb = &b;Swap(pa,pb);return 0;
}

? ? ?如果用C語言來實現交換兩個指針變量,實參需要傳遞指針變量的地址,那形參就需要用二級指針來接收,這顯然十分容易出錯。有了引用之后,實參直接傳遞指針變量即可,形參用指針類型的引用。

🎀做返回值

引用做返回值的意義:

  • 減少拷貝,提高效率。
  • 可以同時讀取和修改返回對象
int& add(int x, int y)
{int sum = x + y;return sum;
}int main()
{int a = 5;int b = 10;int ret = add(a, b);cout << ret << endl;return 0;
}

? ? ?如上代碼,我們使用傳值返回,調用函數要創建棧幀,sum是add函數中的一個局部變量,存儲在當前函數的棧幀中,函數調用結束棧幀銷毀,sum也會隨之銷毀,對于這種傳值返回,會生成一個臨時的中間變量,用來存儲返回值,在返回值比較小的情況下,這個臨時的中間變量一般就是寄存器。

? ? ?如上代碼,傳引用就是給sum了一個別名,返回的值就是sum的別名,但是這里會出現問題,函數調用結束棧幀銷毀,sum也會隨之銷毀,返回它的值再進行調用就是越界訪問,打印出的值為隨機值。

可是這里的值為什么是正確的呢?這是取決于編譯器的,看編譯器是否會對這塊空間進行清理。

📒2.4傳值和引用性能比較

? ? ?以值作為參數或者返回值類型,在傳參和返回期間,函數不會直接傳遞實參或者將變量本身直 接返回,而是傳遞實參或者返回變量的一份臨時的拷貝,因此用值作為參數或者返回值類型,效 率是非常低下的,尤其是當參數或者返回值類型非常大時,效率就更低。

struct A
{int a[100000];
};void TestFunc1(A a)
{;
}void TestFunc2(A& a)
{;
}void TestFunc3(A* a)
{;
}//引用傳參————可以提高效率(大對象或者深拷貝的類對象)
void TestRefAndValue()
{A a;// 以值作為函數參數size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)//就是單純的調用一萬次這個函數傳一萬次參TestFunc1(a);size_t end1 = clock();// 以引用作為函數參數size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);//這里直接傳的是變量名size_t end2 = clock();//以指針作為函數參數size_t begin3 = clock();for (int i = 0; i < 10000; i++){TestFunc3(&a);}size_t end3 = clock();// 分別計算兩個函數運行結束后的時間cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;cout << "TestFunc3(A*)-time:" << end3 - begin3 << endl;
}

????

值和引用的作為返回值類型的性能比較:

struct A
{int a[100000];
};
A a;//全局的,函數棧幀銷毀后還在// 值返回
A TestFunc1()
{return a;
}// 引用返回
A& TestFunc2()
{return a;
}
void TestReturnByRefOrValue()
{// 以值作為函數的返回值類型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();//就讓他返回不接收size_t end1 = clock();// 以引用作為函數的返回值類型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 計算兩個函數運算完成之后的時間cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;
}int main()
{TestReturnByRefOrValue();return 0;
}

📒2.5常引用

🎀?權限放大

int main()
{const int a = 10;int& b = a;return 0;
}

? ? ?上面代碼中,用const定義了一個常變量?a?,然后給a取一個別名?b?,這段代碼在編譯過程中出現了錯誤,這是為什么呢?

? ? ??a?是一個常變量,是不可以被修改的,給?a?取別名為變量 b?,變量b沒有用const修飾,所以不具有常屬性,是可以被修改的,相當于權限的放大,這種情況是不允許的。正確的做法是:

int main()
{const int a = 10;const int& b = a;return 0;
}

🎀?權限縮小

int main()
{int a = 10;const int& b = a;return 0;
}

? ? ?上面代碼中,給一個普通的變量a取了一個別名b,這個b是一個常引用。這意味著,可以通過a變量去對內存中存儲的數據進行修改,但是不能通過b去修改內存中存儲的數據,但是b會跟著變。

📒2.6引用和指針的區別?

  • 引用在概念上定義一個變量的別名,指針存儲一個變量的地址
  • 引用在定義時必須初始化,指針沒有要求。
  • 引用在初始化時引用一個一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實體。
  • 沒有NULL引用,但有NULL空指針。
  • 在sizeof中的含義不同,引用結果為引用類型的大小,但指針始終是地址空間所占字節個數(32位機下占四個字節,64位機下占八個字節)。
  • 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小。
  • 有多級指針,但是沒多級引用
  • 訪問實體方式不同。指針顯式解引用,引用編譯器自己做處理。
  • 引用比指針使用起來相對更安全。

本次的內容到這里就結束啦。希望大家閱讀完可以有所收獲,同時也感謝各位讀者三連支持。文章有問題可以在評論區留言,博主一定認真認真修改,以后寫出更好的文章。你們的支持就是博主最大的動力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/165477.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/165477.shtml
英文地址,請注明出處:http://en.pswp.cn/news/165477.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于51單片機音樂盒設計( proteus仿真+程序+原理圖+PCB+報告+講解視頻)

音樂盒 主要功能&#xff1a;仿真原理圖PCB圖程序設計&#xff1a;設計報告實物圖資料清單&#xff08;提供資料清單所有文件&#xff09;&#xff1a;資料下載鏈接&#xff1a; 基于51單片機音樂盒仿真設計( proteus仿真程序原理圖PCB報告講解視頻&#xff09; 仿真圖proteus …

Python實現交易策略評價指標-收益率

1.收益率的定義 收益率幾乎是所有投資者都會關注的一個指標&#xff0c;收益率的高低決定了投資策略的賺錢能力&#xff0c;常見關于收益率的指標如下&#xff1a; 持有期收益率 持有期收益率 期末投資權益 ? 期初投資權益 期初投資權益 持有期收益率 \frac {期末投資權益…

GeoTrust SSL數字安全證書介紹

一、GeoTrust OV證書的介紹 GeoTrust OV證書是由GeoTrust公司提供的SSL證書&#xff0c;它是一種支持OpenSSL的數字證書&#xff0c;具有更高的安全性和可信度。GeoTrust是全球領先的網絡安全解決方案提供商&#xff0c;為各類用戶提供SSL證書和信任管理服務。GeoTrust OV證書…

docker國內鏡像加速

創建或修改 /etc/docker/daemon.json 文件&#xff0c;修改為如下形式 {"registry-mirrors": ["https://registry.docker-cn.com","http://hub-mirror.c.163.com","https://docker.mirrors.ustc.edu.cn"] } Docker中國區官方鏡像htt…

51單片機應用從零開始(八)·循環語句(for循環、while 語句、do‐while 語句)

51單片機應用從零開始&#xff08;七&#xff09;循環語句&#xff08;if語句&#xff0c;swtich語句&#xff09;-CSDN博客 目錄 1. 用for 語句控制蜂鳴器鳴笛次數 2. 用while 語句控制 LED 3. 用 do‐while 語句控制 P0 口 8 位 LED 流水點亮 1. 用for 語句控制蜂鳴器鳴笛…

Kafka 控制器(controller)

Kafka 控制器&#xff08;controller&#xff09; 在kafka集群中 會存在一個或者多個broker&#xff08;一個服務器就是一個broker&#xff09;&#xff0c;其中有一個broker會被選舉為控制器 kafka controller &#xff0c;負責管理整個集群中所有副本、分區的狀態&#xff0…

多語言快速排序算法

快速排序是一種高效的排序算法&#xff0c;使用分治法策略。它的基本思想是&#xff1a;選擇一個元素作為“基準”&#xff08;pivot&#xff09;&#xff0c;重新排序數列&#xff0c;所有比基準值小的元素擺放在基準前面&#xff0c;所有比基準值大的擺在基準的后面。在這個分…

python內置模塊binascii,二進制數據和ASCII字符串之間進行轉換

一、簡介 binascii是Python標準庫中的一個模塊&#xff0c;提供了在二進制數據和ASCII字符串之間進行轉換的功能。它包含了一些用于處理二進制數據的函數&#xff0c;可以進行二進制數據的編碼、解碼和轉換。 二、方法 binascii.unhexlify(hexstr)&#xff1a;將十六進制表示…

事件循環機制及常見面試題

借鑒&#xff1a; 《Javascript 忍者秘籍》第二版&#xff0c;事件循環篇 面試 | JS 事件循環 event loop 經典面試題含答案 - 知乎 (zhihu.com) 概念 主棧隊列就是一個宏任務&#xff0c;每一個宏任務執行完就會執行宏任務中的微任務&#xff0c;直到微任務全部都執行完&a…

Python 使用XlsxWriter操作Excel

在數據處理和報告生成的領域中&#xff0c;Excel 文件一直是廣泛使用的標準格式。為了讓 Python 開發者能夠輕松創建和修改 Excel 文件&#xff0c;XlsxWriter 庫應運而生。XlsxWriter 是一個功能強大的 Python 模塊&#xff0c;專門用于生成 Microsoft Excel 2007及以上版本&a…

Vue3-provide和inject

作用和場景&#xff1a;頂層組件向任意的底層組件傳遞數據和方法&#xff0c;實現跨層組件通信 跨層傳遞普通數據&#xff1a; 1.頂層組件通過provide函數提供數據 2.底層組件通過inject函數獲取數據 既可以傳遞普通數據&#xff0c;也可以使用ref傳遞響應式數據&#xff08…

批量插入SQL 錯誤 [933] [42000]: ORA-00933: SQL 命令未正確結束

使用DBeaver向【oracle數據庫】插入大量數據 INSERT INTO Student(name,sex,age,address,birthday) VALUES(Nike,男,18,北京,2000-01-01) ,(Nike,男,18,北京,2000-01-01) ,(Nike,女,18,北京,2000-01-01) ,(Nike,女,18,北京,2000-01-01) ,(Nike,男,18,北京,2000-01-01) ,(Nike…

使用Arrays.Sort并定制Comparator排序解決合并區間

合并區間-力扣算法題56題 以數組 intervals 表示若干個區間的集合&#xff0c;其中單個區間為 intervals[i] [starti, endi] 。請你合并所有重疊的區間&#xff0c;并返回 一個不重疊的區間數組&#xff0c;該數組需恰好覆蓋輸入中的所有區間 。 示例 1&#xff1a; 輸入&am…

新能源行業碳酸氫鋰純化除鈣鎂工藝

在碳酸氫鋰純化中常規的沉淀或者其它工藝不能夠滿足鈣鎂等堿土金屬的深度去除。通常采用離子交換工藝實現鈣離子、鎂離子的去除&#xff0c;以提升碳酸鋰的品質&#xff0c;但是國產樹脂在此行業應用中存在的使用量過大的問題&#xff0c;會導致設備造價偏高、廢水量太大&#…

C++二分向量算法:最多可以參加的會議數目 II

本題的其它解法 C二分算法&#xff1a;最多可以參加的會議數目 II 本文涉及的基礎知識點 二分查找算法合集 題目 給你一個 events 數組&#xff0c;其中 events[i] [startDayi, endDayi, valuei] &#xff0c;表示第 i 個會議在 startDayi 天開始&#xff0c;第 endDayi …

gitt開源項目的意義,公司為什么會對在gitt上有開源項目的人更大機會

Git是一種分布式版本控制系統&#xff0c;它可以幫助程序員管理代碼的歷史版本和協同工作。同時&#xff0c;Git也成為了開源項目的主要托管平臺之一。Git的開源項目意義重大&#xff0c;因為這種開源項目托管平臺可以幫助開發者將代碼和項目分享給全球的開發者&#xff0c;并且…

從0開始學習JavaScript--JavaScript元編程

JavaScript作為一門靈活的動態語言&#xff0c;具備強大的元編程能力。元編程是一種通過操作程序自身結構的編程方式&#xff0c;使得程序能夠在運行時動態地創建、修改、查詢自身的結構和行為。本文將深入探討JavaScript中元編程的各個方面&#xff0c;包括原型、反射、代理等…

2023亞太杯數學建模C題思路模型代碼

已完成C題思路代碼&#xff0c;文末名片獲取 C題是我們的一個數據分析問題&#xff0c;這個題目主要就是我們要去收集數據&#xff0c;清洗處理后進行分析。 問題1&#xff1a;分析影響中國新能源電動汽車發展的主要因素&#xff0c;建立數學模型&#xff0c;描述這些因素對中…

對未來新能源車測試工具的看法

汽車行業正在經歷變革的說法算是比較輕描淡寫的了&#xff0c;還記得我1983年加入這個行業時&#xff0c;行業聚焦點是引入發動機管理系統。當時還是以家庭掀背車為主的時代&#xff0c;發動機分析儀的體積像衣柜一樣大&#xff0c;還沒出現“CAN”通信協議。現在經常聽到我的導…