溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++引用的特性是什么

發(fā)布時(shí)間:2022-01-17 09:02:08 來源:億速云 閱讀:157 作者:iii 欄目:開發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“C++引用的特性是什么”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“C++引用的特性是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識(shí)吧。

    一、 引用概念

    引用不是新定義一個(gè)變量,而是給已存在變量取了一個(gè)別名,語法理解上程序不會(huì)為引用變量開辟內(nèi)存空間,它和它引用的變量共用同一塊內(nèi)存空間

    比如:李逵,在家稱為"鐵牛",江湖上人稱"黑旋風(fēng)"

    C++引用的特性是什么

     類型& 引用變量名(對(duì)象名) = 引用實(shí)體

    int main()
    {
    	//有一塊空間a,后面給a取了三個(gè)別名b、c、d
    	int a = 10;
    	int& b = a;
    	int& c = a;
    	int& d = b;
    	//char& d = a;//err,引用類型和引用實(shí)體不是同類型(這里有爭(zhēng)議————char a = b[int類型],留個(gè)懸念,下面會(huì)解答)
    	//會(huì)被修改
    	c = 20;
    	d = 30;
    	return 0;
    }

    注意

    • 引用類型必須和引用實(shí)體是同種類型

    • 注意區(qū)分 ‘&’ 取地址符號(hào)

    二、 引用特性

    • 引用在定義時(shí)必須初始化

    • 一個(gè)變量可以有多個(gè)引用

    • 引用一旦引用一個(gè)實(shí)體,再不能引用其他實(shí)體

    int main()
    {
    	//int& e;//err
    	int a = 10;
    	int& b = a;
    	//這里指的是把c的值賦值于b
    	int c = 20;
    	b = c;
    	return 0;
    }

    三、 常引用

    void TestConstRef()
    {
    	const int a = 10;
    	//int& ra = a; //該語句編譯時(shí)會(huì)出錯(cuò),a為常量;由const int到int
    	const int& ra = a;//ok
    	int b = 20;
    	const int& c = b; //ok,由int到const int
    	//b可以改,c只能讀不能寫
    	b = 30;
    	//c = 30;//err
    	//b、c分別起的別名的權(quán)限可以是不變或縮小
    	int& d = b;//ok
    	//int& e = c//err
    	const int& e = c;//ok
    	//int& f = 10; // 該語句編譯時(shí)會(huì)出錯(cuò),b為常量
    	const int& g = 10;//ok
    	int h = 10;
    	double i = h;//ok
    	//double& j = h;//err
    	const double& j = h;//ok
    	//?為啥h能賦值給i了(隱式類型轉(zhuǎn)換),而給h起一個(gè)double類型的別名卻不行————如果是僅僅是類型的問題那為啥加上const就行了?
    	//double i = h;并不是直接把h給i,而是在它們中間產(chǎn)生了一個(gè)臨時(shí)變量(double類型、常量),并利用這個(gè)臨時(shí)變量賦值
    	//也就是說const double& j = h;就意味著j不是直接變成h的別名,而是變成臨時(shí)變量(doublde類型)的別名,但是這個(gè)臨時(shí)變量是一個(gè)常量,這也解釋了為啥需要加上const
    }

    小結(jié)

    1.我能否滿足你變成別名的條件:可以不變或者縮小你讀寫的權(quán)限 (const int -> const int 或 int -> const int),而不能放大你讀寫的權(quán)限 (const int -> int)

    2.別名的意義可以改變,并不是每個(gè)別名都跟原名有一樣的權(quán)限

    3.不能給類型不同的變量起別名的真正原因不是類型不同,而是隱式類型轉(zhuǎn)換后具有常性了

     常引用的意義 (舉例棧)

    typedef struct Stack
    {
    	int* a;
    	int top;
    	int capacity;
    }ST;
    void InitStack(ST& s)//傳引用是為了形參的改變影響實(shí)參
    {//...}
    void PrintStack(const ST& s)//1.傳引用是為了減少拷貝 2. 同時(shí)保護(hù)實(shí)參不會(huì)被修改
    {//...}
    void Test(const int& n)//即可以接收變量,也可以接收常量
    {//...}
    int main()
    {
    	ST st;
    	InitStack(st);
    	//...
    	PrintStack(st);
    	int i = 10;
    	Test(i);
    	Test(20);
    	return 0;
    }

    小結(jié)

    1.函數(shù)傳參如果想減少拷貝使用引用傳參,如果函數(shù)中不改變這個(gè)參數(shù)最好使用 const 引用傳參

    2.const 引用的好處是保護(hù)實(shí)參,避免被誤改,且它可以傳普通對(duì)象也可以傳 const 對(duì)象

    四、 使用場(chǎng)景

    1、做參數(shù) 

    void Swap1(int* p1, int* p2)
    {
    	int temp = *p1;
    	*p1 = *p2;
    	*p2 = temp;
    }
    void Swap2(int& rx, int& ry)
    {
    	int temp = rx;
    	rx = ry;
    	ry = temp;
    }
    int main()
    {
    	int x = 3, y = 5;
    	Swap1(&x, &y);//C傳參
    	Swap2(x, y);//C++傳參
    	return 0;
    }

    在 C++ 中形參變量的改變,要影響實(shí)參,可以用指針或者引用解決

    意義:指針實(shí)現(xiàn)單鏈表尾插 || 引用實(shí)現(xiàn)單鏈表尾插

    指針

    C++引用的特性是什么

    引用
    void SListPushBack(SLTNode*& phead, int x)
    {
    	//這里phead的改變就是plist的改變
    }
    void TestSList2()
    {
    	SLTNode* plist = NULL;
    	SListPushBack(plist, 1);
    	SListPushBack(plist, 2);
    }

    有些書上喜歡這樣寫 (不推薦)

    typedef int SLTDataType;
    typedef struct SListNode
    {
    	SLTDataType data;
    	struct SListNode* next;
    }SLTNode, *PSLTNode;
    void SListPushBack(PSLTNode& phead, int x)
    {
    	//...
    }

    2、做返回值 

    2.1、傳值返回
    //傳值返回
    int Add(int a, int b)
    {
    	int c = a + b;
    	return c;//需要拷貝
    }
    int main()
    {
    	int ret = Add(1, 2);//ok, 3
    	Add(3, 4);
    	cout << "Add(1, 2) is :"<< ret <<endl;
    	return 0;
    }

    Add 函數(shù)里的 return c; &mdash;&mdash; 傳值返回,臨時(shí)變量作返回值。如果比較小,通常是寄存器;如果比較大,會(huì)在 main 函數(shù)里開辟一塊臨時(shí)空間

    怎么證明呢

    int Add(int a, int b)
    {
    	int c = a + b;
    	return c;
    }
    int main()
    {
    	//int& ret = Add(1, 2);//err
    	const int& ret = Add(1, 2);//ok, 3
    	Add(3, 4);
    	cout << "Add(1, 2) is :"<< ret <<endl;
    	return 0;
    }

    從上面就可以驗(yàn)證 Add 函數(shù)的返回值是先存儲(chǔ)在臨時(shí)空間里的

    2.2、傳引用返回
    //傳引用返回
    int& Add(int a, int b)
    {
    	int c = a + b;
    	return c;//不需要拷貝
    }
    int main()
    {
    	int ret = Add(1, 2);//err, 3
    	Add(3, 4);
    	cout << "Add(1, 2) is :"<< ret <<endl;
    	return 0;
    }

    結(jié)果是不確定的,因?yàn)?Add 函數(shù)的返回值是 c 的別名,所以在賦給 ret 前,c 的值到底是 3 還是隨機(jī)值,跟平臺(tái)有關(guān)系 (具體是平臺(tái)銷毀棧幀時(shí)是否會(huì)清理?xiàng)臻g),所以這里的這種寫法本身就是越界的 (越界抽查不一定報(bào)錯(cuò))、錯(cuò)誤的

    發(fā)現(xiàn)這樣也能跑,但詭異的是為啥 ret 是 7

    //傳引用返回
    int& Add(int a, int b)
    {
    	int c = a + b;
    	return c;
    }
    int main()
    {
    	int& ret = Add(1, 2);//err, 7
    	Add(3, 4);
    	cout << "Add(1, 2) is :"<< ret <<endl;
    	return 0;
    }

    在上面我們?cè)?VS 下運(yùn)行,可以得出編譯器并沒有清理?xiàng)?,那么這里進(jìn)一步驗(yàn)證引用返回的危害

    雖然能正常運(yùn)行,但是它是有問題的

    C++引用的特性是什么

    小結(jié)引用做返回值

    1.出了 TEST 函數(shù)的作用域,ret 變量會(huì)銷毀,就不能引用返回

    2. 出了 TEST 函數(shù)的作用域,ret 變量不會(huì)銷毀,就可以引用返回

    3.引用返回的價(jià)值是減少拷貝

    觀察并剖析以下代碼

    int main()int main()
    {
    	int x = 3, y = 5;
    	int* p1 = &x;
    	int* p2 = &y;
    	int*& p3 = p1;
    	*p3 = 10;
    	p3 = p2;
    	return 0;
    }

    C++引用的特性是什么

    五、函數(shù)參數(shù)及返回值 &mdash;&mdash; 傳值、傳引用效率比較

    #include <time.h>#include<iostream>using namespace std;struct A { int a[10000]; };A a;void TestFunc1(A a) {}void TestFunc2(A& a) {}A TestFunc3() { return a; }A& TestFunc4() { return a; }void TestRefAndValue(){A a;// 以值作為函數(shù)參數(shù)size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作為函數(shù)參數(shù)size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);size_t end2 = clock();// 分別計(jì)算兩個(gè)函數(shù)運(yùn)行結(jié)束后的時(shí)間cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;}void TestReturnByRefOrValue(){// 以值作為函數(shù)的返回值類型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc3();size_t end1 = clock();// 以引用作為函數(shù)的返回值類型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc4();size_t end2 = clock();// 計(jì)算兩個(gè)函數(shù)運(yùn)算完成之后的時(shí)間cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;}int main(){//傳值、傳引用效率比較TestRefAndValue();cout << "----------cut----------" << endl;//值和引用作為返回值類型的性能比較TestReturnByRefOrValue();return 0;}#include <time.h>
    #include<iostream>
    using namespace std;
    struct A { int a[10000]; };
    A a;
    void TestFunc1(A a) {}
    void TestFunc2(A& a) {}
    A TestFunc3() { return a; }
    A& TestFunc4() { return a; }
    void TestRefAndValue()
    {
    	A a;
    	// 以值作為函數(shù)參數(shù)
    	size_t begin1 = clock();
    	for (size_t i = 0; i < 10000; ++i)
    		TestFunc1(a);
    	size_t end1 = clock();
    	// 以引用作為函數(shù)參數(shù)
    	size_t begin2 = clock();
    	for (size_t i = 0; i < 10000; ++i)
    		TestFunc2(a);
    	size_t end2 = clock();
    	// 分別計(jì)算兩個(gè)函數(shù)運(yùn)行結(jié)束后的時(shí)間
    	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
    	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
    }
    void TestReturnByRefOrValue()
    {
    	// 以值作為函數(shù)的返回值類型
    	size_t begin1 = clock();
    	for (size_t i = 0; i < 100000; ++i)
    		TestFunc3();
    	size_t end1 = clock();
    	// 以引用作為函數(shù)的返回值類型
    	size_t begin2 = clock();
    	for (size_t i = 0; i < 100000; ++i)
    		TestFunc4();
    	size_t end2 = clock();
    	// 計(jì)算兩個(gè)函數(shù)運(yùn)算完成之后的時(shí)間
    	cout << "TestFunc1 time:" << end1 - begin1 << endl;
    	cout << "TestFunc2 time:" << end2 - begin2 << endl;
    }
    int main()
    {
    	//傳值、傳引用效率比較
    	TestRefAndValue();
    	cout << "----------cut----------" << endl;
    	//值和引用作為返回值類型的性能比較
    	TestReturnByRefOrValue();
    	return 0;
    }

    以值作為參數(shù)或者返回值類型,在傳參和返回期間,函數(shù)不會(huì)直接傳遞實(shí)參或者將變量本身直接返回,而是傳遞實(shí)參或者返回變量的一份臨時(shí)的拷貝,因此用值作為參數(shù)或者返回值類型,效率是非常低下的,尤其是當(dāng)參數(shù)或者返回值類型非常大時(shí),效率就更低

    六、 引用和指針的區(qū)別

    1.語法概念

    引用就是一個(gè)別名,沒有獨(dú)立空間,和其引用實(shí)體共用同一塊空間

    指針變量是開辟一塊空間,存儲(chǔ)變量的地址

    int main()
    {
    	int a = 10;
    	int& ra = a;
    	cout<<"&a = "<<&a<<endl;
    	cout<<"&ra = "<<&ra<<endl;
    	int b = 20;
    	int* pb = &b;
    	cout<<"&b = "<<&b<<endl;
    	cout<<"&pb = "<<&pb<<endl;
    	return 0;
    }

    2.底層實(shí)現(xiàn) 

    引用和指針是一樣的,因?yàn)橐檬前凑罩羔樂绞絹韺?shí)現(xiàn)的

    int main()
    {
    	int a = 10;
    	int& ra = a;
    	ra = 20;
    	int* pa = &a;
    	*pa = 20;
    	return 0;  
    }

    這里我們對(duì)比一下 VS 下引用和指針的匯編代碼可以看出來他倆是同根同源

    C++引用的特性是什么

    引用和指針的不同點(diǎn):

    1、引用在定義時(shí)必須初始化,指針沒有要求

    2、引用在初始化時(shí)引用一個(gè)實(shí)體后,就不能再引用其他實(shí)體,而指針可以在任何時(shí)候指向任何一個(gè)同類型實(shí)體

    3 、沒有 NULL 引用,但有 NULL 指針

    4、在 sizeof 中含義不同:引用結(jié)果為引用類型的大小,與類型有關(guān);但指針始終是地址空間所占字節(jié)個(gè)數(shù) (32 位平臺(tái)下占 4 個(gè)字節(jié),64 位平臺(tái)下占 8 個(gè)字節(jié)),與類型無關(guān)

    5、引用自加即引用的實(shí)體增加 1,與類型無關(guān),指針自加即指針向后偏移一個(gè)類型的大小,與類型有關(guān)

    6、有多級(jí)指針,但是沒有多級(jí)引用

    7、訪問實(shí)體方式不同,指針需要解引用,引用編譯器自己處理

    8、引用比指針使用起來相對(duì)更安全,指針容易出現(xiàn)野指針、空指針等非法訪問問題

    讀到這里,這篇“C++引用的特性是什么”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    c++
    AI