C++學習 const 上

🌈 C++ Primer 的學習筆記

前言

這篇blog 主要是想具體講講新學到的const 當然不止是const 而是基于這個const引申出來的指針和引用。還是需要捋一捋的,這還是有點困難的。

我會把每一節的重點都摘出來,放在前面~

1??首先講講const

2??const引用和const指針 ?? 特別容易混淆

這邊blog 常量指針/引用 指針/引用常量 詳細講的不錯 可以看看~

文章目錄

    • 前言
    • const 限定符
        • 初始化和const
        • 默認情況下,const對象只在文件內有效
        • 練習
      • 關于const 引用
        • 初始化和對const 的引用
        • 對const的引用可能引用一個并非const 的對象
      • 指針和const
        • 指向常量的指針
        • 常量指針
        • 練習

const 限定符

🌈敲重點!

🍎因為const對象一旦創建后其值就不能再改變,所以const對象必須初始化

🍐只能在const類型的對象上執行不改變其內容的操作

有時我們希望定義這樣一種變量,它的值不能被改變。例如,用一個變量來表示緩沖區的大小。使用變量的好處是當我們覺得緩沖區大小不再合適時,很容易對其進行調整。另一方面,也應隨時警惕防止程序一不小心改變了這個值。為了滿足這一要求,可以用關鍵字const對變量的類型加以限定:

const int bufsize = 512;//輸入緩沖區大小

這樣就把 bufsize定義成了一個常量。任何試圖為 bufsize賦值的行為都將引發錯誤:

bufSize = 512;//錯誤:試圖向const對象寫值

因為const對象一旦創建后其值就不能再改變,所以const對象必須初始化 一如既往,初始值可以是任意復雜的表達式:

const int i = get_size();//正確:運行時初始化
const int j=42;//正確:編譯時初始化
const int k;//錯誤:k是一個未經初始化的常量
初始化和const

正如之前反復提到的,對象的類型決定了其上的操作。與非 const類型所能參與的操作相比,const類型的對象能完成其中大部分,但也不是所有的操作都適合。主要的限制就是只能在const類型的對象上執行不改變其內容的操作。例如,const int和普通的int一樣都能參與算術運算,也都能轉換成一個布爾值,等等。

在不改變 const 對象的操作中還有一種是初始化,如果利用一個對象去初始化另外一個對象,則它們是不是const都無關緊要:

int i =42;
const int ci = i;//正確:i的值被拷貝給了ci
int j= ci;//正確:ci的值被拷貝給了j

盡管ci是整型常量,但無論如何ci中的值還是一個整型數。ci的常量特征僅僅在執行改變ci的操作時才會發揮作用。當用ci去初始化j時,根本無須在意ci是不是一個常量。拷貝一個對象的值并不會改變它,一旦拷貝完成,新的對象就和原來的對象沒什么關系了。

默認情況下,const對象只在文件內有效

當以編譯時初始化的方式定義一個const對象時,就如對bufsize的定義一樣:

const int bufSize = 512;//輸入緩沖區大小

編譯器將在編譯過程中把用到該變量的地方都替換成對應的值。也就是說,編譯器會找到代碼中所有用到bufsize的地方,然后用512替換。

為了執行上述替換,編譯器必須知道變量的初始值。如果程序包含多個文件,則每個用了const對象的文件都必須得能訪問到它的初始值才行。要做到這一點,就必須在每一個用到變量的文件中都有對它的定義(參見2.2.2節,第41頁)。為了支持這一用法,同時避免對同一變量的重復定義,默認情況下,const對象被設定為僅在文件內有效。當多個文件中出現了同名的const變量時,其實等同于在不同文件中分別定義了獨立的變量

某些時候有這樣一種const變量,它的初始值不是一個常量表達式,但又確實有必要在文件間共享。這種情況下,我們不希望編譯器為每個文件分別生成獨立的變量。相反,我們想讓這類const對象像其他(非常量)對象一樣工作,也就是說,只在一個文件中定義const,而在其他多個文件中聲明并使用它。

解決的辦法是,對于const變量不管是聲明還是定義都添加extern關鍵字,這樣只需定義一次就可以了:

