天天看點

C++入門之函數重載、引用、内聯函數

函數重載:

函數重載定義:

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

舉個栗子:

#include<iostream>
#include<stdlib.h>
using namespace std;
int ADD(int a,int b)
{
	int c;
	c=a+b;
	return c;
}
int ADD(int a,int b,int c)
{
	int d;
	d=a+b+c;
	return d;
}
int main()
{
	int ret1,ret2;
	ret1=ADD(1,2);
	ret2=ADD(1,2,3);
	printf("%d\n",ret1);
	printf("%d\n",ret2);
	system("pause");
	return 0;
}
           

這就是一個函數重載,兩個同名的ADD函數,功能是做加法,第一個有兩個參數,求兩個數字的和。而第二個有三個參數,求三個數字之和。因為他們滿足,在同一作用域中,同名的函數,但參數個數卻不同,是以構成函數重載。

看一下結果驗證一下。
C++入門之函數重載、引用、内聯函數

而且無法重載僅按傳回值類型區分的函數。

這裡可能會有一個誤區:
#include<iostream>
#include<stdlib.h>
using namespace std;
short Add(short left, short right)
{ 
	return left+right; 
}
 
short Add( short right,short left) 
{ 
	return left+right; 
}
int main()
{
	short ret1,ret2;
	printf("%d %d",ret1,ret2);
	system("pause");
	return 0;
}
           
C++入門之函數重載、引用、内聯函數

會有人問,函數參數的順序不是不一樣嗎?為什麼不構成重載?

原因,構成函數重載的條件是,一:參數個數不同 、二:參數類型不同、三:形參順序不同。可以看到因為參數的類型相同了,是以不能構成重載。
C++入門之函數重載、引用、内聯函數

可以看到當參數類型不同時,就可以構成重載,而參數的順序不同之前還要考慮參數的類型不能相同。

總結為:

1.當函數參數個數不同即構成重載。

2.當參數類型不同時,就可構成重載。

3.當參數的順序不同,且參數的類型也不能相同,才能構成重載。

引用:

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

比如:你在家的時候,你媽媽可能會叫你“王大錘”,而你的同學叫你“老王”,而你的真名可能叫王小萌。雖然你有這麼多名字,但你家戶口本也就隻有一個你的名字,國家是不會給你多弄一張身份證的。

具體如何引用呢?

類型& 引用變量名(對象名) = 引用實體

驗證:

#include<iostream>
#include<stdlib.h>
using namespace std;
void Test()
{
	int a=10;
	int& A=a;//引用
	printf("%d\n",a);
	printf("%d\n",A);
	printf("%p\n",&a);//%p列印位址
	printf("%p\n",&A);
}
int main()
{
	Test();
	system("pause");
	return 0;
}
           
C++入門之函數重載、引用、内聯函數

可以看到A的值也是10,并且通過列印這兩個變量,可以發現這兩個變量位址相同,a和A共用一塊記憶體空間。

注意:引用類型必須和引用實體是同種類型的。
引用特性:
  1. 引用在定義時必須初始化
  2. 一個變量可以有多個引用
  3. 引用一旦引用一個實體,再不能引用其他實

具體執行個體看代碼:

#include<iostream>
#include<stdlib.h>
using namespace std;
void test()
{
	int a=3;
	int b=6;
	int& A=a;//初始化
	//int& A;//報錯顯示 引用變量"A"需要初始值設定項
	int& ra=a;//可以有多個引用
	int& A=b;//這裡會報錯
	printf("%d %d %d %d",a,b,A,ra);
}
int main()
{
	test();
	system("pause");
	return 0;
}
           

看一下運作結果

先看一下報錯的部分:
C++入門之函數重載、引用、内聯函數

由此可見,引用一旦引用一個實體,再不能引用其他實體。

修改後:

C++入門之函數重載、引用、内聯函數
常引用

注意的一些問題:

void test1()
{
	const int a=10;
	//int& A=a;//這裡會報錯,顯示:将"int &"類型的引用
	//綁定到"const int"類型的初始值設定項時,限定符被丢棄。
	//這裡涉及到const常量和普通的常量之間的調用關系。
	//解決方法是
	const int& A=a;
	//int& b=1;//這裡報錯,非常量引用的初始值必須為左值,即b不能為常量。
	double b=1.2;
	//int& B=b;//報錯,類型不同無法引用
	double& B=b;
	const int& rb=b;
}
           
應用場景:

1.做參數

我們都知道,如果要交換兩個變量的内容在傳參時,需要傳位址過去,而且需要解引用才能交換兩個變量的值。如:

