- 引用鋪墊:變量名
變量名實(shí)質(zhì)上是一段連續(xù)存儲空間的別名疼鸟,是一個(gè)標(biāo)號(門牌號)翩腐,在程序中是通過變量來申請并命名內(nèi)存空間的窃肠,通過變量的名字可以直接使用對應(yīng)的內(nèi)存空間。
對一段內(nèi)存空間可以起除了變量名之外的其他別名垛玻,如果我們以“先入為主”的想法看待變量名和它對應(yīng)的內(nèi)存空間,那就是說:暫時(shí)地奶躯,這個(gè)變量名就是這段內(nèi)存空間帚桩。
那么引用(對這段內(nèi)存空間起了另外一個(gè)名字)的本質(zhì)就是變量的別名。
- 在C++中增加了引用的概念:引用可以看做一個(gè)已定義變量的別名嘹黔;
- 引用的語法: <類型名>& <別名> = <原變量名>账嚎;
- 引用做函數(shù)參數(shù)
-
普通的引用在聲明時(shí)必須使用其他的變量進(jìn)行初始化;
引用作為函數(shù)參數(shù)聲明時(shí)不用進(jìn)行初始化儡蔓。
#include <iostream>
using namespace std;
void myswap(int &a, int &b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int a = 10, b = 20;
cout << "a: " << a << "\tb: " << b << endl;
myswap(a, b);
cout << "a: " << a << "\tb: " << b << endl;
cout << "hello world.." << endl;
return 0;
}
- 復(fù)雜數(shù)據(jù)類型的引用
#include <iostream>
using namespace std;
struct Teacher
{
char name[64];
int age;
};
void printfT(Teacher *pT)
{
cout << pT->age<<endl;
}
void printT2(Teacher &pT)
{
cout << pT.age <<endl; // 因?yàn)槭亲兞縿e名郭蕉,就把引用看做一個(gè)變量
}
int main()
{
Teacher t1;
t1.age = 35;
printfT(&t1);
printT2(t1); // 引用做函數(shù)參數(shù)不用初始化,直接傳過去
cout << "hello world ..." << endl;
return 0;
}
- 引用的意義
- 引用作為其他變量的別名存在喂江,在一些場合可以直接代替指針使用召锈;
- 引用相對于指針來說,對程序員具有更好的可讀性(省去了輸入星號*還有括號)和實(shí)用性开呐;
- 引用的本質(zhì)
- 5.1 引用在C++內(nèi)部就是一個(gè)常指針——指向一個(gè)固定的內(nèi)存空間
variableType &name <==> variableType * const name;
- 5.2 C++編譯器在編譯過程中使用常指針作用引用的內(nèi)部實(shí)現(xiàn)烟勋,因此引用占用的空間大小和一個(gè)指針變量相同,通過代碼的例子2有明確的演示
#include <iostream>
using namespace std;
struct Teacher
{
/***************例子2******************/
char name[64]; // 64byte
int age; // 4byte
double salary; //12byte
// 上面一共占80byte
int &a = age; // 8byte
// 再次測量共占空間88byte
};
int main()
{
int a = 10;
int &b =a;
printf("sizeof (Teache)i: %d\n", sizeof(Teacher));
/********例子1*****************************************/
printf("&a: %d\n", (int *)&a); /*********/
printf("&b: %d\n", (int *)&b); /*********/
/***************顯示效果**********************************/
/***********&a: 1670882700
***********&b: 1670882700
*****************************************************/
/************a和b就是統(tǒng)一段內(nèi)存空間的門牌號***********/
/*****************************************************/
cout << "hello world ..." << endl;
return 0;
}
- 5.3 從實(shí)用的角度筐付,引用會讓讓人誤會其只是一個(gè)別名卵惦,沒有自己的內(nèi)存空間,但其實(shí)這是C++為了實(shí)用性而做出的細(xì)節(jié)隱藏瓦戚。
程序員只需要給出實(shí)參變量沮尿,編譯器代替我們把定義指針和建立關(guān)聯(lián)一起做掉了。
void func(int &a) | void func(int *const a)
{ | {
a = 5; | *a = 5;
} | }
- 使用引用
6.1 引用作為函數(shù)的返回值
結(jié)論:若函數(shù)內(nèi)部的一個(gè)臨時(shí)變量要作為引用返回,則這段代碼是有風(fēng)險(xiǎn)的畜疾,盡量不要這么做赴邻。
還是到代碼里面說問題。
#include <iostream>
using namespace std;
int& getAA2()
{
int a;
a = 10;
return a; //函數(shù)在這里試圖返回一個(gè)棧上的變量啡捶,而且返回類型是引用
}
int main()
{
int a2 = 0;
a2 = getAA2();
int &a3 = getAA2();
printf("a2: %d\n", a2);
printf("a3: %d\n", a3);
cout << "Hello world..." << endl;
return 0;
}
運(yùn)行結(jié)果:
- windows:
a2: 10
a3: 20655117
Hellod world...
請按任意鍵繼續(xù)... - Ububtu:
Command terminated
Press ENTER or type command to continue
發(fā)現(xiàn)在g++編譯器下代碼無法通過編譯姥敛,但是在VisualStudio 2015的編譯器雖然通過,但是a3的值是一堆亂碼瞎暑。
首先說明彤敛,g++編譯器在引用的上的檢查比VS2015嚴(yán)格一些, 而且編譯器提示:warning: reference to local variable ‘a(chǎn)’ returned 。
那么了赌,VS2015中的亂碼怎么出現(xiàn)墨榄?這需要繞一下來講。
我們已經(jīng)知道勿她,引用的實(shí)質(zhì)就是“常指針袄秩。”
在a2 = getAA2();這一次調(diào)用中逢并,函數(shù)運(yùn)行到return a;
a是一個(gè)int類型變量之剧;
返回值類型為int&引用,則返回機(jī)制創(chuàng)建的副本變量為:int& tempVariable = a;(我們借助這個(gè)tempVariable可以獲得較為直觀的理解)
回到main函數(shù)初始的位置筒狠,a2 = getAA2(); 則即a2 = tempVarible; tempVariable是一個(gè)引用猪狈,本質(zhì)是常指針,指向了存儲函數(shù)的返回值的內(nèi)存地址(這個(gè)地址是什么暫時(shí)不清楚辩恼,應(yīng)該也不重要)雇庙。
在int& a3 = getAA2();這一次調(diào)用中,函數(shù)運(yùn)行到return a;
a是一個(gè)int類型變量灶伊;
返回值類型為int&引用疆前,則返回機(jī)制創(chuàng)建的副本變量為:int& tempVariable = a;(我們借助這個(gè)tempVariable可以獲得較為直觀的理解)
回到main函數(shù)初始的位置,int& a3 = getAA2(); 則a3中保存的實(shí)際是一個(gè)常指針聘萨,它指向局部變量a的地址竹椒,但是a現(xiàn)在已經(jīng)被釋放,原來地址中是一個(gè)隨機(jī)的數(shù)值;
而在后面的打印指令中米辐,編譯器檢測到要打印引用a3的值胸完,那么自動轉(zhuǎn)換為*(常指針),于是就取到了原來地址里面的隨機(jī)數(shù)翘贮。
至于g++編譯器赊窥,在檢測到引用作為了右值之后,代碼只要一執(zhí)行函數(shù)對用的語句狸页,就自動down掉了锨能。
所以說一條保守的結(jié)論就是:不要使用引用對變量賦初始值。
當(dāng)函數(shù)返回值為引用時(shí):
若返回棧變量,1)不能作為其他引用的初始值址遇;2)不能作為左值使用熄阻;
若返回靜態(tài)變量或全局變量,既可作為右值使用倔约,也可作為左值使用秃殉。
- 指針的引用
在這部分使用代碼比較就可以看到使用指針引用的作用,它和C語言中的函數(shù)通過二級指針修改實(shí)參的內(nèi)容做的是一樣的事情跺株。
struct Teacher
{
char name[64];
int age;
};
// 在被調(diào)用函數(shù)中獲取資源
int getTeacher(Teacher **p)
{
int nRet = 0;
if (p == NULL) {
nRet = -1;
printf("func getTeacher():: detect that (p == NULL), error code: %d\n", nRet);
return nRet;
}
Teacher *tmp = NULL;
tmp = (Teacher *)malloc(sizeof(Teacher));
if (tmp == NULL) {
nRet = -2;
printf("func getTeacher():: detect that (tmp == NULL), error code: %d\n", nRet);
return nRet;
}
tmp->age = 33;
// *p是實(shí)參的地址 *實(shí)參的地址區(qū)間接地修改實(shí)參的值
*p = tmp;
return nRet;
}
int getTeacher2(Teacher * &myp)
{
// c++中指針引用作為函數(shù)的參數(shù)就是這種形式:類型 + &引用名
// Teacher *的意思就是說這是個(gè)Teacher類型的指針
int nRet = 0;
// 此處myp是實(shí)參的引用复濒,給myp賦值就是給main函數(shù)中的pT1賦值
// 這里就是通過<指針的引用>達(dá)到了<二級指針>的效果
myp = (Teacher *)malloc(sizeof(Teacher));
if (myp == NULL) {
nRet = -1;
printf("func getTeacher2():: detects that (myp == NULL), error code: %d\n", nRet);
return nRet;
}
myp->age = 36;
return nRet;
}
可以看到,這兩段函數(shù)實(shí)現(xiàn)的功能是一樣的乒省,但是代碼量相比后者要比前者少一些。因此C++中的引用幫助程序員節(jié)省了一些時(shí)間和操作畦木。
- 常引用
引用分為常引用和普通引用袖扛。
其中,普通引用的形式如下
int a = 10;
int &b = a;
常引用的形式則是
int x = 10;
const int &y = x;
常引用的作用是讓變量擁有“只讀”屬性,不能用過引用去修改原來的變量的值十籍。
常引用的初始化蛆封,有兩種情況
1). 用變量初始化常引用
{
// 用變量去初始化常引用
int x1 = 30;
const int &y = x1;
}
2). 用常量去初始化常引用
{
const int a = r410; // c++編譯器把a(bǔ)放在符號表中
int &m = 41; // 這個(gè)普通引用,引用的是一個(gè)字面量
// 這時(shí)勾栗,字面量只是一個(gè)值惨篱,它是沒有內(nèi)存空間的
// 但是引用的本質(zhì)就是給內(nèi)存起一個(gè)別名
// 所以上面的這行代碼編譯器不會編譯通過,那么改怎么辦围俘?
// 答案:加一個(gè)const砸讳!
const int &m = 43;
// c++編譯器是怎么工作的:
// 編譯器會多分配一個(gè)內(nèi)存空間,賦值43.再把這個(gè)內(nèi)存空間起別名為:m
const int &m = 42;
}
---END