// file_1.cc定義并初始化了一個常量,該常量能被其他文件訪問
extern const int bufSize = fcn() ;
// file_ 1.h頭文件
extern const int bufSize;//與file_1.cc中定義的bufsize是同一個

如上述程序所示,file_1.cc定義并初始化了bufsize。因為這條語句包含了初始值,所以它(顯然)是一次定義。然而,因為bufsize是一個常量,必須用extern加以限定使其被其他文件使用。

file 1.h頭文件中的聲明也由extern做了限定,其作用是指明bufsize并非本文件所獨有,它的定義將在別處出現。

如果想在多個文件之間共享const對象,必須在變量的定義之前添加extern關鍵字。

練習
//練習2.26:下面哪些句子是合法的?如果有不合法的句子,請說明為什么?
(a) const int buf;
(b) int cnt =0
(c) const int sz= cnt;
(d)++cnt; ++SZ

(a) const int buf;

這個句子是不合法的。在C++中,const關鍵字用于聲明一個常量,它必須被初始化。因此,const int buf;缺少初始化表達式,正確的聲明應該是const int buf = 0;

(b) int cnt = 0;
這個句子是合法的。它聲明了一個整型變量cnt并初始化為0。

? const int sz = cnt;

這個句子是不合法的。這里有兩個問題:首先,const變量必須在聲明時初始化,不能在聲明后賦值。其次,即使cnt已經被聲明并初始化,sz作為const變量也不能從非const變量cnt那里賦值。正確的做法是直接在聲明時初始化,例如const int sz = 5;

(d) ++cnt; ++SZ;

首先,++cnt;是合法的,它表示對變量cnt進行自增操作。然而,++SZ;是不合法的,因為SZ沒有被聲明為一個變量,而且變量名通常不以大寫字母開頭,這是C++中常見的命名約定,盡管這不是語法錯誤。如果SZ是一個未聲明的變量,那么這個句子將導致編譯錯誤。

關于const 引用

敲重點

🍌常量的引用 不能用作修改被綁定的對象

也就是說 原來的數據類型是const xx 類型 那么,對這個數據引用后就不能利用引用的變量去更改原有的值(好像有點繞 具體看下文)

??注意 常量的引用 和 **常量引用 ** 的不同 一個是被引用的變量 一個是變量本身 下面看的時候要認真一些

常量的引用 數據類型需要一致 相當于對一個const類型的變量進行引用,這個時候就叫常量的引用

常量引用 是一個引用類型的變量

🍑在初始化常量引用時允許用任意表達式作為初始值,允許為一個常量引用綁定非常量的對象、字面值,甚至是個一般表達式。

🍊常量引用僅對引用可參與的操作做出了限定,對于引用的對象本身是不是一個常量未作限定。因為對象也可能是個非常量,所以允許通過其他途徑改變它的值:

可以把引用綁定到const對象上,就像綁定到其他對象上一樣,我們稱之為對常量的引用(reference to const)。與普通引用不同的是,對常量的引用不能被用作修改它所綁定的對象:

const int ci = 1024;
const int &r1 = ci;  //正確:引用及其對應的對象都是常量
r1 = 42;//錯誤:r1是對常量的引用
int &r2=ci;//錯誤:試圖讓一個非常量引用指向一個常量對象

因為不允許直接為ci賦值,當然也就不能通過引用去改變ci。因此,對r2的初始化是錯誤的。假設該初始化合法,則可以通過r2來改變它引用對象的值,這顯然是不正確的。

常量引用是對const的引用

程序員們經常把詞組“對 const的引用”簡稱為“常量引用”,這一簡稱還是挺靠譜的,不過前提是你得時刻記得這就是個簡稱而已。

嚴格來說,并不存在常量引用。因為引用不是一個對象,所以我們沒法讓引用本身恒定不變。事實上,由于C+語言并不允許隨意改變引用所綁定的對象,所以從這層意義上理解所有的引用又都算是常量。引用的對象是常量還是非常量可以決定其所能參與的操作,卻無論如何都不會影響到引用和對象的綁定關系本身

初始化和對const 的引用

