C++函數及其應用
一.為什么要用函數
函數是編程很重要的一部分,他能給程序帶來很多益處,也方便我們程序員編寫代碼。
-
我們知道,c和c++中使用函數,能簡化代碼量,對各個部分進行封裝,使得問題變得簡單和直觀,提高了程序的易讀性。
-
還可以提升可維護性,把一些計算或操作編成通用的函數,以供隨時調用,從而避免了代碼的重復冗長。
-
但是運用函數,就需要傳遞參數,開辟緩存、堆棧等,相比較而言,會耗一些多余的效率。
例:比較三個數的大小
#include <iostream>
using namespace std;
void func(int a,int b,int c)
{
int middle,max,small;
if(a>b){
max=a;
middle =b;
}
else{
max=b;
middle =a;
}
if(c>max){
cout << c << " " << max << " "<< middle;
}
else if(c<middle){
cout << max << " " << middle << " " <<c;
}
else{
cout << max << " " << c << " " <<middle;
}
}
int main(){
int a,b,c;
cout << "請輸入a,b,c的大小\n";
cin >> a >> b >> c;
func(a,b,c);//進行排序
return 0;
}
我們可以看出用一個func函數對三個數進行比較大小后,主函數就變得簡單清晰,程序可讀性變強。
二.函數重載
在C++中,如果需要定義幾個功能相似,而參數類型不同的函數,那么這樣的幾個函數可以使用相同的函數名,這就是“函數重載”。
兩個重載函數必須在下列一個或兩個方面有所區別,僅僅返回值類型不同是不行的。
1、函數的參數個數不同;
2、函數的參數類型不同或者參數類型順序不同。
例如,求和函數,對應不同的參數類型,可以定義如下幾個重載函數:
#include<iostream>
using namespace std;
int sum(int a=0,int b=0){
return a+b;
}
double sum(double a=0,double b=0){
return a+b;
}
float sum(float a=0,float b=0,float c=0){
return a+b+c;
}
int main(){
int x=sum(5,9);
float y=sum(2.7,5.87);
float z=sum(float(x),y,5);
cout << x << " " << y << " " << z;
return 0;
}

三.什么是值傳遞
調用時,將實參的值傳遞對應的形參,即為值傳遞。函數中對任何形參值得修改都不會改變實參變量的值。
例如經典的交換x,y的值:
#include<iostream>
using namespace std;
void swap(int x,int y){
int temp;
temp=x;
x=y;
y=temp;
cout << "swap中a和b " << x << " " << y << endl;
}
int main(){
int a,b;
cin >> a >> b;
swap(a,b);
cout << "main中a和b " << a << " " << b << endl;
return 0;
}

我們分析一下整個程序:在主函數中,實參a和b有自己的存儲空間,並且有自己的初始值。當調用函數Swap時,內存給函數的參數x和y分配存儲空間,並將a和b的值復制過來,函數執行過程中,將x和y的值進行交換,當函數執行結束之后,x和y所占用的存儲空間將被釋放,這種傳遞的方式,並不會對實參a和b的值產生影響,所以看到的a和b的值沒喲改變。
四.什么是傳引用
引用變量是變量的另一個別名,它沒有自己的存儲數據的內存位置,它訪問的是另一個變量的內存位置。對引用變量作出的任何更改,實際上都是對它所引用的變量內存位置中存儲數據的更改。
我們用傳值的程序,把void swap(int a,int b){...}改為void swap(int &a,int &b){...}。可以看出,在main函數中,ab的值發生了改變。
#include<iostream>
using namespace std;
void swap(int &x,int &y){
int temp;
temp=x;
x=y;
y=temp;
cout << "swap中a和b " << x << " " << y << endl;
}
int main(){
int a,b;
cin >> a >> b;
swap(a,b);
cout << "main中a和b " << a << " " << b << endl;
return 0;
}

五.如何編寫遞歸函數
說起遞歸,不得不說他很想小時候聽過的故事:從前有座山,山里有座廟,廟里有個和尚,和尚在講故事,從前有座山,山里有座廟,廟里有個和尚,和尚在講故事,從前有座山...
網上流傳的這個表情包,他也很好的詮釋了什么叫遞歸。遞歸的原理就是通過棧機制來把遞歸過程中的函數,以及它的符號入棧和出棧,並在出棧過程中對這些符號和函數返回值等進行運算。


我們再來看看不好理解的漢羅塔問題:該問題是在一塊銅板裝置上,有三根桿(編號A、B、C),在A桿自下而上、由大到小按順序放置n個金盤。游戲的目標:把A桿上的金盤全部移到C桿上,並仍保持原有順序疊好。

研究這個問題我們就要利用遞歸思想,要把n個盤子從A->C,我們就要先把n-1個盤子從A->C->B,再把A最后一個盤子從A->C,緊接着再把n-1個盤子從B->A->C上。
再具體一點,就是我們要把前n-2個盤子移動到B上,n-3個盤子移動到C上,以此類推,最終推出第一個盤子應該放在B或者C上。由於遞歸是倒序輸出,我們在前面已經記錄過倒數第二個盤子應放在哪里,再把前兩個盤子疊加在一起,在記錄中找出倒數第三個盤子放在哪里,以此類推,一直到n-1個盤子把他們疊在一起就完成了第一步(我們就要先把n-1個盤子從A->C->B),第一部move(n-1,x,z,y)遞歸函數調用徹底結束。
這時中間語句直接輸出第n個盤子從A->C,完成第二部。
完成后到達第三個move函數, move(n-1,y,x,z),這個函數的作用和第一個函數的作用相似,只是改變了起始針,中間針和目的針。原理和第一個遞歸函數相同。由於我們知道第一個遞歸函數和第二個遞歸函數是相似的,步驟也是相同的,可以得出漢諾塔的步數一定是個奇數(加上中間直接移動最后一個盤子的一步)。
具體代碼:
//棧之遞歸漢諾塔問題
#include<iostream>
using namespace std;
void move(int n,char x,char y,char z){
if(n==1)
cout << x <<"->" << z << endl; //把x放到z上
else{
move(n-1,x,z,y);
cout << x <<"->" << z << endl;
move(n-1,y,x,z);
}
}
int main(){
move(3,'x','y','z');
return 0;
}

