首頁 > 軟體

C++範例分析行內函式與參照變數及函數過載的使用

2022-08-22 14:00:33

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=aint* 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:190

b:
name:Bob
weight:230

a:
name:Alice
weight:190

b:
name:Bob
weight:230

a:
name:Bob
weight:230

b:
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函數有多個過載,現代編譯器會根據你傳遞的引數型別,而選擇最匹配的函數。

關於函數過載的一些細節

  1. 型別參照和型別本身視為同一個特徵標,例如double cube(double x);double cube(double &x);是不能共存的。
  2. 匹配函數時,會區分const和非const變數,例如 void display(char* a);void display(const char* a);是函數過載。
  3. 請記住是特徵標,而不是函數型別使得可以對函數進行過載。例如 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++行內函式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com