C++入門——引用(2)

前言

上一節我們開始學習了C++,并且對C++有了初步的了解,這一節我們繼續學習C++的基礎,那么廢話不多說,我們正式進入今天的學習

C++中的引用

1.1引用的概念

引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會為引用變量開辟內存空 間,它和它引用的變量共用同一塊內存空間

引用的語法如下:

	int a = 0;int& b = a;

第二行的代碼的意思是:給已創建的變量a取一個別名為b,注意這里的&符號不是取地址的意思,而是引用的意思

當&符號放在一個類型和一個變量的中間位置才叫做引用,不然的話仍然起的是取地址的功能。

int main(void)
{int a = 0;int& b = a;cout << &a << endl;cout << &b << endl;return 0;
}

通過運行以上代碼我們可以知道:引用并沒有重新開辟一個空間,而是和原變量共用一個空間

我們對b變量執行++操作也相當于對a變量執行++操作,因為它們本質上就是同一個變量

引用的概念有點像我們在日常生活中給別人起的外號

學到這里可能有人會覺得引用沒有什么用,只是給變量取了一個“外號”罷了,其實并不是這樣的。我們在C語言中若是要完成交換兩個不同變量中的數據內容時,我們創建swap函數應該要傳入兩個變量的地址,如果是傳值調用則無法完成交換的功能,因為在出函數的時候臨時的變量被銷毀。但是有了引用就可以不用使用指針,引用傳參修改的值可以直接影響到原來的參數

void swap(int& x1, int& x2)
{int tmp = x1;x1 = x2;x2 = tmp;
}int main(void)
{int x = 1;int y = 2;swap(x, y);cout << "x = " << x << endl;cout << "y = " << y << endl;return 0;
}

1.2引用的注意事項

1. 引用在定義時必須初始化

 int a = 10;// int& ra;   // 該條語句編譯時會出錯

2. 一個變量可以有多個引用

int a = 0;
int& ra = a;
int& rra = a;

3. 引用一旦引用一個實體,再不能引用其他實體

int a = 0;
int& b = a;
int x = 1;
b = x;
//這種情況下并不是把b變成x的引用,而是給b中賦值為1,也就是a改為1

4.引用的類型必須和引用實體的類型相同

1.3傳引用返回

首先我們需要了解一個概念,叫做傳值返回:

int Count()
{int n = 0;n++;//……return n;
}int main(void)
{int ret = Count();return 0;
}

在出函數的時候變量n已經被銷毀了,所以函數并不是直接拿n作為返回值,此時編譯器用以下兩種方式返回

1.把n拷貝到一個寄存器中,讓寄存器充當返回值,該情況適用于返回值比較小的情況

2.當n的取值比較大的的時候,會在Count函數和main函數之間的空隙里面壓出一個空間,將值存入并將它作為一個返回值

隨后我們來了解一下引用返回

int& Count()
{int n = 0;n++;//……return n;
}int main(void)
{int ret = Count();return 0;
}

傳引用返回相當于返回的是值n的別名,這個概念看起來有些奇怪,因為原來的變量n已經被銷毀了,而返回的值又是已經被銷毀的變量的別名,所以有的人可能認為這個概念的性質和野指針差不多 。首先我們需要知道的是,當一個變量或者一個空間被銷毀了,仍然可以返回這個已經被銷毀的空間里面的變量的別名,因為空間被銷毀并不是意味著這一塊空間已經沒有了。空間銷毀的概念有點類似于我們在日常生活中的“退房”,我們銷毀了的那塊空間并不是不能使用了,而是收回了對那塊空間的使用權。但是傳引用返回是比較危險的,傳引用返回一般都會報一個警告,返回來的值可能是正常值也有可能是一個隨機值,這取決于函數棧幀銷毀了以后原空間里面的內容會不會被重置為一個隨機值

int& Count()
{int n = 0;n++;//……return n;
}int main(void)
{int& ret = Count();cout << ret << endl;cout << ret << endl;return 0;
}

我們來看一下這樣的情況下為什么第二次打印的結果是一個隨機值:

我們首先需要了解,cout是一個函數調用,調用的是一個運算符重載的函數,cout是ostream類型

我們在第一次調用函數的時候并不會出現任何的問題,而當我們第二次調用函數的時候,創建棧幀的區域還是在第一次調用所創建的區域,此時區域內原先的數據就已經被覆蓋了,所以打印出來的是一個隨機值

