C++中函數(shù)重載與引用的操作方法
1. 函數(shù)重載
在中文語境中有些詞語它就是一詞多義的,人們通過上下文來判斷詞語的意思,即該詞被重載了。
就比如說:以前有個笑話,我們國家有兩個體育項目大家根本不用看,也不用擔(dān)心。一個是乒乓球,一個是足球。前者"誰也贏不了"后者"誰也贏不了"。
1.1 函數(shù)重載概念
函數(shù)重載:是函數(shù)的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數(shù),這些函數(shù)的形參列表(參數(shù)個數(shù)或者類型或者類型順序)不同,常用來處理功能類似數(shù)據(jù)類型不同的問題。
#include <iostream> using namespace std; //1.參數(shù)類型不同 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; } //2.參數(shù)個數(shù)不同 void test() { cout<<"test()"<<endl; } void test(int a) { cout<<"test(int a)"<<endl; } //3.參數(shù)類型順序不同 void test2(int a,char b) { cout<<"test2(int a,char b)"<<endl; } void test2(char b,int a) { cout<<"test2(char b,int a)"<<endl; } int main() { add(1,2); add(1.1,2.2); test(); test(100); test2(10,'a'); test2('a',10); return 0; }
1.2 C++支持函數(shù)重載的原理–名字修飾(name Mangling)
為什么C++支持函數(shù)重載,而C語言不支持函數(shù)重載呢?
在C/C++中,一個程序要運(yùn)行起來,需要經(jīng)歷以下幾個階段:預(yù)處理、編譯、匯編、鏈接。
- 實際項目中通常是由多個頭文件和多個源文件構(gòu)成的,而通過C語言階段學(xué)習(xí)的編譯鏈接,我們可以知道,【當(dāng)前a.cpp中調(diào)用了b.cpp中定義的Add函數(shù)時】,編譯后鏈接前,a.o的目標(biāo)文件中沒有Add的函數(shù)地址,因為Add是在b.cpp中定義的,所以Add的地址在b.o中。那么怎么辦呢?
- 所以鏈接階段就是專門處理這種問題的,鏈接器看到a.o調(diào)用Add,但是沒有Add的地址,就會到b.o的符號表中找Add的地址,然后鏈接到一起。
- 那么鏈接時,面對Add函數(shù),鏈接器會使用哪個名字去找呢?這里每個編譯器都有自己的函數(shù)名修飾規(guī)則。
- 由于windows下vs的修飾規(guī)則過于復(fù)雜,而Linux下g++的修飾規(guī)則簡單易懂,下面我們使用了g++演示這個修飾后的名字。
- 通過下面我們可以看到gcc的函數(shù)修飾后名字不變。而g++的函數(shù)修飾后變成【__Z+函數(shù)長度+函數(shù)名+類型首字母】
- 采用C語言編譯器編譯后的結(jié)果:
//file.c中的代碼 #include <stdio.h> int Add(int a,int b) { return a+b; } void test(int a,double b,int*p) { //... } int main() { Add(10,20); test(1,2,0); return 0; }
結(jié)論:在Linux下,采用gcc編譯完成后,函數(shù)名字的修飾沒有發(fā)生改變。
采用C++編譯器編譯后結(jié)果:
結(jié)論:在Linux下,采用g++編譯完成后,函數(shù)名字的修飾發(fā)生改變,編譯器將函數(shù)參數(shù)類型信息添加到修改后的名字中。
windows下名字修飾規(guī)則
總結(jié):對比Linux會發(fā)現(xiàn),Windows下vs編譯器對函數(shù)名字修飾規(guī)則相對復(fù)雜難懂,但道理類似。
6. 通過這里就理解了C語言沒辦法支持重載,因為同名函數(shù)沒辦法區(qū)分。而C++是通過函數(shù)修飾規(guī)則來區(qū)分地,只要參數(shù)不同,修飾出來地名字就不一樣,那么就可以支持重載了。
7. 如果兩個函數(shù)函數(shù)名和參數(shù)是一樣的,返回值不同是不構(gòu)成重載的,因為調(diào)用時編譯器沒辦法區(qū)分。
2. 引用
2.1 引用的概念
引用不是新定義一個變量,而是給已存在變量取一個別名,編譯器不會為引用變量開辟內(nèi)存空間,它和它引用的變量共用同一塊內(nèi)存空間。
就像外號一樣,盡管名字不同但人都是一個人。
語法:
類型& 引用變量名(對象名) = 引用實體
#include <cstdio> void test() { int a = 10; int& ra = a;//定義引用類型 printf("%p\n", &a); printf("%p\n", &ra); } int main() { test(); return 0; } //打印結(jié)果: /* 009EFDE0 009EFDE0 */
注意:引用類型必須和引用實體是同種類型的。
2.2 引用的特性
- 引用在定義時必須初始化。
- 一個變量可以有多個引用。
- 引用一旦引用一個實體,就不能再引用其他實體。
void test() { int a = 10; //int& ra;//引用在定義時必須初始化,否則報錯 int& ra = a; int& rra = a; printf("%p\n", &a); printf("%p\n", &ra); printf("%p\n", &rra); } //打印結(jié)果: /* 00AFF9A0 00AFF9A0 00AFF9A0 */
2.3 常引用
void testconstref() { const int a = 10; //int& ra = a;//該語句編譯時會出錯,a為常量 const int& ra = a; //int& b = 10;//該語句編譯時會出錯,10為常量 const int& b = 10; double d = 3.14; //int& rd = d;該語句編譯時會出錯,類型不同 const int& rd = d; }
2.4 使用場景
做參數(shù)
void Swap(int& left,int& right) { int temp = left; left = right; right = temp; }
做返回值
int& Count() { static int n = 0; n++; //... return n; }
觀察下來代碼,會輸出什么結(jié)果?
#include <iostream> using namespace std; int& add(int a, int b) { int c = a + b; return c; } int main() { int& ret = add(2, 3); add(4, 5); cout << "add(2,3) is :" << ret << endl; return 0; } //打印結(jié)果 /* add(2,3) is :9 */
函數(shù)運(yùn)行時,系統(tǒng)需要給函數(shù)開辟獨立的??臻g,用來保存函數(shù)的形參,局部變量以及一些寄存器信息等。
函數(shù)運(yùn)行結(jié)束后,該函數(shù)對應(yīng)的??臻g就被系統(tǒng)回收了。
空間被回收指該??臻g暫時不能使用,但是內(nèi)存還在,比如:上課申請教室,上完課之后教師歸還給學(xué)校,但是教室本身還在,不能說歸還后,教室就消失了。
注意:如果函數(shù)返回了,出了函數(shù)作用域,如果返回對象還在(沒有還給系統(tǒng)),則可以使用引用返回,如果已經(jīng)還給系統(tǒng)了,則必須使用傳值返回。
2.5 傳值、傳引用效率比較
以值作為參數(shù)或者返回類型,在傳參和返回期間,函數(shù)不會直接傳遞實參或者將變量本身直接返回,而是傳遞實參或者返回變量的一份臨時拷貝,因此用值作為參數(shù)或者返回類型,效率是非常低下的,尤其是當(dāng)參數(shù)或返回類型非常大時,效率就更低。
演示:效率對比,值和引用作為參數(shù)類型的性能對比
#include <iostream> #include <ctime> using namespace std; struct A { int a[10000]; }; void TestFunc(A a) { } void TestFunc2(A& a) { } int main() { A a; //以值作為函數(shù)參數(shù) size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) { TestFunc(a); } size_t end1 = clock(); //以引用作為函數(shù)參數(shù) size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) { TestFunc2(a); } size_t end2 = clock(); //分別計算兩個函數(shù)運(yùn)行結(jié)束后的時間 cout << "Testfunc(A) time:" << end1 - begin1 << endl; cout << "Testfunc2(A&) time:" << end2 - begin2 << endl; return 0; } //打印結(jié)果 /* Testfunc(A) time:12 Testfunc2(A&) time:0 (單位毫秒) */
演示:值和引用的作為返回類型的性能對比
#include <iostream> #include <ctime> using namespace std; struct A { int a[10000]; }; struct A a; //值返回 A TestFunc() { return a; } //引用返回 A& TestFunc2() { return a; } int main() { //以值作為函數(shù)的返回類型 size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) { TestFunc(); } size_t end1 = clock(); //以引用作為函數(shù)的返回類型 size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) { TestFunc2(); } size_t end2 = clock(); //分別計算兩個函數(shù)運(yùn)行結(jié)束后的時間 cout << "Testfunc(A) time:" << end1 - begin1 << endl; cout << "Testfunc2(A&) time:" << end2 - begin2 << endl; return 0; } //打印結(jié)果 /* Testfunc(A) time:25 Testfunc2(A&) time:1 */
結(jié)論:通過上述的代碼可以清楚的發(fā)現(xiàn),傳值和傳引用在作為傳參以及返回值類型上效率相差很大。
2.6 引用和指針的區(qū)別
在語法層面上呢,引用就是一個別名,沒有獨立空間,和其被引用體共用一塊空間。
#include <iostream> using namespace std; int main() { int val = 100; int& rval = val; cout<<"&val = "<<&val<<endl; cout<<"&rval = "<<&rval<<endl; return 0; } //打印結(jié)果 /* &val = 003AFB98 &rval = 003AFB98 */
但是呢,在底層方面實際是由空間的,因為引用是按照指針方式來實現(xiàn)的。
#include <iostream> using namespace std; int main() { int a = 10; int& ra = a; ra = 20; int* pa = &a; *pa = 20 return 0; }
反匯編:
可以看到操作是類似的。
引用和指針的不同點:
- 引用概念上定義一個變量的別名,指針存儲一個變量地址。
- 引用在定義時必須初始化,指針就沒有要求。
- 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以再任何時候指向任何一個同類型實體。
- 沒有NULL引用,但是又NULL指針
- 再sizeof中含義不同:引用結(jié)果為引用類型的大小,但是指針始終是地址空間所占字節(jié)數(shù)個(根據(jù)所在平臺確定,如32位平臺占4個字節(jié))
- 引用自加即引用的實體加1,指針自加即指針向后偏移一個類型的大小。
- 由多級指針,但是沒有多級引用。
- 訪問實體方式不同,指針需要顯示解引用,引用編譯器自己處理。
- 引用比指針使用起來相對安全。
到此這篇關(guān)于C++中函數(shù)重載與引用的的文章就介紹到這了,更多相關(guān)C++函數(shù)重載與引用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言進(jìn)階之字符串查找?guī)旌瘮?shù)詳解
字符串是一種非常重要的數(shù)據(jù)類型,但是C語言不存在顯式的字符串類型,C語言中的字符串都以字符串常量的形式出現(xiàn)或存儲在字符數(shù)組中,下面這篇文章主要給大家介紹了關(guān)于C語言進(jìn)階之字符串查找?guī)旌瘮?shù)的相關(guān)資料,需要的朋友可以參考下2023-01-01C語言大小端模式、判斷大小端、大小端轉(zhuǎn)換方法詳解
這篇文章主要介紹了C語言大小端模式、判斷大小端、大小端轉(zhuǎn)換的相關(guān)資料,大端和小端是數(shù)據(jù)在內(nèi)存中的存儲方式,大端模式下高字節(jié)存于低地址,小端模式則相反,大小端問題由數(shù)據(jù)類型多字節(jié)存儲引起,不同選擇形成不同存儲模式,需要的朋友可以參考下2024-10-10基于C++ list中erase與remove函數(shù)的使用詳解
本篇文章是對C++ list中erase與remove函數(shù)的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05