c++筆記_引用
前言
複合型別(compound type)是指基於其他型別定義的型別。
一、引用是什麼?
引用(reference)為物件起了另外一個名字,引用型別引用(refers to)另外一種型別。通過將宣告符寫成&d的形式來定義引用型別,其中d是宣告的變數名:
程式碼如下(示例):
int ival = 1024;
int &refVal = ival; //refVal指向ival(refVal是ival的另一個名字)
int &refVal2; //錯誤:引用必須被初始化
在初始化變數中時,初始值會被拷貝到新建的物件中。然而定義時,程式把引用和它的初始值繫結在一起,而不是將初始值拷貝給引用。所以一旦初始化完成,引用將和它的初始值物件一直繫結在一起。因為無法令引用重新繫結到另外一個物件,因此引用必須初始化。
ps:C++11中新增了一種引用:所謂的“右值引用”,這種引用主要用於內建類。嚴格來說當我們使用術語“引用”時,指的其實是“左值引用”。
1.引用即別名
定義了一個引用之後,對其進行的所有操作都是在與之繫結的物件上進行的:
#include<iostream>
using namespace std;
int main(void)
{
int ival = 1024;
int &refVal = ival;
refVal = 2; //把2賦給refVal指向的物件,此處是給了ival
int li = refVal; //與li = ival執行結果一樣(li = 2)
cout<<"ival="<<ival<<endl;
cout<<"refVal="<<refVal<<endl;
cout<<"li="<<li<<endl;
cout<<"&ival="<<&ival<<endl; //列印變數ival的記憶體地址
cout<<"&refVal="<<&refVal<<endl; //列印引用refVal的記憶體地址
return 0;
}
程式碼輸出:
ival=2
refVal=2
li=2
&ival=0x7ffeefbff3f4 //ival和refVal是同一塊記憶體地址,所以可以理解ival完全等價與refVal
&refVal=0x7ffeefbff3f4
所以可以理解成引用就是為一個已經存在的物件起的另外一個名字。
二、引用的定義
允許在一條語句中定義多個引用,其中每個引用識別符號都必須以符號&開頭:
int i = 1024, i2 = 2048; //i和i2都是int
int &r = i, r2 = i2; //r是一個引用,與i繫結在一起,r2是int
int i3 = 1024, &ri = i3; //i3是int,ri是一個引用,與i3繫結在一起
int &r3 = i3, &r4 = i2; //r3和r4都是引用
大部分情況下引用都型別都要和與之繫結的物件嚴格匹配。而且,引用只能繫結在物件上,而不能與字面值或某個表示式的計算結果繫結在一起。
int &refVal4 = 10; //錯誤:引用型別的初始值必須是一個物件
double dval = 3.14;
int &refVal5 = dval; //錯誤:引用的型別的初始值必須是int物件
三、用引用給陣列取個名字
當然也可以用引用給陣列取個別名,具體方法如下:
#include<iostream>
using namespace std;
int main(void)
{
int arr[5] = {1,2,3,4,5};
//目的:給陣列arr取個別名
int (&my_arr)[5] = arr; //my_arr就是陣列arr的別名
//列印陣列arr元素
for(int i=0;i<5;i++)
cout<<arr[i]<<endl;
cout<<"-------"<<endl;
//列印陣列my_arr元素
for(int b=0;b<5;b++)
cout<<my_arr[b]<<endl;
cout<<"陣列arr的記憶體地址:"<<&arr<<endl;
cout<<"陣列my_arr的記憶體地址:"<<&my_arr<<endl;
return 0;
}
程式碼輸出結果:
1
2
3
4
5
-------
1
2
3
4
5
陣列arr的記憶體地址:0x7ffeefbff480
陣列my_arr的記憶體地址:0x7ffeefbff480
四、配合typedef使用引用
首先簡單介紹下typedef是什麼?就是為型別建立個新的名字。
某些場景下,我們也可以配合typedef來使用引用,具體方法如下:
#include<iostream>
using namespace std;
int main(void)
{
int arr[5] = {1,2,3,4,5};
typedef int TYPE_ARR[5]; //TYPE_ARR就是一個陣列型別(有5個int型別元素)
//用typedef給陣列型別取個別名
TYPE_ARR &myarr = arr; //myarr就是陣列arr的別名
for(int i=0;i<5;++i)
cout<<myarr[i]<<endl;
return 0;
}
四、引用作為函式引數
引用也可以用作函式引數。假設,我們需要交換a和b兩個數的位置,在不用引用的情況下會輸出什麼呢?
#include<iostream>
using namespace std;
//不使用引用進行交換
void swap(int a,int b)
{
int tmp; //定義變數tmp用於存放a的值
tmp = a;
a = b;
b=tmp;
}
int main(void)
{
int a=1;
int b=2;
swap(a,b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;
return 0;
}
程式碼輸出:
a=1 b=2
從程式碼輸出結果來看,a和b還是原來的引數,並沒有交換到。那麼我們換做引用的方式處理看看結果會發生什麼?
#include<iostream>
using namespace std;
//函式引數不使用引用
void swap(int a,int b)
{
int tmp; //定義變數tmp用於存放a的值
tmp = a;
a = b;
b=tmp;
}
//函式引數使用引用
void refSwap(int &a,int &b)
{
int tmp; //定義變數tmp用於存放a的值
tmp = a;
a = b;
b=tmp;
}
int main(void)
{
int a=1;
int b=2;
refSwap(a,b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;
return 0;
}
程式碼輸出:
a=2 b=1
使用引用的方法進行交換,看到程式碼結果a和b已經互換的位置,因此可以總結出,如果需要操作(改變)物件本身,那麼需用引用處理,當然也可以用指標的方式進行處理,但是書寫對比引用會相對麻煩,所以一般都是引用。不信可以對比看看。
#include<iostream>
using namespace std;
//函式引數不使用引用
void swap(int a,int b)
{
int tmp; //定義變數tmp用於存放a的值
tmp = a;
a = b;
b=tmp;
}
//函式引數使用引用
void refSwap(int &a,int &b)
{
int tmp; //定義變數tmp用於存放a的值
tmp = a;
a = b;
b=tmp;
}
//函式引數使用指標
void ptrSwap(int *a,int *b)
{
int tmp=*a;
*a = *b;
*b=tmp;
}
int main(void)
{
int a=1;
int b=2;
ptrSwap(&a,&b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;
return 0;
}
程式碼輸出:
a=2 b=1
從上述的程式碼看出,引用和指標傳參的方式都能使a和b資料進行交換,但是從書寫上來看指標會相對複雜且程式碼難理解,因此建議使用引用的方式對物件本身進行處理。
五、引用作為函式返回值
如上所說引用可以用作函式引數,那麼引用也可以作為函式返回值。
不過注意的是引用作為函式返回值,不能返回區域性變數的引用,比如:
#include<iostream>
using namespace std;
//引用作為函式返回值型別
int &data(void)
{
int num = 100; //num數區域性變數
return num; //函式返回什麼變數,那麼引用的就是該變數的別名
}
int main(void)
{
int &ret = data(); //ret是函式data()中變數num的別名
cout<<"ret="<<ret<<endl;
}
程式碼輸出:
ret=32767
從程式碼輸出結果來看返回的是值跟預期結果的值不一致,因此可以知道如果返回的是區域性變數的引用,那麼編譯器會把返回的結果處理成一個隨機值,主要原因是因為區域性變數會在返回的過程中進行銷燬,從而導致區域性變數丟失。
那麼如何解決這個問題?通常我們可以使用靜態變數,在變數前加上關鍵字static。
#include<iostream>
using namespace std;
//引用作為函式返回值型別
int &data(void)
{
static int num = 100; //變數num前加上關鍵字statci,使num變成靜態變數
return num;
}
int main(void)
{
int &ret = data(); //ret是函式data()中變數num的別名
cout<<"ret="<<ret<<endl;
}
程式碼輸出:
ret=100
這時候程式碼輸出結果就跟預期一致啦,當然我們也可以把變數num定義成全域性變數,從而達到正確結果。
ps:補充如果函式返回值作為左值,那麼函式到返回型別必須是引用。
#include<iostream>
using namespace std;
//函式到返回值作為左值
int &data(void)
{
static int num = 100;
return num;
}
int main(void)
{
int num1=data();
int num2=data()=200;
cout<<"num1="<<num1<<endl;
cout<<"num2="<<num2<<endl;
return 0;
}
程式碼輸出:
num1=100
num2=200
那麼如果函式返回值用作左值,但是函式返回型別不是引用的話,函式返回值將無法用作左值,編譯器會報錯。
六、引用的本質
引用的本質在C++內部實現是一個指標常量,C++編譯器在編譯過程中使用常指標作為引用的內部實現,因此引用所佔的空間大小和指標相同,只是這個過程編譯器內部實現,使用者不可見。
int num = 10;
int &a = num; //a就是num的別名
//編譯器記憶體轉換:int* const a = #
a=100; //等價與num=100
總結
1:引用是什麼?->引用(reference)為物件起了另外一個名字。
2:引用怎麼定義?->使用操作符&。
3:引用定義的注意事項:引用必須進行初始化;引用一旦初始化就不能修改別名。
4:引用作為函式返回值需要注意什麼?->不能返回區域性變數的引用。
5:使用什麼方法可以解決返回變數的引用?->使用靜態變數在資料型別前加上關鍵字static或定義全域性變數。
6:引用用作函式引數有什麼效果?->操作物件本身而不會產生副本,從而提升程式的時間效率。
7:還有個常引用後面複習到有關類的知識點時在補充(const tt &b)。
相關文章
- C++ 學習筆記之 引用C++筆記
- Java筆記:方法引用Java筆記
- 引用-PHP手冊筆記PHP筆記
- C++筆記C++筆記
- 【C++】引用C++
- c++引用C++
- C++ 引用C++
- 讀書筆記-----Java中的引用筆記Java
- Effective C++筆記C++筆記
- c++筆記4C++筆記
- c++筆記2C++筆記
- c++筆記3C++筆記
- 《Effective C++》筆記C++筆記
- c++之引用及記憶體分割槽模型C++記憶體模型
- python3學習筆記之 強引用和弱引用Python筆記
- Java中的四大引用筆記Java筆記
- C++ Primer筆記C++筆記
- C++複習筆記C++筆記
- C++ primer 筆記C++筆記
- C++學習筆記C++筆記
- C++筆記--函式C++筆記函式
- C++筆記--異常C++筆記
- c++語法筆記C++筆記
- effective C++筆記1C++筆記
- C++左值引用與右值引用C++
- c++ 左值引用與右值引用C++
- C++ 右值引用和左值引用C++
- 詳解C++引用C++
- C++右值引用C++
- 【c++】引用計數C++
- C++學習筆記——C++ 繼承C++筆記繼承
- C++類初學筆記C++筆記
- C++ ——vector陣列筆記C++陣列筆記
- C++讀書筆記:字串C++筆記字串
- c++基本型別筆記C++型別筆記
- c++學習筆記(三)C++筆記
- C++學習筆記——003C++筆記
- 《Effective C++》讀書筆記C++筆記