C++中的引用和inline 和 nullptr全面解析
一.引用
1.1引用的概念與定義
引?不是新定義?個(gè)變量,?是給已存在變量取了?個(gè)別名,編譯器不會(huì)為引?變量開(kāi)辟內(nèi)存空間, 它和它引?的變量共?同?塊內(nèi)存空間。?如:?壺傳中李逵,宋江叫"鐵?",江湖上?稱(chēng)"?旋 ?";林沖,外號(hào)豹?頭;
類(lèi)型& 引?別名=引?對(duì)象
C++中為了避免引?太多的運(yùn)算符,會(huì)復(fù)?C語(yǔ)?的?些符號(hào),?如前?的>,這?引?也和取 地址使?了同?個(gè)符號(hào)&,?家注意使??法?度區(qū)分就可以。(吐槽?下,這個(gè)問(wèn)題其實(shí)挺坑的,個(gè) ?覺(jué)得?更多符號(hào)反?更好,不容易混淆

從這里我們可以看出,i和j的地址是一樣的,說(shuō)明他們指向同一塊空間,引用不會(huì)開(kāi)辟新空間

1.2 引?的特性
1.引?在定義時(shí)必須初始化
2.?個(gè)變量可以有多個(gè)引?

3. 引??旦引??個(gè)實(shí)體,再不能引?其他實(shí)體

這里我們可以看出,引用b指向了a,說(shuō)明是給a取了別名;若要讓b在指向d,d現(xiàn)在是20,這里就是賦值的意思,將20賦值給b,而b又是a的別名,所以a的值也發(fā)生了改變
4.別名++,其存在的變量也++

1.3 引?的使?
1.3.1引用傳參的使用
1. 引?在實(shí)踐中主要是于引?傳參和引?做返回值中減少拷?提?效率和改變引?對(duì)象時(shí)同時(shí)改變被引?對(duì)象。
2.引?傳參跟指針傳參功能是類(lèi)似的,引?傳參相對(duì)更?便?些。


如果使用指針來(lái)實(shí)現(xiàn)兩個(gè)數(shù)的交換,首先Swap1中的ab需要開(kāi)辟一個(gè)空間來(lái)指向x1,y1的地址,需要拷貝;
若用引用的話(huà),只是給x2,y2起別名,ab發(fā)生改變,x2,y2也同時(shí)發(fā)生改變,但這種情況不會(huì)開(kāi)辟新空間,不需要拷貝優(yōu)于指針




上面的圖返回的是臨時(shí)變量,是一個(gè)值,需要存儲(chǔ)起來(lái),不能直接進(jìn)行修改;
下面的圖返回的是它的引用,可以進(jìn)行直接修改
3.引?返回值的場(chǎng)景相對(duì)?較復(fù)雜,我們?cè)谶@?簡(jiǎn)單講了?下場(chǎng)景,還有?些內(nèi)容后續(xù)類(lèi)和對(duì)象章節(jié) 中會(huì)繼續(xù)深?講解。
4.引?和指針在實(shí)踐中相輔相成,功能有重疊性,但是各有特點(diǎn),互相不可替代。C++的引?跟其他 語(yǔ)?的引?(如Java)是有很?的區(qū)別的,除了?法,最?的點(diǎn),C++引?定義后不能改變指向, Java的引?可以改變指向。
5.?些主要?C代碼實(shí)現(xiàn)版本數(shù)據(jù)結(jié)構(gòu)教材中,使?C++引?替代指針傳參,?的是簡(jiǎn)化程序,避開(kāi) 復(fù)雜的指針,但是很多同學(xué)沒(méi)學(xué)過(guò)引?,導(dǎo)致?頭霧?。
1.3.2傳引用返回
再說(shuō)傳引用返回,這里返回的是ret的別名


