C++示例分析內聯函數與引用變量及函數重載的使用
1.內聯函數
1.1為什麼使用內聯函數
- 減少上下文切換,加快程序運行速度。
- 是對C語言中的宏函數的改進。
1.2語法
#include<iostream> using namespace std; inline double square(double x){ return x*x; } int main(){ cout<<square(2.2)<<endl; }
其實就是在函數聲明或者定義前加上關鍵字inline
。
2.引用變量
2.1為什麼要使用引用變量
- 主要用途是用作函數的形參。通過引用變量做參數,函數將使用原始數據,而不是其副本。
- 高效。
2.2語法
引用實際上就是定義一個別名。看看下面代碼:
#include<iostream> using namespace std; int main(){ int a=50; int &b=a;//定義並初始化,這裡b是a的引用。 cout<<"a:"<<a<<endl; cout<<"b:"<<b<<endl; cout<<"address of a:"<<&a<<endl; cout<<"address of b:"<<&b<<endl; b=100; cout<<"a:"<<a<<endl; cout<<"b:"<<b<<endl; int c=200; b=c;//試圖將b作為c的引用。行不通。 cout<<"a:"<<a<<endl; cout<<"b:"<<b<<endl; cout<<"c:"<<c<<endl; cout<<"address of a:"<<&a<<endl; cout<<"address of b:"<<&b<<endl; cout<<"address of c:"<<&c<<endl; }
a:50
b:50
address of a:0x61fe14
address of b:0x61fe14
a:100
b:100
a:200
b:200
c:200
address of a:0x61fe14
address of b:0x61fe14
address of c:0x61fe10
a和b的數據地址是一樣的,這說明b相當於a的別名,我們改變b的值,也會改變a的值,而且後面我們試圖將b轉變為c的引用,但是行不通,b=c這個代碼做的是賦值語句,相當於a=c.
引用和指針的區別
引用在聲明的時候必須初始化
int &b;
這句話是不允許的。
引用的本質就是指針常量。因為引用變量一旦初始化就不能更改。
int &b=a
和int* const p=&a
這兩句中b
和*p
是一模一樣的。
引用作為函數參數
#include<iostream> using namespace std; void swap(int &a,int &b){ int c; c=a; a=b; b=c; } int main(){ int a=2; int b=3; swap(a,b); cout<<a<<b<<endl; }
可以看出把引用作為參數的函數,隻需在聲明時,把參數設置成引用即可。
臨時變量
試想一下,在參數傳遞過程中,我們把常數或者錯誤類型的實參,傳給引用參數,會發生什麼?這個引用參數會變成這個實參的引用嗎?顯然不會,因為常數不能修改,引用是錯誤的,正如int &a=2;
會報錯一樣;錯誤類型的實參,也不能直接引用。
為瞭解決這個事,c++允許臨時變量的產生。但是隻有const引用才會產生臨時變量,const引用不允許變量發生賦值。
總結來說,臨時變量的產生條件是,在傳參給const引用參數時:
實參不是左值.(左值指的是const變量 和 常規變量。)
實參類型不正確且可類型轉換。
所以說,為瞭使得引用參數傳遞的兼容性和安全性,請多使用const。
#include<iostream> using namespace std; double square(const double &a){ return a*a*a; } int main(){ int a=3; cout<<square(3+a)<<endl; }
可以看出來這裡square函數可以接受非左值,類型錯誤的實參。
你可能覺得這樣做很復雜,直接使用按值傳參就行瞭。double square(double a)
和double square(const double &a)
,從效果來說,這兩一樣,但是我們使用第二種傳參的好處是高效,試想一下我們同時傳一個double類型的變量,const引用傳參不需要數據的拷貝,更快。
右值引用
采用 && 來對右值做引用,這麼做的目的是用來實現移動語義。
#include<iostream> using namespace std; int main(){ double a=3.1; double && b=a*1.2+2.3; cout<<b<<endl; b=3; cout<<a<<endl; cout<<b<<endl; }
6.02
3.1
3
結構引用
引用非常適合於結構和類
#include<iostream> using namespace std; struct apple { string name; double weight; }; apple & swap(apple &a, apple &b){ apple temp; temp=a; a=b; b=temp; return a; } int main(){ apple a={"Bob",230}; apple b={"Alice",190}; swap(a,b); cout<<"a:"<<endl<<"name:"<<a.name<<endl<<"weight:"<<a.weight<<endl<<endl; cout<<"b:"<<endl<<"name:"<<b.name<<endl<<"weight:"<<b.weight<<endl<<endl; swap(swap(a,b),b); cout<<"a:"<<endl<<"name:"<<a.name<<endl<<"weight:"<<a.weight<<endl<<endl; cout<<"b:"<<endl<<"name:"<<b.name<<endl<<"weight:"<<b.weight<<endl<<endl; swap(swap(swap(a,b),b),b); swap(swap(a,b),b); cout<<"a:"<<endl<<"name:"<<a.name<<endl<<"weight:"<<a.weight<<endl<<endl; cout<<"b:"<<endl<<"name:"<<b.name<<endl<<"weight:"<<b.weight<<endl<<endl; }
a:
name:Alice
weight:190b:
name:Bob
weight:230a:
name:Alice
weight:190b:
name:Bob
weight:230a:
name:Bob
weight:230b:
name:Alice
weight:190
swap()函數的返回值是一個引用變量,所以swap(swap(swap(a,b),b),b)
是合法的,且它等價於swap(a,b)
。
為何要返回引用?高效。 因為傳統返回機制,會把返回結果復制到一個臨時位置。 但是應該避免返回 函數終止時不再存在的內存單元引用。例如避免返回臨時變量的引用。
2.3對於C語言的改進
- 用const引用傳參傳遞 代替 按值傳遞。
- 對於要修改原始數據的函數,采用引用傳參方式。
3. 函數重載
3.1默認參數
默認參數指的是函數調用中省略瞭實參時自動使用的一個值。
如何設置默認值?必須通過函數原型。 例如這裡的void display(int a,int n=999);
這裡n=999 就是默認參數 默認參數的作用是,不給這個參數傳參時,他會采用默認值。
#include<iostream> using namespace std; void display(int a,int n=999); int main(){ display(1); display(3,31); } void display(int a,int n){ cout<<a<<endl; cout<<n<<endl; }
1
999
3
31
3.2函數重載
默認參數能讓我們使用不同數目的參數調用同一個函數,而函數重載能讓我們使用多個同名的函數。
函數重載的關鍵是函數的參數列表–也稱函數特征標。如果兩個函數的名字和特征標相同,那麼這兩個函數就完全相同。C++允許定義名稱相同,函數特征標不同的函數,這就是所謂的函數重載。
#include<iostream> using namespace std; struct apple{ string name; double weight; }; void print(int); void print(double); void print(char *); void print(apple &a,string str="apple",double w=100); int main(){ int a=2; double b=3.14; char c[10]="hello!"; apple d; print(a); print(b); print(c); print(d); print(d,"Alice",250); } void print(int a){ cout<<"int ="<<a<<endl; } void print(double a){ cout<<"double ="<<a<<endl; } void print(char * a){ cout<<"char* ="<<a<<endl; } void print(apple &a,string str,double b){ a.name=str; a.weight=b; cout<<"the name:"<<a.name<<endl; cout<<"the weight:"<<a.weight<<endl; }
int =2
double =3.14
char* =hello!
the name:apple
the weight:100
the name:Alice
the weight:250
可以看出來print
函數有多個重載,現代編譯器會根據你傳遞的參數類型,而選擇最匹配的函數。
關於函數重載的一些細節
- 類型引用和類型本身視為同一個特征標,例如
double cube(double x);
和double cube(double &x);
是不能共存的。 - 匹配函數時,會區分const和非const變量,例如
void display(char* a);
和void display(const char* a);
是函數重載。 - 請記住是特征標,而不是函數類型使得可以對函數進行重載。例如
long gronk(int,float);
和double gronk(int,float);
是不能共存的。
函數重載的shortcoming
函數重載在實現同函數名多種功能的同時,也應當付出代價。
標準類型轉化、強制匹配能力下降。
#include<iostream> using namespace std; void print(double); void print(char *); int main(){ int a=2; double b=3.14; char c[10]="hello!"; print(a); print(b); print(c); } void print(double a){ cout<<"double ="<<a<<endl; } void print(char * a){ cout<<"char* ="<<a<<endl; }
double =2
double =3.14
char* =hello!
可以看出來這裡print(a)
這裡a是int類型,編譯器會將其類型轉化成double,然後調用對應函數。
但是,我們稍微改動一下代碼
#include<iostream> using namespace std; void print(int); void print(double); void print(char *); int main(){ int a=2; double b=3.14; char c[10]="hello!"; print(a); print(b); print(c); print(12L); } void print(int a){ cout<<"int ="<<a<<endl; } void print(double a){ cout<<"double ="<<a<<endl; } void print(char * a){ cout<<"char* ="<<a<<endl; }
這段代碼中print(12L);
會報錯,因為12L
是long類型的常量,如果我們試著強制匹配會發現,12L
既可以轉化成int類型,也可以轉化成double類型,從而編譯器不知道到底調用哪個函數。
不要濫用函數重載
僅當函數基本執行相同的任務,但使用不同類型的數據時,才應當使用函數重載。
到此這篇關於C++示例分析內聯函數與引用變量及函數重載的使用的文章就介紹到這瞭,更多相關C++內聯函數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!