我們來舉一個例子就能更好地理解這一點:

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

因為本來執行Add操作計算出來的值是3,但是我們在第二次調用函數的時候算出來的值是7,第二次調用覆蓋了第一次調用,所以此時去打印算出來的值是7

所以:如果函數返回時,出了函數作用域,如果返回對象還在(還沒有還給系統),則可以使用引用返回,如果已經還給系統了,則必須使用傳值返回

我們在C語言中要完成順序表的第i個位置的數據和修改需要兩個步驟:

struct Seqlist
{int* a;int size;int capacity;
};
//讀取第i個位置的值
//修改第i個位置的值
int SLAT(struct Seqlist* ps, int i)
{assert(i < ps->size);//...return ps->a[i];
}int SLModify(struct Seqlist* ps, int i, int x)
{assert(i < ps->size);//...return ps->a[i] = x;
}

而在C++中使用引用返回就可以簡化這一過程:

struct Seqlist
{int* a;int size;int capacity;
};
//讀取&修改第i個位置的值
int& SLAT(struct Seqlist& ps, int i)
{assert(i < ps.size);//...return ps.a[i];
}
int main(void)
{struct Seqlist s;//...SLAT(s, i) = 1;cout << SLAT(s, i) << endl;return 0;
}

當出了作用域時,ps.a[i] 仍然存在,所以可以直接對其修改,修改過后的值仍然存在

怎么解釋這個現象呢?

因為傳值返回出函數的時候返回的是一份臨時的拷貝,是不可以直接修改的

而傳引用返回返回的是別名,是可以被修改的

傳值、傳引用效率比較

