1. 程式人生 > 其它 >《C++Primer》之——引用、指標以及它們與const的愛恨糾葛

《C++Primer》之——引用、指標以及它們與const的愛恨糾葛

技術標籤:J-C++J-知識點c++指標

前言

C++入門路上的第一個糾結:引用和指標,兩個好好的東西,跟const混上之後,就開始在你腦子裡打架了。。。
對常量的引用、常量引用;指向常量的指標、常量指標(底層const、頂層const)。如果單看名字的話,這些概念很清晰,似乎沒什麼奇怪的呀。
本文就來 扣扣他們的概念,捋一捋他們的關係,不過這都不重要,這個過程只是讓它們在你的腦子裡先扭打在一起,再分幫結派的站在兩邊,從而深刻明瞭的將它們區分開,並瞭解它們的特性
最終,最重要的還是搞清楚:在程式碼使用中時,它們能幹啥、不能幹啥

先定義幾個全域性變數在前面:
int i=1 , j=2;

1.先介紹一下引用和指標

  • 1.1 引用 &

  1. 引用不是物件:int& a=i; 這其中的a不是一個物件,只是i的別名。故它具有以下性質
    a. 引用不能賦值和拷貝(對引用進行賦值,是對被引用物件賦值)
    b. 引用不能重新繫結一個物件(這很合理,因為壓根兒也沒有給引用本身賦值的辦法呀…)
    c. 引用在定義時必須初始化
    可以將引用理解為一個“常量”,它是不可變、必須初始化的。
  2. 引用的使用要求
    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 &" 型別的引用(非常量限定)
}
  • 1.2 指標 *

  1. 指標是一個物件:int* p=&i; 這其中p是一個物件。故它具有以下性質
    a. 指標可以賦值和拷貝
    b. 在其生命週期內可以先後指向不同的物件(即指標的值是可以修改的)
    c. 指標無在定義時初始化(無須,但是不是無需,避免野指標,從你我做起~)
    指標是一個存放地址值的變數,它存放的值隨你改。
  2. 指標的使用要求
    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 *”
}
  • 1.3 程式碼使用中要注意

針對上面的性質和要求:

  1. 引用型別和被繫結物件型別要匹配
  2. 指標別瞎給值,好好地往物件上指,或者給個nullptr
  3. 指標定義的時候記得初始化,沒得指就給nullptr

2. 引用與const限定符

對引用加上const的修飾,僅對引用可參與的操作做出了限定,引用的物件本身是否是個常量未作限定。

  • 2.1 對常量的引用——常量引用

  1. 從結合形式上來看:引用與const的結合形式只有一種:
	const int k=3;
	const int& cy=k;

  [如果非要說,其實另一種也行:
    int m=1;
    int& const cy=m;//這也不是人能寫出來的玩意兒啊(小聲嗶嗶)
  這裡的const限定符其實是沒起啥效果的,因為人家引用本來就不可變(可以理解為給引用加頂層const屬性,這操作是沒啥意義的)。
  直接 int& cy=m; 也是一樣的效果]

  1. 故“常量引用”與“對常量的引用”不用刻意區分,就當前者是後者的簡稱就好(後文即是如此)

    嚴格來說,“常量引用”並不存在,因為引用不是一個物件,尤其引用是不可修改的(引用繫結的物件是不可變的)
    讓引用保持恆定不變這個事兒本來就是不存在的。P55
  • 2.2 引用與const結合後帶來的例外

    常量引用允許用任意表達式作為初始值,只要該表示式能轉換成引用的型別即可:
    分解翻譯一下:

    1. 對常量的引用 可以繫結字面值、一般表示式;(不再是隻能繫結在物件上)
    2. 對常量的引用 可以繫結另外型別的物件;(型別不用嚴格匹配了)(另外型別的物件 包括 對應型別的非常量物件

    這就離譜。。。沒錯,下面這些程式碼都是正確的:

	double data = 3.14, bata=4.15;
	const int& cy1 = data;//cy1是3
	const int& cy2 = 6.66;//cy2是6
  • 2.3 const引用的使用要注意

  1. 正常程式碼中,一般也沒人拿引用去繫結別的型別的物件或是繫結字面值常量(主要用於函式傳參),所以上面的兩個例外知道就好,使用中不用太在意
  2. 例外中有一點需要注意:常量引用時,被引用物件可以是常量也可以是非常量。(這是個型別匹配的例外:引用是常量,而被引量不是常量)
  3. 但如果被引用物件是常量時,一定要用常量引用
  4. 實際上,引用就是用來方便的修改變數的值的,所以常量引用基本沒用。那它存在的意義是什麼呢?參考這個連結
// 對引用加上const的修飾,僅對引用可參與的操作做出了限定,引用的物件本身是否是個常量未作限定。
int a=1;
const int b=2;
/*對常量的引用*/
const int& cy1=a;
const int& cy2=b;
//int& cy3=b;//error: 對常量的引用必須是常量引用
  • 2.4 來思考幾個充滿哲理的問題

先忘掉2.1、2.2、2.3節所看到的所想到的東西,從如下這寫字眼的本質出發,思考一下下面這些問題
(思考完就趕緊忘了吧,不是啥好東西,就當沒看過)

問題一:“常量引用”一定是“對常量的引用”嗎?
 單從字面來看:
  1.對常量的引用——被引用物件是常量;常量引用——引用本身是常量
  2.常量引用的被引用物件不一定是常量
 故,“常量引用”不一定是“對常量的引用”。
問題二: “對常量的引用”一定是“常量引用”嗎?
 是的,“對常量的引用”必須使用“常量引用”
 故 “對常量的引用”一定是“常量引用”。

3. 指標與const限定符

  整理一下思緒,大的