2.3.1節(第46頁)提到,引用的類型必須與其所引用對象的類型一致,但是有兩個例外。第一種例外情況就是在初始化常量引用時允許用任意表達式作為初始值,只要該表達式的結果能轉換成(參見2.1.2節,第32頁)引用的類型即可。尤其,允許為一個常量引用綁定非常量的對象、字面值,甚至是個一般表達式:

int i =42;·
const int &r1 = i;//允許將const int&綁定到一個普通int對象上
const int &r2=42;  //正確:r1是一個常量引用
const int &r3 = r1 * //正確:r3是一個常量引用
int &r4 = r1 * 2;//錯誤:r4是一個普通的非常量引用

要想理解這種例外情況的原因,最簡單的辦法是弄清楚當一個常量引用被綁定到另外一種類型上時到底發生了什么:

double dval = 3.14;
const int &ri = dval;

此處ri引用了一個int 型的數。對ri的操作應該是整數運算,但dval卻是一個雙精度浮點數而非整數。因此為了確保讓ri綁定一個整數,編譯器把上述代碼變成了如下形式:

const int temp =dval;
//由雙精度浮點數生成一個臨時的整型常量
const int &ri = temp;
// 讓ri綁定這個臨時量

接下來探討當ri不是常量時,如果執行了類似于上面的初始化過程將帶來什么樣的后果。如果ri不是常量,就允許對ri賦值,這樣就會改變ri所引用對象的值。注意,此時綁定的對象是一個臨時量而非 dval。程序員既然讓 ri引用dval,就肯定想通過ri改變dval的值,否則為什么要給ri賦值呢? 如此看來,既然大家基本上不會想著把引用綁定到臨時量上,C++語言也就把這種行為歸為非法。

對const的引用可能引用一個并非const 的對象

必須認識到,常量引用僅對引用可參與的操作做出了限定,對于引用的對象本身是不是一個常量未作限定。因為對象也可能是個非常量,所以允許通過其他途徑改變它的值:

int i =42;
int &r1 = i;
//引用ri綁定對象i
const int &r2 = i;
// r2也綁定對象i,但是不允許通過r2修改i的值
r1 = 0;
//r1并非常量,i的值修改為0
r2 = 0;
//錯誤:r2是一個常量引用

r2綁定(非常量)整數i是合法的行為。然而,不允許通過r2修改i的值。盡管如此,i的值仍然允許通過其他途徑修改,既可以直接給i賦值,也可以通過像r1一樣綁定到i的其他引用來修改。

指針和const

🌈 敲重點?

和上一節一樣 常量指針和指向常量的指針 這倆是不一樣的!

??指向常量的指針 指向常量的指針不能用于改變其所指向對象的值。存放常量對象的地址 只能使用指向常量的指針。和引用一樣,指向常量的指針沒有規定所指向的對象是一個常量,僅僅要求不能通過該指針改變對象的值,沒有規定那個對象的值不能通過其他途徑改變。

🌊 常量指針 必須初始化 一旦初始化, 他的值(也就是存放再指針的地址)不會再改變了 。*放在const之前說明指針是一個常量,也就是不變的是指針本身的值而不是指向的那個值

要想弄清楚聲明的含義最行之有效的辦法是從右向左閱讀

指向常量的指針

指向常量的指針不能用于改變其所指向對象的值。存放常量對象的地址 只能使用指向常量的指針

帶有前綴 const

const double pi = 3.14;// pi是個常量,它的值不能改變
double *ptr = π//錯誤:ptr是一個普通指針const double *cptr = π//正確:cptr可以指向一個雙精度常量
*cptr = 42;//錯誤:不能給*cptr賦值double dval = 3.14;//dval是一個雙精度浮點數,它的值可以改變
cptr = &dval;//正確:但是不能通過cptr改變dval的值

指針類型不一定要與所致的對象一致

和引用一樣,指向常量的指針沒有規定所指向的對象是一個常量,僅僅要求不能通過該指針改變對象的值,沒有規定那個對象的值不能通過其他途徑改變。

常量指針

常量指針必須初始化 一旦初始化, 他的值(也就是存放再指針的地址)不會再改變了

*** 放在const之前說明指針是一個常量,也就是不變的是指針本身的值而不是指向的那個值**

