C++系列:const關鍵字

来源:https://www.cnblogs.com/TianyiLi-Tone/p/18052254
-Advertisement-
Play Games

前言 在學習C++時,const關鍵字的知識點分散在書的各個章節。當我們嘗試在編程時使用const時,總會感覺有一些細節被遺忘,因而不能得心應手地使用const關鍵字。因此,本篇文章嘗試著對const關鍵字的做一些總結。參考書籍《C++ Primer Plus》 const總結 這裡是我做的關於co ...


前言

在學習C++時,const關鍵字的知識點分散在書的各個章節。當我們嘗試在編程時使用const時,總會感覺有一些細節被遺忘,因而不能得心應手地使用const關鍵字。因此,本篇文章嘗試著對const關鍵字的做一些總結。參考書籍《C++ Primer Plus》

const總結

這裡是我做的關於const關鍵字的一些總結,之後的各章便是對書中知識點的理解。

  • const限定符創建的常量不可再次修改
  • 創建常量時記得初始化
  • const創建的常量可以用來聲明數組長度。
  • const int * p;p指向常量,p可修改,*p不可修改。
  • int * const p;, p指向變數,p不可修改,*p可修改。
  • const指針可以接受const數據和非const數據。
  • const指針僅可以接收非const數據。
  • 不允許將非const指針的地址賦值給const指針。
  • const引用創建臨時變數的兩種情況。
  • const全局變數是內部鏈接性,如static。可用extern更改鏈接性。可在頭文件中使用。
  • cv-限定符。
  • const成員函數,void show() const;,表示函數不會修改調用對象(類成員)。

const限定符

const關鍵字是C++中較為常用的一個關鍵字。當我們想創建一個符號常量時,按照C語言的習慣,我們一般會使用#define這種預處理器方法,例如#define ZERO 0。但在C++中,提供了一種更好的處理符號常量的方法,那就是const關鍵字。

創建常量

創建一個常量的通用格式:const type name = value;
例如:

const int zero = 0; // 一個普通的常量·

這個例子中,被const修飾過的變數zero會變為常量。常量zero被初始化後,其值就被固定了,C++編譯器不允許再次修改常量的值。

常量初始化

這裡有一點需要註意:在用const聲明一個變數時,需要進行初始化。例如下麵的代碼是錯誤的:

const int zero;//聲明常量時需要進行初始化,否則zero的值未知。
zero = 0;//因為C++編譯器不允許再次修改常量的值。所以此處錯誤。

另外,常量可以用來作為聲明名數組時的元素數目。例如:

const int ten = 10;//創建了一個常量。
int array[ten];//用常量創建數組。

一級指針與const

在用const修飾指針時則會出現一些很微妙的地方。在C++中,可以用兩種不同的方式將const關鍵字用於指針。第一種方法是讓指針指向一個常量,第二種方法是讓指針本身就是常量。其中,第一種方法可以防止使用該指針來修改所指向的值。而第二種方法可以防止改變指針所指的位置。例如:

int value = 0;
const int * p1 = &value; //第一種用法,防止利用p1修改value的值。
int * const p2 = &value; //第二種用法,p2本身不能再修改了。

這裡有一個特殊情況令人在意。如果將一個指向變數的指針指向一個常量會發生什麼?代碼如下:

const int value = 0;
int * p2 = &value; //這是錯誤的,C++禁止這樣的行為。

我們發現,value是常量。但p2是指向value的,那麼我們可以通過* p2value的值進行修改。可是做這樣的話const的作用就失效了。
事實上,C++禁止這樣的用法。也就是說,C++禁止將const常量的地址賦值給非const指針。因此,上面的代碼是錯誤的。在邏輯上也很好解釋。
我們可以這樣理解:為保證常量不可再次修改的屬性,我們不能通過指針修改常量,因此,非const指針僅可以接收非const數據。與之類似,因為我們聲明瞭const指針目的是不會通過當前指針修改其指向的數據,因此其指向的數據一直都是安全的,自然const數據和非const數據都可以。因此,const指針可以接受const數據和非const數據。