以值作為參數或者返回值類型,在傳參和返回期間,函數不會直接傳遞實參或者將變量本身直接返回,而是傳遞實參或者返回變量的一份臨時的拷貝,因此用值作為參數或者返回值類型,效率是非常低下的,尤其是當參數或者返回值類型非常大時,效率就更低。如果是傳引用的話就能夠提高效率,當然,傳指針也可以

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{A a;// 以值作為函數參數size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作為函數參數size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);size_t end2 = clock();// 分別計算兩個函數運行結束后的時間cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}int main(void)
{TestRefAndValue();return 0;
}

由此我們可以看出:傳引用返回比傳值返回的效率要高

那么在什么情況下適合使用傳引用返回呢?

1.返回的是一個全局對象

2.返回的是一個靜態對象

3.在堆上動態申請的對象

因為這三個變量出了作用域仍然存在,使用傳引用返回就可以在確保沒有錯誤的情況下提高效率

此時我們就可以總結一下:

傳引用傳參的優勢(在任何的情況下都可以

1.提高效率

2.輸出型參數(形參的修改可以影響實參)

傳引用返回的優勢(出了函數作用域對象還在才可以使用

1.提高效率

2.修改返回的對象

1.4常引用

int main(void)
{const int a = 0;int& b = a;return 0;
}

在這種情況下,編譯是無法通過的,因為這是一種權限的放大。在引用的過程中權限是可以平移和縮小的,但是不能被放大

權限的平移:

const int a = 0;
//權限的平移
const int& c = a;

權限的縮小:

int x = 0;
//權限的縮小
const int& y = x;

注意:賦值是不受權限的影響的,因為賦值是一種拷貝,b的修改不影響a

const int a = 0;
//賦值
int b = a;

我們再來看一個有趣的現象:

我們這樣寫出的代碼編譯會失敗:

int i = 0;
double& d = i;

而這樣寫編譯就不會報錯:

int i = 0;
const double& d = i;

第一種情況其實是一種權限的放大,臨時變量具有常性

而第二種情況我們加入了const,此時就沒有權限的放大了

1.5引用與指針的區別

我們通過之前的學習可以知道:指針在創建的時候會在內存中開辟空間,而在語法上來看引用是不會額外的開辟空間的(其實在底層實現上引用也會占用空間,但是在語法的層面上不會占用空間)

引用和指針的不同點:

1.引用是定義一個變量的別名,而指針是存儲一個變量的地址

2.引用在定義的時候必須要初始化,而指針沒有要求

3.引用在初始化時引用一個實體以后,就不能再引用其他的實體,而指針可以在任何時候指向任何一個同類型實體

4.沒有NULL引用,但是有NULL指針

5.sizeof的含義不同:引用結果是引用類型的大小,但是指針始終是地址空間所占用的字節數(4或者8)

6. 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小

7. 有多級指針,但是沒有多級引用

8. 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理

9. 引用比指針使用起來相對更安全

結尾

引用是C++中的一個重要的概念,我們需要加深對引用的理解,有助于我們更好的學習C++,那么本節的內容到此就結束了,謝謝您的瀏覽!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11341.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11341.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11341.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uniapp小程序:大盒子包裹小盒子但是都有點擊事件該如何區分?

在開發過程中我們會遇到這種情況&#xff0c;一個大盒子中包裹這一個小盒子&#xff0c;兩個盒子都有點擊事件&#xff0c;例如&#xff1a; 這個時候如果點擊評價有可能會點擊到它所在的大盒子&#xff0c;如果使用css中的z-index設置層級的話如果頁面的盒子多的話會混亂&…

Spring解決泛型擦除的思路不錯,現在它是我的了。

你好呀&#xff0c;我是浮生。 Spring 的事件監聽機制&#xff0c;不知道你有沒有用過&#xff0c;實際開發過程中用來進行代碼解耦簡直不要太爽。 但是我最近碰到了一個涉及到泛型的場景&#xff0c;常規套路下&#xff0c;在這個場景中使用該機制看起來會很傻&#xff0c;但…

15、FreeRTOS 軟件定時器

文章目錄 一、什么是定時器?1.1 定時器的理解1.2 軟件定時器的特性 二、 軟件定時器的上下文2.1 守護任務2.2 守護任務的調度2.3 回調函數 三、軟件定時器的函數3.1 創建3.2 刪除3.3 啟動/停止3.5 修改周期3.6 定時器ID 四、案例4.1 一般使用4.2 消除抖動 一、什么是定時器? …

怎么解決ModuleNotFoundError: No module named ‘httpx_sse‘

解決方案 pip install httpx_sseLooking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple Collecting httpx_sse Downloading https://pypi.tuna.tsinghua.edu.cn/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-n…

Android 14.0 frameworks添加自定義服務

1.概述 在14.0的系統rom定制化產品開發中,對于提供系統接口來給app調用,來控制系統的某些功能,所以需要添加自定義服務也是常有功能,因此需要來在frameworks層中添加自定義系統服務的功能 2.frameworks添加自定義服務的核心類 frameworks\base\services\java\com\android…

Midjourney Imagine API 申請及使用

Midjourney Imagine API 申請及使用 申請流程 要使用 Midjourney Imagine API&#xff0c;首先可以到 Midjourney Imagine API 頁面點擊「Acquire」按鈕&#xff0c;獲取請求所需要的憑證&#xff1a; 如果你尚未登錄或注冊&#xff0c;會自動跳轉到登錄頁面邀請您來注冊和登…

多線程【LeetCode】

多線程【LeetCode】 前言前言推薦多線程信號量1114.按序打印1115.交替打印FooBar1116.打印零與奇偶數1117.H2O生成1188.設計有限阻塞隊列Plus1195.交替打印字符串1226.哲學家進餐 最后 前言 這是陳舊已久的草稿2022-11-27 20:44:17 這個是刷算法&#xff0c;也是準備寒假實習…

語音轉文字服務的調用接口

語音轉文字&#xff08;Speech-to-Text&#xff0c;STT&#xff09;技術允許將口語化的語音轉換成書面文字。以下是一些提供語音轉文字服務的調用接口及其特點。北京木奇移動技術有限公司&#xff0c;專業的軟件外包開發公司&#xff0c;歡迎交流合作。 1.訊飛開放平臺語音轉寫…

[貓頭虎分享21天微信小程序基礎入門教程]第1天:微信小程序概述與開發環境搭建教程

第1天&#xff1a;微信小程序概述與開發環境搭建 &#x1f63a; 文章目錄 第1天&#xff1a;微信小程序概述與開發環境搭建 &#x1f63a;自我介紹微信小程序概述特點 開發環境搭建步驟1: 注冊微信小程序賬號步驟2: 安裝開發者工具步驟3: 熟悉開發者工具界面 今日學習總結小測試…

UnityDOTS備忘

Unity DOTS中創建一個AssetBundle并將其用作Entity 創建一個新的Unity項目&#xff0c;并確保已啟用DOTS功能。 創建一個AssetBundle&#xff0c;可以通過在Project視圖中右鍵單擊文件夾并選擇“Create > AssetBundle”來創建。 將您想要轉換為Entity的資源&#xff08;例…

炒股開戶傭金最低萬1和萬0.854,融資融券現在利率最低4.0%~5%

??炒股開戶傭金一般是萬1和萬0.854&#xff0c;萬0.854有一定的資金量要求&#xff0c;高于萬1的是可以申請降低的。 開戶萬1傭金和萬0.854傭金只需要聯系證券公司客戶經理協商就行。 開戶流程&#xff1a; 1、向客戶經理索要開戶鏈接或者掃描二維碼、進入申請頁面&#x…

本地搭建各大直播平臺錄屏服務結合內網穿透工具實現遠程管理錄屏任務

文章目錄 1. Bililive-go與套件下載1.1 獲取ffmpeg1.2 獲取Bililive-go1.3 配置套件 2. 本地運行測試3. 錄屏設置演示4. 內網穿透工具下載安裝5. 配置Bililive-go公網地址6. 配置固定公網地址 本文主要介紹如何在Windows系統電腦本地部署直播錄屏利器Bililive-go&#xff0c;并…

Nachi那智不二越機器人維修技術合集

一、Nachi機械手維護基礎知識 1. 定期檢查&#xff1a;定期檢查機器人的各個部件&#xff0c;如機械手伺服電機、機器人減速器、機械臂傳感器等&#xff0c;確保其運行正常。 2. 清潔與潤滑&#xff1a;定期清潔Nachi工業機器人表面和內部&#xff0c;并使用合適的潤滑油進行潤…

VRRP協議-負載分擔配置【分別在路由器與交換機上配置】

VRRP在路由器與交換機上的不同配置 一、使用路由器實現負載分擔二、使用交換機實現負載分擔一、使用路由器實現負載分擔 使用R1與R2兩臺設備分別進行VRRP備份組 VRRP備份組1,虛擬pc1的網關地址10.1.1.254 VRRP備份組2,虛擬pc2的網關地址10.1.1.253 ①備份組1的vrid=1,vrip=…

修正牛頓法求解無約束問題

function [x,val,k]revisenm(fun,gfun, Hess, x0) %功能:用修正牛頓法求解無約束問題:min f(x) %輸入:x0是初始點&#xff0c;fun, gfun,Hess分別是求目標函數值&#xff0c;梯度&#xff0c;Hesse矩陣的函數 %輸出:x,val分別是近似最優點和最優值&#xff0c;k是迭代次數 nl…

vue3中使用cherry-markdown

附cherry-markdown官網及api使用示例 官網:https://github.com/Tencent/cherry-markdown/blob/main/README.CN.md api:Cherry Markdown API 考慮到復用性,我在插件的基礎上做了二次封裝,步驟如下: 1.下載 (一定要指定版本0.8.22,否則會報錯: [vitel Internal server e…

初識指針(5)<C語言>

前言 在前幾篇文章中&#xff0c;已經介紹了指針一些基本概念、用途和一些不同類型的指針&#xff0c;下文將介紹某些指針類型的運用。本文主要介紹函數指針數組、轉移表&#xff08;函數指針的用途&#xff09;、回調函數、qsort使用舉例等。 函數指針數組 函數指針數組即每個…

深度學習知識點全面總結

ChatGPT 深度學習是一種使用神經網絡來模擬人腦處理數據和創建模式的機器學習方法。下面是深度學習的一些主要知識點的總結&#xff1a; 1. 神經網絡基礎&#xff1a; - 神經元&#xff1a;基本的計算單元&#xff0c;模擬人腦神經元。 - 激活函數&#xff1a;用于增加神…

【CSP CCF記錄】數組推導

題目 過程 思路 每次輸入一個Bi即可確定一個Ai值&#xff0c;用temp記錄1~B[i-1]&#xff0c;的最大值分為兩種情況&#xff1a; 當temp不等于Bi時&#xff0c;則說明Bi值之前未出現過&#xff0c;Ai必須等于Bi才能滿足Bi是Ai前綴最大的定義。當temp等于Bi時&#xff0c;則說…

SpringAMQP-消息轉換器

這邊發送消息接收消息默認是jdk的序列化方式&#xff0c;發送到服務器是以字節碼的形式&#xff0c;我們看不懂也很占內存&#xff0c;所以我們要手動設置一下 我這邊設置成json的序列化方式&#xff0c;注意發送方和接收方的序列化方式要保持一致 不然回報錯。 引入依賴&#…