int errNumb = 0;
int *const curErr = &errNumb;// curErr將一直指向errNumb
const double pi = 3.14159;
const double *const pip = π // pip是一個指向常量對象的常量指針

如同2.3.3節(第52頁)所講的,要想弄清楚這些聲明的含義最行之有效的辦法是從右向左閱讀。此例中,離curErr最近的符號是const,意味著curErr本身是一個常量對象,對象的類型由聲明符的其余部分確定。聲明符中的下一個符號是*,意思是 curErr是一個常量指針。最后,該聲明語句的基本數據類型部分確定了常量指針指向的是一個int對象。與之相似,我們也能推斷出,pip是一個常量指針,它指向的對象是一個雙精度浮點型常量。

指針本身是一個常量并不意味著不能通過指針修改其所指對象的值,能否這樣做完全依賴于所指對象的類型。例如,pip是一個指向常量的常量指針,則不論是pip所指的對象值還是pip自己存儲的那個地址都不能改變。相反的,curErr指向的是一個一般的非常量整數,那么就完全可以用curErr去修改errNumb的值:

*pip = 2.72;
//錯誤:pip是一個指向常量的指針
//如果curErr所指的對象(也就是errNumb)的值不為0
if(*curErr) {errorHandler ();*curErr = 0;//正確:把curErr所指的對象的值重置
}
練習

🐋 ?ps 對于不確定的式子 可以代入代碼敲一敲

//練習2.27:下面的哪些初始化是合法的?請說明原因。
(a)int i = -1,&r = 0;
(b) int *const p2 = &i2;
(c) const int i = -1,&r = 0;
(d) const int *const p3 = &i2;
(e) const int *pl = &i2;
(f)const int &const r2;
(g)const int i2= i,&r = i;
//練習2.28:說明下面的這些定義是什么意思,挑出其中不合法的。
(a)int i, *const cp;
(b)int *p1, *const p2;
(C) const int ic,&r = ic;
(d) const int *const p3;
(e)const int *p;
//練習2.29:假設已有上一個練習中定義的那些變量,則下面的哪些語句是合法的?請說明原因。
(a)i = ic;
(b)p1 = p3;
(c)p1 = ⁣
(d)p3 = ⁣
(e)p2= pl;
(f)ic = *p3 ;

🔑 答案答案

//練習2.27:下面的哪些初始化是合法的?請說明原因。
(a) int i = -1,&r = 0;  
//可能一些小伙伴看不懂 這其實相當于 int i = -1,int &r = 0;  
//  這類引用應初始化一個變量 而不是字面量 不合法(具體看引用篇)
(b) int *const p2 = &i2; 
//p2 是一個常量指針  也就是地址不變的指針 如果i2是int類型的則合法, 只能指向int
(c) const int i = -1,&r = 0; 
//正確  相當于const int &r=0;  是一個常量引用 可以指向字面量
//在初始化常量引用時允許用任意表達式作為初始值,允許為一個常量引用綁定非常量的對象、字面值,甚至是個一般表達式。
(d) const int *const p3 = &i2; 
//指向常量的常量指針 不可以用這個指針更改i2的值 同時這個指針的地址也不會變(e) const int *pl = &i2; 
//指向常量的指針  不可以用這個指針更改i2的值(f)const int &const r2; 
//這個 應該是語法錯誤(g)const int i2= i,&r = i; 
// 相當于 const int &r =i;
//如果i是常量 可以這么做
//練習2.28:說明下面的這些定義是什么意思,挑出其中不合法的。
(a)int i, *const cp;
//int *const cp  常量指針  初始化后不能變地址 也就是地址不能更改,且一定要初始化
(b)int *p1, *const p2;
//int *const p2  常量指針  初始化后不能變地址 也就是地址不能更改,且一定要初始化 
(C) const int ic,&r = ic;
//需要初始化 const int &r=ic  
(d) const int *const p3;
//需要初始化
(e)const int *p;
//指向常量的指針 需要指向const int 類型的值   不能通過指針改變指向的值的內容
//練習2.29:假設已有上一個練習中定義的那些變量,則下面的哪些語句是合法的?請說明原因。
(a)int i;const int ic;i = ic;
//合法  i只是用了ic的值 
(b)int *p1;const int *const p3;p1 = p3; 
//int 指針變量 =指向int 的常量指針 類型不一致(c)int *p1;const int icp1 = ⁣
//不合法 必須是const int *p1 才可(d) const int *const p3;const int ic;p3 = ⁣
//不合法 p3不可更改地址(e)int *const p2;int *p1p2= pl;
//常量指針 值不可更改  p2
//pl已經初始化了 (f)	const int ic;const int *const p3;ic = *p3 ;
//不能  const類型 是常量 不可更改