二級指針使用const的限制

關於二級指針與常量的關係有些複雜,我們來看下麵的代碼:

const int ** pp;//這是一個二級const指針
int *p;
const int value = 0;//這是一個常量
pp  = &p;  //這裡是錯誤的,雖然高亮沒有提示錯誤。
           //錯誤C2440:初始化:無法從int **轉換為const int **。
*pp = &value;//兩個都是常量,賦值沒有問題
*p  = 10; //通過p修改了value的值!

如果pp = &p允許的話,那麼我們可以通過二級指針繞開const的限制,如上訴代碼一樣。C++規定,僅當且只有一層間接關係(如指針指向基本數據類型)時,才能將非const地址賦值給const指針。也就是說,C++不允許將非const指針的地址賦值給const指針。
最後,關於const與指針的關係,下麵還有幾個例子,請看:

const int value =0;//這是常量
const int *p1;//p1可變,*p1不可變
int * const p2 = &value;//p2不可變,*p2可變

const int ** pp3;//pp3可變,*pp3可變,**pp3不可變
int * const * pp4;//pp4可變,*pp4不可變,**pp4可變
int ** const pp5;//pp5不可變,*pp5不可變,**pp5可變
const int * const *const p6 = &p1;//pp6不可變,*pp6不可變,**pp6不可變

const引用

我們在使用函數的時候,一般會使用引用形參。原因就是因為速度快,無需走複製的流程。當我們使用引用的時候,如果實參與引用參數不匹配,那麼C++將產生臨時變,關於const引用卻有需要瞭解的知識點。
如果引用形參是const,則C++編譯器將在下麵兩種情況下生成臨時變數:

  • 實參的類型正確,但不是左值。
  • 實參的類型不正確,但可以轉換為正確的類型。

左值:在C語言中,左值最初指的是可出現在賦值語句左邊的實體,但現在,常規變數和const變數都視為左值,因為可以通過地址訪問它們。
左值例子:變數,數組元素,結構成員,引用,解除引用的指針等。
非左值例子:字面常量(用引號括起的字元串除外,因為它們是地址),包含多項的表達式等。

代碼例子如下:

double refcube (const double &ra){return ra * ra * ra;}
double side = 3.0;
long edge = 5L;

double x1 = refcube(edge);//實參類型不正確,但可以轉換為正確的類型
double x2 = refcube(7.0);//實參類型正確,但不是左值(字面常量)
double x3 = refcube(side+10.0);//實參類型正確,但不是左值(表達式)

const全局變數

在C++中,const限定符對預設存儲類型稍有影響。預設情況下,C++全局變數的鏈接性是外部的,但const全局變數的鏈接性為內部的。也就是說,在c++看來,全局const定義就像使用了static說明符一樣。
C++這樣做有著很多的好處,這意味著每個文件都有自己的一組常量,而不是所有文件共用一組常量。因此我們可以將常量定義到頭文件中,這樣只要在兩個源代碼文件中包括同一個頭文件,則它們將獲得同一組常量。當然,如果我們希望某個常量的鏈接性為外部的,那麼我們可以使用extern關鍵字來覆蓋預設的內部連接線。extern const int states = 50;

cv-限定符

  • const
  • volatile
    const限定符表明,記憶體被初始化後,程式便不會再對它進行修改。
    volatile限定符表明,即使程式沒有對記憶體單元進行修改,但其中的值也可能發生變化。可能由於硬體的原因,也可能由其他程式修改,如共用數據。這個關鍵字的作用主要是為了改善編譯器的優化能力。
    防止編譯器將該值用緩存的方式進行優化。
    mutable限定符也是需要瞭解的。當我們聲明一個數據結構體為常量,而其中某個成員卻需要修改時。我們可以利用mutable限定符對需要修改的成員加以修飾。例子如下:
