1. 程式人生 > >C++高階進階 第一季:const 詳解

C++高階進階 第一季:const 詳解

零、文章來由

打算將基礎知識在看書的同時系統的整理一下,方便大家也方便自己。整理的知識儘量參照書本知識,比網上獲取的資料有更高的可信度。

一、從 文字常量和常變數 開始

這裡寫圖片描述

1、文字常量

程式中的特殊識別符號表示式,由於同時滿足:
(1)不可定址(放在程式碼區)
(2)值不可變
所以可視為文字常量。他們是 靜態陣列名、列舉變數、全域性(靜態變數)首地址、#define定義的常量

整型文字常量:
(1)前加0表示 八進位制
(2)前加0x表示 十六進位制
(3)後加L(推薦)或l,表示long型別
(4)後加U(或u)表示無符號數

eg.1024UL

2、常變數Const

同其他變數一樣被分配空間,可定址。

const是在高階語言的語義層面上定義的,是編譯器在編譯期做語法檢測來保證,但是執行時,const變數不是在只讀記憶體中,而是和一般變數一樣放在資料區,所以一樣可以對其進行修改。

所以:常變數是一種加了特殊限制的變數,理解成“只讀”變數

即使是const修飾,也是可以修改的

#include <iostream>
using namespace std;

void ShowValue(const int &i) {
    cout<<i<<endl;
}