完結 歡迎批評指正~ 點個贊點個贊~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20513.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20513.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20513.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux C/C++動態庫制作

概念:程序在編譯時不會把庫文件的二進制代碼鏈接到目標程序中,而是在運行時候才被載入。 如果多個進程中用到了同一動態庫中的函數或類,那么在內存中只有一份,避免了空間浪費問題。 特點: 程序運行在運行的過程中&…

統計信號處理基礎 習題解答10-6

題目 在例10.1中,把數據模型修正為: 其中是WGN,如果,那么方差,如果,那么方差。求PDF 。把它與經典情況PDF 進行比較,在經典的情況下A是確定性的,是WGN,它的方差為&#…

5.算法講解之-二分查找(簡單易懂)

1.簡介 1.二分查找的思路簡單易懂,較難的是如何處理查找過程中的邊界條件,當較長時間沒寫二分查找的時候就容易忘記如何處理邊界條件。 2.只有多寫代碼,多做筆記就不易忘記邊界條件 2.算法思路 正常查找都是從頭到尾查找一個數字是否在數組中…

使用pycharm+opencv進行視頻抽幀(可以用來擴充數據集)+ labelimg的使用(數據標準)

一.視頻抽幀 1.新創建一個空Pycharm項目文件,命名為streach zhen 注:然后要做一個前期工作 創建opencv環境 (1)我們在這個pycharm項目的終端里面輸入下面的命令: pip install opencv-python --user -i https://pypi.t…

SettingWithCopyWarning: A value is trying to be set on a copy of a slice fro

SettingWithCopyWarning: A value is trying to be set on a copy of a slice fro 錯誤代碼&#xff1a; while i < len(data_csv_data):if data_csv_data[flowmember][i] j:data_csv_data[label][i] data_csv_label[label][j-1]data_csv_data[classes][i]data_csv_label[…

[數據集][目標檢測]獼猴桃檢測數據集VOC+YOLO格式1838張1類別

數據集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路徑的txt文件&#xff0c;僅僅包含jpg圖片以及對應的VOC格式xml文件和yolo格式txt文件) 圖片數量(jpg文件個數)&#xff1a;1838 標注數量(xml文件個數)&#xff1a;1838 標注數量(txt文件個數)&#xff1a;1838 標注…

企業級寬表建設

1 寬表概述 寬表&#xff0c;從字面意義上講就是字段比較多的數據庫表&#xff0c;通常情況下是講很多相關的數據&#xff0c;包括實時表、維度表、指標等格言錄在一起形成的一張數據表。 2 寬表的優點 2.1 開發效率提升 由于把不同的信息放在同一張表存儲&#xff0c;寬表…

sensitive-word 敏感詞 v0.17.0 新特性之 IPV4 檢測

敏感詞系列 sensitive-word-admin 敏感詞控臺 v1.2.0 版本開源 sensitive-word-admin v1.3.0 發布 如何支持分布式部署&#xff1f; 01-開源敏感詞工具入門使用 02-如何實現一個敏感詞工具&#xff1f;違禁詞實現思路梳理 03-敏感詞之 StopWord 停止詞優化與特殊符號 04-…

詳解 Spark 核心編程之 RDD 持久化