但是這樣的傳引用返回的行為,本質(zhì)上是非常危險(xiǎn)的,func函數(shù)結(jié)束之后,func棧幀就銷(xiāo)毀了,但是tmp依舊是ret這塊空間的別名,tmp依舊在訪(fǎng)問(wèn)ret,就相當(dāng)于野指針的訪(fǎng)問(wèn)。此時(shí)返回的就不一定是0了,也有可能是隨機(jī)值
正確傳引用返回:
如圖出了作用域,func1結(jié)束,ret還在,此時(shí)被static修飾的ret就不存在func1的棧幀之中了

1.4 const引用
1.可以引用一個(gè)const對(duì)象,但是必須用const引用。const引用也可以引用普通對(duì)象,因?yàn)閷?duì)象的訪(fǎng)問(wèn)權(quán)限在引用過(guò)程中可以縮小,但是不能放大。
如圖,如果a被const修飾了,引用就不能使用了,此時(shí)a不能改變,但是b是a的別名,可以改變a,這就是一個(gè)權(quán)限放大的場(chǎng)景,權(quán)限是不能放大的,想要正常引用就要權(quán)限移b也要被const修飾

用const修飾a,a只能讀不能寫(xiě);而b是a的引用,沒(méi)有用const,那么b既能讀又能寫(xiě),放大了a的權(quán)限

這樣寫(xiě)才是正確的,a和b都只能讀不能寫(xiě)。

a可讀可寫(xiě),但是引用后只能讀不能寫(xiě),這樣是正確的,縮小了權(quán)限
2.不需要注意的是類(lèi)似 int& rb = a*3; double d = 12.34; int& rd = d; 這樣?些場(chǎng) 景下a*3的和結(jié)果保存在?個(gè)臨時(shí)對(duì)象中, int& rd = d 也是類(lèi)似,在類(lèi)型轉(zhuǎn)換中會(huì)產(chǎn)?臨時(shí)對(duì) 象存儲(chǔ)中間值,也就是說(shuō),rb和rd引?的都是臨時(shí)對(duì)象,?C++規(guī)定臨時(shí)對(duì)象具有常性,所以這? 就觸發(fā)了權(quán)限放?,必須要?常引?才可以。

int&是非常量左值引用,它只能綁定到一個(gè)可修改的左值(即有內(nèi)存地址的變量)。a * 3是一個(gè)臨時(shí)右值(計(jì)算結(jié)果是一個(gè)沒(méi)有名字的臨時(shí)值,沒(méi)有持久的內(nèi)存地址),無(wú)法被非常量左值引用綁定。
正確寫(xiě)法是在引用前面加上const修飾,只能讀。

double d = 12.34;定義了一個(gè)double類(lèi)型變量d,值為12.34。int i = d;- 將
double類(lèi)型的d隱式轉(zhuǎn)換為int,截?cái)嘈?shù)部分,i的值為12。 - 這是直接賦值,
i是一個(gè)獨(dú)立的新變量,和d共享內(nèi)存。
- 將
const int& ri = d;- 試圖將
double類(lèi)型的d綁定到const int&類(lèi)型的引用。 - 由于類(lèi)型不匹配,編譯器會(huì)生成一個(gè)臨時(shí)的
int類(lèi)型對(duì)象,將d截?cái)酁?12存入該臨時(shí)對(duì)象,然后讓ri綁定到這個(gè)臨時(shí)對(duì)象。 - 這就是圖中標(biāo)注的「臨時(shí)對(duì)象」:它是一個(gè)匿名的、生命周期被
const int&延長(zhǎng)的臨時(shí)變量,ri并不直接引用d,而是引用這個(gè)臨時(shí)對(duì)象。
- 試圖將
1.5指針與引用的關(guān)系
| 對(duì)比維度 | 引用(Reference) | 指針(Pointer) |
|---|---|---|
| 本質(zhì)概念 | 變量的別名,不單獨(dú)占用內(nèi)存空間 | 存儲(chǔ)變量的內(nèi)存地址,需要占用內(nèi)存空間 |
| 初始化要求 | 定義時(shí)必須初始化,且必須綁定到一個(gè)已存在的對(duì)象 | 建議初始化,但語(yǔ)法上不強(qiáng)制,可以先聲明后賦值 |
| 指向關(guān)系 | 初始化后無(wú)法再更改綁定對(duì)象,始終指向最初綁定的變量 | 可以隨時(shí)改變指向,指向不同的變量或空地址 |
| 訪(fǎng)問(wèn)方式 | 直接使用引用名訪(fǎng)問(wèn)目標(biāo)對(duì)象,無(wú)需解引用 | 需要通過(guò) * 解引用操作符才能訪(fǎng)問(wèn)目標(biāo)對(duì)象 |
| sizeof 含義 | sizeof(引用) 結(jié)果為所引用類(lèi)型的大小(如 sizeof(int&) = 4) | sizeof(指針) 結(jié)果為地址空間的字節(jié)數(shù)(32 位平臺(tái)為 4 字節(jié),64 位平臺(tái)為 8 字節(jié)) |
| 安全性 | 不存在空引用、野引用問(wèn)題,使用相對(duì)更安全 | 容易出現(xiàn)空指針、野指針問(wèn)題,使用時(shí)需額外判斷合法性 |
| 典型用途 | 函數(shù)參數(shù)傳遞(避免拷貝)、函數(shù)返回值(保證非空) | 動(dòng)態(tài)內(nèi)存管理、數(shù)組遍歷、復(fù)雜數(shù)據(jù)結(jié)構(gòu)(鏈表 / 樹(shù))的節(jié)點(diǎn)操作 |
二、inline
先看一下宏常見(jiàn)的問(wèn)題



