1. 程式人生 > >C++解析(9):關於const和引用的疑問

C++解析(9):關於const和引用的疑問

0.目錄

1.關於const的疑問

2.關於引用的疑問

3.小結

1.關於const的疑問

const什麼時候為只讀變數?什麼時候是常量

const常量的判別準則

  • 只有用字面量初始化的const常量才會進入符號表
  • 使用其它變數初始化的const常量仍然是只讀變數
  • volatile修飾的const常量不會進入符號表

(volatile代表所宣告的識別符號是易變的,是可能發生變化的,只不過這個可能性也許不在當前編譯的檔案當中,這個使得當前識別符號發生變化的可能性是發生在外部的,外部就相當於是其它檔案當中,比如多執行緒、中斷等等。volatile既然代表易變的,那麼每次訪問volatile所修飾的識別符號時應該到記憶體裡面去直接讀取。這就意味著被volatile所修飾的識別符號不可能進入符號表,也就是說當volatile和const同時修飾一個識別符號的時候,得到的僅僅是一個只讀變數,const在這裡的意義僅僅是說明在當前的檔案當中或者說當前的作用域當中,volatile和const同時修飾的識別符號不能出現在賦值符號的左邊。)

總結:在編譯期間不能直接確定初始值const識別符號,都被作為只讀變數處理。

const引用的型別與初始化變數的型別:

  • 相同初始化變數成為只讀變數
  • 不同生成一個新的只讀變數

示例程式:

#include <stdio.h>

int main()
{
    const int x = 1;
    const int& rx = x;
    
    int& nrx = const_cast<int&>(rx);
    
    nrx = 5;
    
    printf("x = %d\n", x);
    printf("rx = %d\n", rx);
    printf("nrx = %d\n", nrx);
    printf("&x = %p\n", &x);
    printf("&rx = %p\n", &rx);
    printf("&nrx = %p\n", &nrx);
    
    return 0;
}

執行結果:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
x = 1
rx = 5
nrx = 5
&x = 0x7ffd8a40b3cc
&rx = 0x7ffd8a40b3cc
&nrx = 0x7ffd8a40b3cc

使用volatile,是真正的常量嗎?
證明在編譯期間不能直接確定初始值的const識別符號,都被作為只讀變數處理。

#include <stdio.h>

int main()
{
    volatile const int y = 2;
    int* p = const_cast<int*>(&y);
    
    *p = 6;
    
    printf("y = %d\n", y);
    printf("p = %p\n", p);
    
    const int z = y;
    
    p = const_cast<int*>(&z);
    
    *p = 7;
    
    printf("z = %d\n", z);
    printf("p = %p\n", p);
    
    return 0;
}

執行結果:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
y = 6
p = 0x7fff2eab9144
z = 7
p = 0x7fff2eab9140

最後驗證const引用的型別與初始化變數的型別相同和不同的情況:

#include <stdio.h>

int main()
{
    char c = 'c';
    char& rc = c;
    const int& trc = c;
    
    rc = 'a';
    
    printf("c = %c\n", c);
    printf("rc = %c\n", rc);
    printf("trc = %c\n", trc);
    
    return 0;
}

執行結果:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
c = a
rc = a
trc = c

2.關於引用的疑問

2.1 引用與指標

引用指標有什麼關係?如何理解“引用的本質就是指標常量”?

指標是一個變數

  • 值為一個記憶體地址,不需要初始化,可以儲存不同的地址
  • 通過指標可以訪問對應記憶體地址中的值
  • 指標可以被const修飾成為常量或者只讀變數

引用只是一個變數的新名字

  • 對引用的操作(賦值,取地址等)都會傳遞到代表的變數上
  • const引用使其代表的變數具有隻讀屬性
  • 引用必須在定義時初始化,之後無法代表其它變數

2.2 從C++語言與C++編譯器角度看引用

使用C++語言的角度來看:

  • 引用指標沒有任何的關係
  • 引用是變數的新名字,操作引用就是操作對應的變數

從C++編譯器的角度來看:

  • 為了支援新概念“引用”必須要一個有效的解決方案
  • 在編譯器內部,使用指標常量來實現“引用”
  • 因此“引用”在定義時必須初始化

2.3 從工程專案開發看引用

在工程專案開發中:

  • 當進行C++程式設計時直接站在使用的角度看待引用,與指標毫無關係,引用就是變數的別名
  • 當對C++程式碼進行除錯分析時一些特殊情況,可以考慮站在C++編譯器的角度看待引用

下面的程式碼有問題嗎?

#include <stdio.h>

int a = 1;

struct SV
{
    int& x;
    int& y;
    int& z;
};

int main()
{
    int b = 2;
    int* pc = new int(3);
    SV sv = {a, b, *pc};
    int& array[] = {a, b, *pc}; // &array[1] - &array[0] = ?  Expected ==> 4
    
    printf("&sv.x = %p\n", &sv.x);
    printf("&sv.y = %p\n", &sv.y);
    printf("&sv.z = %p\n", &sv.z);
    
    delete pc;
    
    return 0;
}

執行結果為:

[[email protected] Desktop]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:17: error: declaration of ‘array’ as array of references

原因:引用陣列破壞了陣列地址空間的連續性!!!
C++不支援引用陣列!C++不支援引用陣列!C++不支援引用陣列!

註釋掉引用陣列後:

#include <stdio.h>

int a = 1;

struct SV
{
    int& x;
    int& y;
    int& z;
};

int main()
{
    int b = 2;
    int* pc = new int(3);
    SV sv = {a, b, *pc};
    
    printf("&sv.x = %p\n", &sv.x);
    printf("&sv.y = %p\n", &sv.y);
    printf("&sv.z = %p\n", &sv.z);
    
    delete pc;
    
    return 0;
}

執行結果為:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
&sv.x = 0x600b14
&sv.y = 0x7ffc32798de4
&sv.z = 0x23be010

3.小結

  • 指標是一個變數
  • 引用是一個變數的新名字
  • const引用能夠生成新的只讀變數
  • 在編譯器內部使用指標常量實現“引用”
  • 編譯時不能直接確定初始值const識別符號都是只讀變數