struct Data{
int x;
mutable int y;//聲明此成員是可被再次修改的。
};
const Data data = {1,2};//data實例是常量
data.y=3;//但data的y成員可以被再次修改!

上述代碼中,dataconst禁止程式修改data的成員,但由於y成員的mutable限定符說明瞭datay成員不受這種限制,仍然可以被再次修改。

const成員函數

請看如下的代碼片段:

class Data{
int x;
public:
void show(){std::cout <<x;};
}
//
const Data data = {1};
data.show();//這裡會報錯哦!

C++編譯器在data.show();會報錯,因為show();函數無法確保調用對象不被修改。很有可能show函數修改了data中的成員,因此C++編譯器為了保證data不會被再次修改,禁止了這種調用行為。在C++中,解決這類問題的辦法是const成員函數。
請看下麵的代碼:

class Data{
int x;
public:
void show() const {std::cout <<x;};//這裡的const!
}
//
const Data data = {1};
data.show();//這裡不會報錯了

對於show()的聲明應該類似於這樣:void show() const;
對於show()的定義應該類似於這樣:void Data::show() const {...}
這種方式聲明和定義的類函數被稱為const成員函數,const成員函數表示函數不會修改調用對象(類成員)。
最後,本人才疏學淺,可能會有很多錯誤,還望諸君見諒。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 工業網關是一種用於連接工業設備和網路的關鍵設備,它能夠將不同協議、不同傳輸速率的工業設備連接到網路上,實現數據的傳輸和共用。不同類型的工業網關之間存在一些區別,以下是一些常見的工業網關類型及其區別: ...
  • 1. 有人說 Python 性能沒那麼 Low? 這個我用 pypy 2.7 確認了下,確實沒那麼差, 如果用 NumPy 或其他版本 Python 的話,性能更快。但 pypy 還不完善,pypy3 在 beta, 所以一般情況,我是說一般情況下,這點比較讓人不爽。 2. 有人說怎麼沒有 C#、R ...
  • 1.創建 2.配置tomcat 3.創建webapp step01,war包 step02 創建web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xml ...
  • 3. Java程式流程式控制制(重點) 程式的三種控制結構 3.1 分支結構 if, switch 3.1.1 if if 分支 根據條件(真或假)來決定執行某段代碼。 if分支應用場景 if 第一種形式 執行流程: 首先判斷條件表達式的結果,如果為true執行語句體,為false就不執行語句體。 if ...
  • C-18.MySQL8其他新特性 1.MySQL8新特性概述 MySQL從5.7版本直接跳躍發佈了8.0版本,可見是一個令人興奮的里程碑的版本。MySQL 8版本在功能上,做了顯著的改進與增強,開發者對MySQL的源代碼進行了重構,最突出的一點是對MySQL Optimizer優化器進行了改進。不僅 ...
  • C++ MySQL資料庫連接池 新手學了C++多線程,看了些資料練手寫了C++資料庫連接池小項目,自己的源碼地址 關鍵技術點 MySQL資料庫編程、單例模式、queue隊列容器、C++11多線程編程、線程互斥、線程同步通信和 unique_lock、基於CAS的原子整形、智能指針shared_ptr ...
  • 數據過濾在數據分析過程中具有極其重要的地位,因為在真實世界的數據集中,往往存在重覆、缺失或異常的數據。pandas提供的數據過濾功能可以幫助我們輕鬆地識別和處理這些問題數據,從而確保數據的質量和準確性。 今天介紹的query函數,為我們提供了強大靈活的數據過濾方式,有助於從複雜的數據集中提取有價值的 ...
  • 目錄數組(Array)一、數組概念二、如何聲明一個數組三、如何為數組初始化1、數組本身初始化:2、數組的元素初始化2.1 一維數組2.2多維數組四、如何表示數組的各個概念五、數組記憶體和分配空間六、數組相關演算法七、十大內部排序演算法八、數組的工具類:Arrays九、數組的異常 數組(Array) 一、數 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...