一、問題引出 /** 案例&#xff1a;對同一份數據文件分別做 WordCount 聚合操作和 Word 分組操作 期望&#xff1a;針對數據文件只進行一次分詞、轉換操作得到 RDD 對象&#xff0c;然后再對該對象分別進行聚合和分組&#xff0c;實現數據重用 */ object TestRDDPersist {def …

Jupyter Notebook快速搭建

Jupyter Notebook why Jupyter Notebook Jupyter Notebook 是一個開源的 Web 應用程序&#xff0c;允許你創建和分享包含實時代碼、方程、可視化和解釋性文本的文檔。其應用包括&#xff1a;數據清洗和轉換、數值模擬、統計建模、數據可視化、機器學習等等。 Jupyter Notebo…

東芝機械人電池低報警解除與機器人多旋轉數據清零

今天啟動一臺設備,觸摸屏一直顯示機器人報警(翻譯過后為電池電量低),更換電池后關機重啟后也不能消除,所以打開示教器,下面就來說說怎么解決此項問題(可以參考官方發的手冊,已手冊為主)。 一,設備 下面來看看機械手的照片與示教器的照片 四軸機械手(六軸機器人有可…

可視化大屏也在卷組件化設計了?分享一些可視化組件

hello&#xff0c;我是大千UI工場&#xff0c;這次分享一些可視化大屏的組件&#xff0c;供大家欣賞。&#xff08;本人沒有源文件提供&#xff09;

動態內存基礎實踐

文章目錄 1.new 創建堆內存對象2.delete釋放內存空間3.malloc申請內存4.free釋放malloc申請的內存空間 1.new 創建堆內存對象 2.delete釋放內存空間 3.malloc申請內存 4.free釋放malloc申請的內存空間 #include <iostream> #include <string>using namespace s…

基礎數學內容重構(后綴0個數)

今天也是參加了一下寧波大學的校賽&#xff0c;其中有一道題是求后綴0的個數&#xff0c;題意是讓我們求一下式子的后綴0個數&#xff1a; 看上去比較復雜&#xff0c;但是通過化簡我們可以知道以上式子就是求&#xff08;n 1&#xff09;&#xff01;&#xff0c;這里化簡的過…

用貪心算法計算十進制數轉二進制數(小數部分)

在上一篇博文用貪心算法計算十進制數轉二進制數&#xff08;整數部分&#xff09;-CSDN博客中&#xff0c;小編介紹了用貪心算法進行十進制整數轉化為二進制數的操作步驟&#xff0c;那么有朋友問我&#xff0c;那十進制小數轉二進制&#xff0c;可以用貪心算法來計算嗎&#x…

[C++]vector的模擬實現

下面是簡單的實現vector的功能&#xff0c;沒有涉及使用內存池等復雜算法來提高效率。 一、vector的概述 &#xff08;一&#xff09;、抽象數據類型定義 容器&#xff1a;向量&#xff08;vector&#xff09;vector是表示大小可以變化的數組的序列容器。像數組一樣&#xf…

帶你學習Mybatis之Mybatis映射文件

Mybatis映射文件 增刪改查 簡單地增刪改查 <select id"selectUser" resultType"User"> select * from user where id #{id}</select><insert id"addUser"> insert into user (name,account) values (#{name},#{account…

[sylar]后端學習:配置環境(一)

1.介紹 基于sylar大神的C高性能后端網絡框架來進行環境配置和后續學習。網站鏈接&#xff1a;sylar的Linux環境配置 2.下載 按照視頻進行下載&#xff0c;并進行下載&#xff0c;并最好還要下載一個vssh的軟件。可以直接在網上搜索即可。 sylar_環境配置&#xff0c;vssh下…

CentOS 運維常用的shell腳本

文章目錄 一、操作系統磁盤空間查看實時獲取系統運行狀態獲取cpu、內存等系統運行狀態獲取系統信息二、應用程序獲取進程運行狀態查看有多少遠程的 IP 在連接本機三、用戶管理統計當前 Linux 系統中可以登錄計算機的賬戶有多少個創建用戶四、自動化管理自動備份日志文件監控的頁…

MySQL常見操作

MySQL字符串連接 在MySQL中&#xff0c;字符串連接可以使用CONCAT()函數或雙豎線||操作符進行。下面是兩種方法的示例&#xff1a; 使用CONCAT()函數&#xff1a; CONCAT(,2001,, ABC)使用雙豎線||操作符&#xff1a; ,2001, || ABC您可以根據自己的偏好選擇其中一種方法來…