[TOC] 1. 引用的定義 C++新增加了引用的概念: 引用可以看作一個已定義變數的別名 引用的語法 2. 引用的本質 引用在C++中的內部實現是一個常量指針 C++編譯器在編譯過程中使用常量指針作為引用的內部實現,因此引用所占用的記憶體大小和指針相同 從使用的角度,引用只是一個別名,C++為了實用 ...
目錄
1. 引用的定義
C++新增加了引用的概念:
- 引用可以看作一個已定義變數的別名
- 引用的語法
Type &name = var;
int a = 4;
int &b = a; //b為a的別名
b = 5; //操作b就是操作a
2. 引用的本質
- 引用在C++中的內部實現是一個常量指針
Type &name <==> Type *const name
- C++編譯器在編譯過程中使用常量指針作為引用的內部實現,因此引用所占用的記憶體大小和指針相同
- 從使用的角度,引用只是一個別名,C++為了實用性而隱藏了引用的存儲空間這一細節
#include <cstdio>
struct TRef
{
char &r;
};
int main(int argc, char *argv[])
{
char c = 'c';
char &rc = c;
TRef ref = { c };
printf("sizeof(rc) = %d\n", sizeof(rc));
printf("sizeof(TRef) = %d\n", sizeof(TRef));
printf("sizeof(ref) = %d\n", sizeof(ref));
printf("sizeof(ref.r) = %d\n", sizeof(ref.r));
/*sizeof(type &)的大小,就是type類型的大小*/
printf("sizeof(char &) = %d\n", sizeof(char &));
printf("sizeof(int &) = %d\n", sizeof(int &));
printf("sizeof(double &) = %d\n", sizeof(double &));
return 0;
}
#include <stdio.h>
struct TRef
{
char *before;
char &ref;
char *after;
};
int main(int argc, char *argv[])
{
char a = 'a';
char &b = a;
char c = 'c';
TRef r = {&a, b, &c};
printf("sizeof(r) = %d\n", sizeof(r));
printf("sizeof(r.before) = %d\n", sizeof(r.before));
printf("sizeof(r.after) = %d\n", sizeof(r.after));
printf("&r.before = %p\n", &r.before);
printf("&r.after = %p\n", &r.after);
return 0;
}
3. 引用的意義
- C++中的引用作為變數別名而存在,旨在大多數的情況下代替指針
- 引用可以滿足絕大多數需要使用指針的場合
- 引用可以避開由於指針操作不當而帶來的記憶體錯誤
- 引用相對於指針來說具有更好的可讀性和實用性
註意:由於引用的內部實現為指針,因此函數不能返回非靜態局部變數的引用
#include <stdio.h>
int &demo()
{
int d = 0;
printf("demo: d = %d\n", d);
return d;
}
int &func()
{
static int s = 0;
printf("func: s = %d\n", s);
return s;
}
int main(int argc, char *argv[])
{
int &rd = demo();
int &rs = func();
printf("\n");
printf("main: rd = %d\n", rd);
printf("main: rs = %d\n", rs);
printf("\n");
rd = 10;
rs = 11;
demo();
func();
printf("\n");
printf("main: rd = %d\n", rd);
printf("main: rs = %d\n", rs);
printf("\n");
return 0;
}
4. 特殊的引用—const引用
- 在C++中可以聲明const引用
const Type &name = var
- 可以使用const常量、變數、字面值常量對const引用初始化
- 不管使用何種方式初始化,const引用都將產生一個只讀變數
- 當使用字面值常量對const引用初始化時,C++編譯器會為常量值分配記憶體空間,並將引用作為這段記憶體空間的別名
const引用類型 VS 初始化變數類型
- 類型相同,const引用的就是初始化變數
- 類型不同,const引用的不是初始化變數,而是初始化變數的臨時對象
註意:const只是修飾符,不代表類型,也就是說,const int和int是相同類型。
#include <stdio.h>
int main()
{
const int a = 3;
int b = 4;
char c = 'c';
const int &ra = a;
const int &rb = b;
const int &rc = c;
const int &rd = 1;
int *p1 = (int *)&ra;
int *p2 = (int *)&rb;
int *p3 = (int *)&rc;
int *p4 = (int *)&rd;
*p1 = 5;
*p2 = 6;
*p3 = 7;
*p4 = 8;
printf("ra = %d\n", ra);
printf("rb = %d\n", rb);
printf("rc = %d\n", rc);
printf("rd = %d\n", rd);
printf("\n");
printf("b = %d\n", b); //b的類型和rb相同,rb引用的就是b,所以改變rb的值,b也跟著一起改變
printf("c = %c\n", c); //c的類型和rc不同,rb引用的是c的臨時對象,所以改變rc的值,c不受影響
return 0;
}
5. 引用和指針的關係
指針 | 引用 |
---|---|
指針是一個變數,其值為一個記憶體地址 | 引用是一個變數的新名字 |
指針可以不初始化,而是在使用時賦值 | 引用必須在定義時初始化 |
通過指針可以訪問對應記憶體地址中的值 | 對引用的操作(賦值、取地址等)會傳遞到代表的變數上 |
指針可以保存不同的地址 | 引用在初始化之後無法代表其他變數 |
指針可以被const修飾,成為常量或只讀變數 | const引用使其代表的變數具有隻讀屬性 |
在工程項目開發中
- 當進行C++編程時,直接站在使用的角度,引用和指針沒有任何關係
- 當對C++代碼進行調試分析時,一些特殊情況,可以考慮站在C++編譯器的角度,引用在內部實現為指針常量
我們給出一個站在C++編譯器的角度看待引用的示例,下麵這段代碼有問題嗎?
int a = 1;
int b = 2;
int *pc = new int(3);
int &array[] = {a, b, *pc};
- 數組是一片連續的記憶體空間
- 引用數組會破壞該特性,各元素代表的變數可能存儲在不同的位置
- 因此,C++不支持引用數組!!!!!!
#include <stdio.h>
int a = 1;
struct SV
{
int &x;
int &y;
int &z;
};
int main()
{
int b = 2;
int *c = new int(3);
SV sv = {a, b, *c};
int &array[] = {a, b, *c}; //&array[1] - &array[0] != 4,編譯報錯
printf("&sv.x = %p\n", &sv.x);
printf("&sv.y = %p\n", &sv.y);
printf("&sv.z = %p\n", &sv.z);
delete c;
return 0;
}
首先,註釋掉代碼第17行,編譯運行結果如下,可以看出列印出的記憶體地址是各不相同的。
然後,去除代碼第17行註釋,結果編譯報錯,原因就是數組的三個元素地址不連續,而是各不相同。