看似沒(méi)有問(wèn)題,但這個(gè)宏在另一種場(chǎng)景下就又不對(duì)了

所以可以看出宏函數(shù)很容易出現(xiàn)問(wèn)題,很復(fù)雜,還不能調(diào)試
所以C++之父就搞了個(gè)內(nèi)聯(lián)出來(lái)
寫(xiě)個(gè)函數(shù)是不容易寫(xiě)錯(cuò)的,但寫(xiě)個(gè)宏函數(shù)很容易寫(xiě)錯(cuò)
宏函數(shù)的優(yōu)點(diǎn)就是可以提高效率,適用于高頻調(diào)用的小函數(shù),預(yù)處理階段宏會(huì)替換,不建立棧幀,而且宏甚至可以傳類(lèi)型
C++中內(nèi)聯(lián)就是用來(lái)替換宏的
用inline修飾的函數(shù)叫做內(nèi)聯(lián)函數(shù),編譯時(shí)C++編譯器會(huì)在調(diào)用的時(shí)候展開(kāi)(不是把函數(shù)部分的代碼直接放到主函數(shù)處,而是把函數(shù)地方的邏輯經(jīng)過(guò)一定的處理,經(jīng)過(guò)編譯器自己分析,用于主函數(shù)處)內(nèi)聯(lián)函數(shù),這樣調(diào)用內(nèi)聯(lián)函數(shù)就不需要建立棧幀了,就可以提高效率
inline對(duì)于編譯器而言只是一個(gè)建議,也就是說(shuō),你加了inline編譯器也可以選擇在調(diào)用的地方不展開(kāi),不同編譯器關(guān)于inline什么情況展開(kāi)各不相同,因?yàn)镃++標(biāo)準(zhǔn)沒(méi)有規(guī)定這個(gè)。inline適用于頻繁調(diào)用的短小函數(shù),對(duì)于遞歸函數(shù),代碼相對(duì)多一些的函數(shù),加上inline也會(huì)被編譯器忽略。