void test2(int*left,int*right)
{
	int temp=*left;
	*left=*right;
	*right=temp;
}
int  main()
{
	int left=1;
	int right=2;
	test2(&left,&right);
	cout<<left<<" "<<right<<endl;
	system("pause");
	return 0;
}
           

但如果用變量的引用做參數,就會很好的解決這個問題,不用傳位址過去,因為引用和實體所占得是同一塊記憶體空間。

void test2(int& left,int& right)
{
	int temp=left;
	left=right;
	right=temp;
}
int  main()
{
	int left=1;
	int right=2;
	test2(left,right);
	cout<<left<<" "<<right<<endl;
	system("pause");
	return 0;
}
           
運作結果如下:
C++入門之函數重載、引用、内聯函數

2.做傳回值

先看一段代碼:

#include<iostream>
#include<stdlib.h>
using namespace std;
int Add(int a,int b) 
{
	int c;
	c=a+b;
	return c;
}
int main()
{
	int ret=Add(12,23);
	Add(23,24);
	cout<<ret<<endl;
	system("pause");
	return 0;
}

           
C++入門之函數重載、引用、内聯函數

這是一個簡單的求和函數,當引用用作函數傳回值會發生什麼呢?

猜一下下面代碼的列印結果:

int& Add(int a,int b) 
{
	int c;
	c=a+b;
	return c;
}
int main()
{
	int& ret=Add(12,23);
	Add(23,24);
	cout<<ret<<endl;
	system("pause");
	return 0;
}
           
可以看到,列印結果是47,也就是說列印的是Add(23,24)這個的和,這是為什麼呢?
C++入門之函數重載、引用、内聯函數

再加一個求和:

C++入門之函數重載、引用、内聯函數

這是因為:

如果函數傳回時,離開函數作用域後,其棧上空間已經還給系統,是以不能用棧上的空間作為引用類型 傳回。如果以引用類型傳回,傳回值的生命周期必須不受函數的限制(即比函數生命周期長)

也就是int & ret 不再受Add(12,23)的限制了,這個傳回值的生命周期比函數的生命周期還要長,是以每次調用這個函數,它的傳回值都一直還在。

傳值、傳引用效率比較:

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

寫段代碼測試一下:
#include <time.h> 
#include<iostream>
#include<stdlib.h>
using namespace std;
struct A 
{  
	int a[10000];
};
void TestFunc1(A a) 
{} 
void TestFunc2(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();// 分别計算兩個函數運作結束後的時間
    //值和引用的作為傳回值類型的性能比較 
    cout << "以值作為函數參數time:"<< end1 - begin1 << endl;  
	cout << "以引用作為函數參數time:" << end2 - begin2 << endl; 
}
// 運作多次,檢測值和引用在傳參方面的效率差別 
int main() 
{    
	for (int i = 0; i < 10; ++i)    
	{        
		TestRefAndValue();    
	}  
	system("pause");
	return 0; 
}
           

運作結果如下:

C++入門之函數重載、引用、内聯函數

很明顯可以看到以引用作為函數參數效率會比較高。

另外,以引用作為傳回值也是一樣,引用的效率高。

傳值和指針在作為傳參以及傳回值類型上效率相差很大

引用和指針的差別:
雖然說引用隻是一個别名,指向的是同一塊的記憶體空間,概念上引用是沒有獨立空間的,但是在底層實作上實際是有空間的,因為引用是按照指針方式來實作的

而他們有差別嗎?

1.引用在定義時必須初始化,指針沒有要求

2. 引用在初始化時引用一個實體後,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實 體

3. 沒有NULL引用,但有NULL指針

4. 在sizeof中含義不同:引用結果為引用類型的大小,但指針始終是位址空間所占位元組個數(32位平台下占4 個位元組)

5. 引用自加即引用的實體增加1,指針自加即指針向後偏移一個類型的大小

6. 有多級指針,但是沒有多級引用

7. 通路實體方式不同,指針需要顯式解引用,引用編譯器自己處理

8. 引用比指針使用起來相對更安全

内聯函數(了解即可)

定義:

以inline修飾的函數叫做内聯函數,編譯時C++編譯器會在調用内聯函數的地方展開,沒有函數壓棧的開銷, 内聯函數提升程式運作的效率

特性:

1. inline是一種以空間換時間的做法,省去調用函數額開銷。是以代碼很長或者有循環/遞歸的函數不适宜使 用作為内聯函數。

2. inline對于編譯器而言隻是一個建議,編譯器會自動優化,如果定義為inline的函數體内有循環/遞歸等 等,編譯器優化時會忽略掉内聯。

3. inline不建議聲明和定義分離,分離會導緻連結錯誤。因為inline被展開,就沒有函數位址了,連結就會找不到

繼續閱讀