int main() 
{
    const
int j=5; void *p=(void *)&j; int *ptr=(int *)p; (*ptr)++; //cout<<j<<endl; //還是會顯示5,因為編譯器優化的時候將j替換為文字常量5 //但如果是int i=5; const int j=i; 則無法替換,直接輸出j為6 ShowValue(j); //顯示6 return 0; }

或:

#include <iostream>
using namespace std;

void ShowValue(const int &i) {
    cout
<<i<<endl; } int main(int argc,char *argv[]) { int tmp = 5; const int & aa =tmp; ShowValue(++(*((int *)&aa))); //顯示6 return 0; }

3、常變數替換

如果常變數有初始化賦初值,那編譯器將該常變數在其他地方替換成文字常量
但是如果開始不初始化就會錯誤

如:

void DefineArray(const int n){
     int B[n]={}; //error,陣列大小在編譯期確定
}

int main(){
     const int m=5;
     int A[m]={}; //ok
}

4、文字常量和常變數定址

int &r=5; //error,無法定址文字常量,無法建立引用

const int &r=5; //ok,在資料區開闢一個值為5的無名整數量,然後將引用r與這個整形兩繫結

二、const用法

1、const的位置

int const *p; //指向常量的指標(即常指標,const修飾的是int),指向的物件是const型,不可以修改,但是指標p的指向可以修改
int *const p; //指標常量(const修飾的是int*),指標變數p是const型,它的指向不可修改,但是指向的物件可以修改

const和資料型別結合在一起 —>“常型別”。(看成一個整體)

修飾型別時,既可以放在放前面,也可以放在後面;用常型別宣告 or 定義變數,const只出現在變數前

const和被修飾型別間不能有其他識別符號存在。

引用本身可以理解成一個指標常量

故在引用前使用const沒有意義

int & const r4=i; //const是多餘的,編譯器warning後忽略const存在

const配合二重指標,此例子中const在不同位置,結果不同

#include <iostream>
using namespace std;

int main() 
{
    int const **p1; //不是指標常量,指向 int count*(“int const*”是一個 指向整型常量的指標)
    int *const *p2; //不是指標常量,但所指的變數是指標常量(int *const,即指向整型的指標常量,指向不能修改)

    int i=5;
    int j=6;

    const int *ptr1=&i;
    int *const ptr2=&j;

    p1=&ptr1;
    p2=&ptr2;

    cout<<**p1<<endl;
    cout<<**p2<<endl;

    return 0; 
}

輸出:
5
6

上述p1和p2 賦值有講究,如果 p1=&ptr2 或 p2=ptr1 就會編譯錯誤

2、const修飾某個類 —> 常物件 和 常函式

const修飾物件–>常物件
const修飾成員函式—>常函式

在常函式中,不允許對任何成員變數進行修改

通過常物件,只能呼叫該物件的常函式

#include <iostream>
using namespace std;

class A
{
    int num;
public:
    A() {num=5;}
    void disp();
    void disp() const;
    void set(int n) {num=n;}

};

void A::disp() const {
    cout<<num<<endl;
}

void A::disp() {
    cout<<"non-const version of disp()"<<endl;
}

int main() 
{
    A a1;
    a1.set(3);
    a1.disp();
    A const a2;
    a2.disp();
}

以上注意:
(1)如果常函式宣告和定義分開,都需要加const,否則編譯錯誤

只有類的非靜態成員函式可以被宣告為常函式

(2)如果一個類的兩個成員函式,返回值、函式名、引數列表完全相同,其中之一是const,則過載。因為 常成員函式的引數傳入this指標是const Class*型別的,引數不同,導致函式簽名不同。

非只讀物件(如a1)呼叫某個函式(如 disp()),先找非const版本,如果沒有,再呼叫const版本。而常物件,只能呼叫類中定義的常函式,否則編譯器報錯。

如果一個非const物件(如a1)呼叫函式,同時有const和非const版本的函式,我們希望其呼叫const函式。就必須建立該物件的常引用,或指向該物件的常指標來達到目的。如: (const A&)a1.disp(); 或 (const A *)&a1->disp();

(3)常物件建立後,其資料成員不允許在修改。所以顯示建構函式來初始化該物件非常重要。

常物件,全體成員資料成員都是常量看待。
類物件的非靜態常量成員必須在建構函式中初始化,且只能藉助初始化列表進行。

3、const修飾函式引數+函式返回值

#include <iostream>
using namespace std;

void disp1(const int &ri){
    cout<<ri<<endl;
}

void disp2(const int i){
    cout<<i<<endl;
}

const int disp3(const int& ri){
    cout<<ri<<endl;
    return ri;
}

int& disp4(int& ri){
    cout<<ri<<endl;
    return ri;
}

const int& disp5(int& ri){
    cout<<ri<<endl;
    return ri;
}


int main(int argc,char* argv[])
{
    int n=5;
    disp1(n);
    disp2(n);
    disp3(n);
    disp4(n)=6; //修改引用返回值
    disp5(n);//disp5(n)=6;是錯誤的
}

注意:
(1)const修飾引數,主要作用是被引用物件被指向物件,如果只是形參,就沒有多少意義。如:void disp2(const int i),這裡的i在函式中改不改變,加不加const沒什麼影響。

不但如此,同時定義一個相似的用const修飾引數和不用const修飾引數的函式,會引起重定義錯誤。比如:任何整型表示式的值,都可以傳給int型參變數,也可以傳給const int型參變數,故不過載。

(2)當返回值是一個普通資料,而非引用,const修飾也沒多少意義。因為函式返回值是一個非左值,本來就不能改變其值。故其上 const int disp3(const int& ri),對返回值修飾然並卵。

(3)如果返回值為引用,用const修飾可以阻止對被引用物件修改,disp5(n)=6;是錯誤的

(4)常見的對const的誤解。

誤解一:用const修改的變數值一定是不能改變的。const修飾的變數可通過指標可間接修改。

如:

const int j=5;
void *p=(void *)&j;
int *ptr=(int *)p;
(*ptr)++;

誤解二:常引用或常指標,只能指向常變數,這是一個極大的誤解。常引用或者常指標只能說明不能通過該引用(或者該指標)去修改被引用的物件,至於被引用物件原來是什麼性質是無法由常引用(常指標)決定的。

三、const_cast 的用法

1、作用

const_cast 是 C++ 運算子,作用是去除符合型別中的const或者volatile

當大量使用const_cast是不明智的,只能說程式存在設計缺陷。使用方法見下例:

void constTest(){
    int i;
    cout<<"please input a integer:";
    cin>>i;
    const int a=i;
    int& r=const_cast<int&>(a);//若寫成int& r=a;則發生編譯錯誤
    ++r;
    cout<<a<<endl;
}
int main(int argc,char* argv[])
{
    constTest();
    return 0;
}

輸入:
5

輸出:
6

總結:
(1)const_cast運算子的語法形式是const_cast< type> (expression)。 括號不可省略

(2)const_cast只能去除目標的const或者volatile屬性,不能進行不同型別的轉換。只能將 const type* 轉換為 type*,或者 const type & 轉換為 type &。
如下轉換就是錯誤的:

cons tint A={1,2,3}; 
char* p=const_cast< char*>(A); //不能由const int[]轉換為char* 

(3)一個變數被定義為只讀變數(常變數),那麼它永遠是常變數。cosnt_cast取消的是間接引用時的改寫限制,而不能改變變數本身的const屬性。 如下就是錯誤的:

int j = const_cast< int> (i);

(4)利用傳統的C語言中的強制型別轉換也可以將 const type* 型別轉換為 type* 型別,或者將 const type& 轉換為 type& 型別。但是使用 const_cast 會更好一些,因為 const_cast 寫法複雜(提醒程式猿不要輕易轉換),轉換能力較弱,目的明確,不易出錯,易查bug;而C風格的強制型別轉換能力太強,風險較大。

參考資料

[1]陳剛.C++高階進階教程[M].武漢:武漢大學出版社,2008.

相關推薦

C++高階 第一const

零、文章來由 打算將基礎知識在看書的同時系統的整理一下,方便大家也方便自己。整理的知識儘量參照書本知識,比網上獲取的資料有更高的可信度。 一、從 文字常量和常變數 開始 1、文字常量 程式中的特殊識別符號或表示式,由於同時滿足: (1)不可

C語言-第一作業01

1字串比對(10分) 題目內容: 題目說起來很簡單,你會讀到兩個字串,每個字串佔據一行,每個字串的長度均小於10000字元,而且第一個字串的長度小於第二個字串的。你的程式要找出第一個字串在第二個字串

玩轉資料結構入門與——第一陣列

內容大綱: 使用Java中的陣列 二次封裝屬於我們自己的陣列 向陣列中新增元素 陣列中查詢元素和修改元素 包含,搜尋,刪除功能 使用泛型 動態陣列 簡單的時間複雜度分析 均攤複雜度和防止複雜度振盪 一、java中的陣列 把資

Objective C 高階— GCD佇列淺析(一)

前邊已經介紹了GCD的基本概念,下邊來介紹一下GCD佇列的基本用法,以及一些其他注意的地方。 本文參考連結:https://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_li

Objective C 高階 — GCD佇列淺析(二)

前邊講解了如何建立GCD佇列,下邊接著來講解如何執行一個佇列, 請看官方原文 dispatch_async Submits a block for asynchronous execution on a dispatch queue and returns immedia

FFMPEG系列01-ffplay命令

概述 ffplay是一個基於FFMPEG庫和SDL庫開發的多媒體播放器。它的主要目的是是用來測試FFMPEG的各種API,比如codec/format/filter等等庫。 掌握ffplay的設計邏輯,對於播放器開發人員提升經驗非常有幫助。嗶哩嗶哩的ijkplayer就是基於ffplay做的二次開

Android——屬性動畫Property Animation與應用(二)

引言 前一篇Android進階——屬性動畫Property Animation詳解(一)總結了屬性動畫的絕大部分核心知識點,這篇主要是補充下欠缺的知識點——Layout佈局動畫和View的動畫相關知識、自定義TypeEvaluator屬性動畫的應用。 一、佈

Jmeter(三十四) - 從入門到精通篇 - 引數化(教程)

1.簡介   前邊三十多篇文章主要介紹的是Jmeter的一些操作和基礎知識,算是一些初級入門的知識點,從這一篇開始我們就來學習Jmeter比較高階的操作和深入的知識點了。今天這一篇主要是講引數化,其實前邊或多或少的介紹過類似的知識點,知識沒有系統的講解,由於這個在實際工作中用到比較多而且經常用到,所以巨集哥今

DDD實戰第一波(五)開發一般業務的大健康行業直銷系統(實現產品上下文領域層)

討論 clas 基本 ted ctc decimal nco protect pan 從這篇文章開始,我們根據前面的DDD理論與DDD框架的約束,正式進入直銷系統案例的開發。 本篇文章主要講產品上下文中的領域層的主要實現,先簡單講下業務方面的需求:產品SPU與產品SKU,產

ES6入門到第一節 定義變數 let const

本文是自己總結的,把一些概念寫到程式碼裡了,程式碼可以直接執行。 註釋有點多,執行時候注意一下就好了。  let 具備塊級作用域 先定義再使用,同一個作用域裡不能重複定義同一個變數,不會覆蓋會報錯  const 常量 定義好了不能改變,宣告完必須賦值,沒有變數提升 &nb

C++模板指南SFINAE

C++模板進階指南:SFINAE 空明流轉(https://zhuanlan.zhihu.com/p/21314708) SFINAE可以說是C++模板進階的門檻之一,如果選擇一個論題來測試對C++模板機制的熟悉程度,那麼在我這裡,首選就應當是SFINAE機制。 我們不用糾結這個詞的發音,它來自於 Su

POJ C程式設計 程式設計題#2二維陣列從右上左下遍歷

程式設計題#2: 二維陣列右上左下遍歷 來源: POJ (Coursera宣告:在POJ上完成的習題將不會計入Coursera的最後成績。) 注意: 總時間限制: 1000ms 記憶體限制: 65536kB 描述 給定一個row行col列的整數陣列array,要求從

屬性動畫2ValueAnimator高階(一)

1. 概述 前面一篇屬性動畫1:基礎知識和ValueAnimator寫完,我對屬性動畫基礎知識和ValueAnimator的簡單用法有了一些瞭解。要想把屬性動畫吃透,我感覺需要更加深入的學習。現在,就從ValueAnimator的高階進階開始,繼續攻克

C語言-第15~16講結構體應用(學生成績統計)

任務和程式碼:         學生成績統計:         每位同學的資訊學號、姓名、C、高數、英語成績。定義一個學生成績的結構體陣列,其中的資料成員包括學號(char num[13])、姓名(name)、三門課的成績(grade)、總分(score))       

C語言剖析 09 const 和 volatile

文章目錄 const 只讀變數 const 全域性變數的分歧 程式設計實驗: const 的變數本質 const 的本質 例項分析: const 的本質分析 const

c# MEF框架(四 MEF高階

轉自:http://www.cnblogs.com/yunfeifei/p/3991330.html 好久沒有寫部落格了,今天抽空繼續寫MEF系列的文章。有園友提出這種系列的文章要做個目錄,看起來方便,所以就抽空做了一個,放到每篇文章的最後。 前面四篇講了MEF的基礎

Vue 教程之 v-model

com 方式 事件 變化 復習 簡寫 mage fine 需要 分享 Vue 官網教程上關於 v-model 的講解不是十分的詳細,寫這篇文章的目的就是詳細的剖析一下, 並介紹 Vue 2.2 v-model改進的地方,然後穿插的再說點 Vue 的小知識。 在 Vue 中,

Java知識點2看不懂的代碼 - 協變與逆變

階段 off 開始 限制 array 設計者 equals 性方面 版本兼容 要搞懂Java中的協辦與逆變,不得不從繼承說起,如果沒有繼承,協變與逆變也天然不存在了。 我們知道,在Java的世界中,存在繼承機制。比如MochaCoffee類是Coffee類的派生類,那麽我

翻譯(六)——T-SQL的之路超過基礎的2級水平寫子查詢

相關 完整 圖像 reg 想要 試驗 releases 驗證 不用 T-SQL的進階之路:超過基礎的2級水平:寫子查詢 格雷戈裏·拉森(Gregory Larsen),2016/01/01(第一次出版:2014/01/29) 該系列 這篇文章是樓梯系列的

第一課 Python模塊簡介

pre 導入 nbsp 自己的 自己 proc 什麽 第一課 ftime 一、前面我們說過,Python自從出世以來,就是免費、開放的。如今造就了數目龐大的模塊。那麽問題來了,模塊是什麽? 對於Python而言,模塊是若幹功能的集合。 一個模塊有自己的屬性,就像一個人有自己