《C++Primer》之——引用、指標以及它們與const的愛恨糾葛
前言
C++入門路上的第一個糾結:引用和指標,兩個好好的東西,跟const混上之後,就開始在你腦子裡打架了。。。
對常量的引用、常量引用;指向常量的指標、常量指標(底層const、頂層const)。如果單看名字的話,這些概念很清晰,似乎沒什麼奇怪的呀。
本文就來 扣扣他們的概念,捋一捋他們的關係,不過這都不重要,這個過程只是讓它們在你的腦子裡先扭打在一起,再分幫結派的站在兩邊,從而深刻明瞭的將它們區分開,並瞭解它們的特性。
最終,最重要的還是搞清楚:在程式碼使用中時,它們能幹啥、不能幹啥
先定義幾個全域性變數在前面:
int i=1 , j=2;
1.先介紹一下引用和指標
- 引用不是物件:int& a=i; 這其中的a不是一個物件,只是i的別名。故它具有以下性質:
a. 引用不能賦值和拷貝(對引用進行賦值,是對被引用物件賦值)
b. 引用不能重新繫結一個物件(這很合理,因為壓根兒也沒有給引用本身賦值的辦法呀…)
c. 引用在定義時必須初始化
可以將引用理解為一個“常量”,它是不可變、必須初始化的。 - 引用的使用要求:
a. 引用只能繫結在物件上(字面值常量、一般表示式是不可以被引用的)(常量引用例外)
b. 引用的型別必須要和繫結的物件嚴格匹配 (常量引用例外,而且例外的賊離譜!P55)
下面是測試程式碼
void main() {
double data = 3.14, bata=4.15;
double& y1 = data;
/*測試1.引用的性質*/
y1 = bata;//對引用y賦值,是對被引用物件data賦值
//double& y1 = bata;//試圖將y1重新繫結一個物件。error:重定義
cout << data << endl
<< y1 << endl;
/*測試2.引用的使用要求*/
//int& y2 = 666;//error:非常量引用的初始值必須為左值
//int& y3 = data;//error:無法用 "double" 型別的值初始化 "int &" 型別的引用(非常量限定)
}
- 指標是一個物件:int* p=&i; 這其中p是一個物件。故它具有以下性質:
a. 指標可以賦值和拷貝
b. 在其生命週期內可以先後指向不同的物件(即指標的值是可以修改的)
c. 指標無須在定義時初始化(無須,但是不是無需,避免野指標,從你我做起~)
指標是一個存放地址值的變數,它存放的值隨你改。 - 指標的使用要求:
a. 指標必須指向變數(左值或函式識別符號)(不能指向字面值常量或一般表示式,沒有例外)
b. 指標型別要和它指向的物件嚴格匹配(eg:指向int的指標,其指向的物件必須是int)(“指向常量的指標”指向非常量是個例外)
下面是測試程式碼
void main() {
double data = 3.14, bata=4.15;
double* p1=&data;
/*測試1.指標的性質*/
p1=&bata;
/*測試2.指標的使用要求*/
//double* p2=&(2.33);//error: 表示式必須為左值或函式識別符號。這也不是人能寫出來的玩意兒a
//p1=1024;//error:不能將 "int" 型別的值分配到 "double *" 型別的實體。人確實有可能寫出來這玩意兒
p1=0;//這是可以的,相當於賦了個nullptr
//int* p3=&data;//error: 無法從“double *”轉換為“int *”
}
針對上面的性質和要求:
- 引用型別和被繫結物件型別要匹配
- 指標別瞎給值,好好地往物件上指,或者給個nullptr
- 指標定義的時候記得初始化,沒得指就給nullptr
2. 引用與const限定符
對引用加上const的修飾,僅對引用可參與的操作做出了限定,引用的物件本身是否是個常量未作限定。
- 從結合形式上來看:引用與const的結合形式只有一種:
const int k=3;
const int& cy=k;
[如果非要說,其實另一種也行:
int m=1;
int& const cy=m;//這也不是人能寫出來的玩意兒啊(小聲嗶嗶)
這裡的const限定符其實是沒起啥效果的,因為人家引用本來就不可變(可以理解為給引用加頂層const屬性,這操作是沒啥意義的)。
直接 int& cy=m; 也是一樣的效果]
- 故“常量引用”與“對常量的引用”不用刻意區分,就當前者是後者的簡稱就好(後文即是如此)
嚴格來說,“常量引用”並不存在,因為引用不是一個物件,尤其引用是不可修改的(引用繫結的物件是不可變的)
讓引用保持恆定不變這個事兒本來就是不存在的。P55
-
2.2 引用與const結合後帶來的例外
常量引用允許用任意表達式作為初始值,只要該表示式能轉換成引用的型別即可:
分解翻譯一下:- 對常量的引用 可以繫結字面值、一般表示式;(不再是隻能繫結在物件上)
- 對常量的引用 可以繫結另外型別的物件;(型別不用嚴格匹配了)(另外型別的物件 包括 對應型別的非常量物件)
這就離譜。。。沒錯,下面這些程式碼都是正確的:
double data = 3.14, bata=4.15;
const int& cy1 = data;//cy1是3
const int& cy2 = 6.66;//cy2是6
- 正常程式碼中,一般也沒人拿引用去繫結別的型別的物件或是繫結字面值常量(主要用於函式傳參),所以上面的兩個例外知道就好,使用中不用太在意
- 例外中有一點需要注意:常量引用時,被引用物件可以是常量也可以是非常量。(這是個型別匹配的例外:引用是常量,而被引量不是常量)
- 但如果被引用物件是常量時,一定要用常量引用
- 實際上,引用就是用來方便的修改變數的值的,所以常量引用基本沒用。那它存在的意義是什麼呢?參考這個連結
// 對引用加上const的修飾,僅對引用可參與的操作做出了限定,引用的物件本身是否是個常量未作限定。
int a=1;
const int b=2;
/*對常量的引用*/
const int& cy1=a;
const int& cy2=b;
//int& cy3=b;//error: 對常量的引用必須是常量引用
先忘掉2.1、2.2、2.3節所看到的所想到的東西,從如下這寫字眼的本質出發,思考一下下面這些問題
(思考完就趕緊忘了吧,不是啥好東西,就當沒看過)
問題一:“常量引用”一定是“對常量的引用”嗎?
單從字面來看:
1.對常量的引用——被引用物件是常量;常量引用——引用本身是常量
2.常量引用的被引用物件不一定是常量
故,“常量引用”不一定是“對常量的引用”。
問題二: “對常量的引用”一定是“常量引用”嗎?
是的,“對常量的引用”必須使用“常量引用”
故 “對常量的引用”一定是“常量引用”。
3. 指標與const限定符
整理一下思緒,大的