三、nullptr
nullptr是C++11的一個(gè)關(guān)鍵字,是用來(lái)替代C語(yǔ)言的NULL的
NULL實(shí)際上是一個(gè)宏,在傳統(tǒng)的C頭文件(stddef.h)中,可以看到如下代碼:
#ifndef NULL
#ifdef __cplusplus
//C++中的NULL被替換為0,默認(rèn)為整型
#define NULL 0
#else
//C語(yǔ)言中的NULL也被替換為0,但是被強(qiáng)制類(lèi)型轉(zhuǎn)換為(void*)
#define NULL ((void *)0)
#endif
#endifC++中NULL可能被定義為字面常量0,或者C中被定義為無(wú)類(lèi)型指針(void * )的常量。不論采取何種定義,在使用空值的指針時(shí),都不可避免的會(huì)遇到一些麻煩,本想通過(guò)f(NULL)調(diào)用指針版本的f(int * )函數(shù),但是由于NULL被定義成0,調(diào)用了f(int x),因此與程序的初衷相悖。f((void* )NULL);調(diào)用會(huì)報(bào)錯(cuò)


在C++之中,void* 給int* 是需要強(qiáng)制類(lèi)型轉(zhuǎn)換的
C++11中引用nullptr,nullptr是一個(gè)特殊的關(guān)鍵字(就相當(dāng)于語(yǔ)法層在編譯的時(shí)候就特殊解決該問(wèn)題了),nullptr是一種特殊類(lèi)型的字面量,它可以轉(zhuǎn)換成任意其它類(lèi)型的指針類(lèi)型(值還為0)。使用nullptr定義空指針可以避免類(lèi)型轉(zhuǎn)換的問(wèn)題,因?yàn)閚ullptr只能被隱式轉(zhuǎn)換為指針類(lèi)型,而不能被轉(zhuǎn)換為整數(shù)類(lèi)型。

到此這篇關(guān)于C++中的引用和inline 和 nullptr全面解析的文章就介紹到這了,更多相關(guān)C++引用和inline 和 nullptr內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++中的引用傳參和指針傳參的區(qū)別小結(jié)
- C++中懸垂引用(Dangling?Reference)?的實(shí)現(xiàn)
- C++?左值、右值、左值引用、右值引用的用途及區(qū)別
- C++函數(shù)重載與引用實(shí)戰(zhàn)案例詳解
- C++11右值引用與Lambda表達(dá)式的使用
- C++11?右值引用的使用場(chǎng)景分析
- 深入解讀C++ 內(nèi)聯(lián)函數(shù)inline|nullptr
- C++11中內(nèi)聯(lián)函數(shù)(inline)用法實(shí)例
- 一文詳解C++關(guān)鍵字nullptr及與NULL的區(qū)別
相關(guān)文章
c語(yǔ)言中static和extern的用法詳細(xì)解析
以下是對(duì)c語(yǔ)言中static和extern的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-09-09
C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能
這篇文章主要介紹了C++17 使用 std::string_view避免字符串拷貝優(yōu)化程序性能,幫助大家提高程序運(yùn)行速度,感興趣的朋友可以了解下2020-10-10
C++?std::copy與memcpy區(qū)別小結(jié)
本文主要介紹了C++?std::copy與memcpy區(qū)別小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05
C語(yǔ)言實(shí)現(xiàn)手寫(xiě)Map(全功能)的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C語(yǔ)言實(shí)現(xiàn)手寫(xiě)Map(全功能),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定幫助,需要的可以參考一下2022-08-08
C++類(lèi)模板實(shí)戰(zhàn)之vector容器的實(shí)現(xiàn)
本文我們將做一個(gè)類(lèi)模板實(shí)戰(zhàn)-手寫(xiě)精簡(jiǎn)版vector容器。讓我們自己封裝一個(gè)數(shù)組類(lèi),可以適應(yīng)基本數(shù)據(jù)類(lèi)型和自定義數(shù)據(jù)類(lèi)型,感興趣的可以了解一下2022-07-07
Qt編寫(xiě)提示進(jìn)度條的實(shí)現(xiàn)示例
進(jìn)度條在很地方都可以使用到,Qt自帶的進(jìn)度條或者操作系統(tǒng)的進(jìn)度條樣式,不夠炫,本文就介紹一下Qt編寫(xiě)自定義控件的提示進(jìn)度條的實(shí)現(xiàn)示例,感興趣的可以了解一下2021-12-12
C++實(shí)現(xiàn)LeetCode(100.判斷相同樹(shù))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(100.判斷相同樹(shù)),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07

