C++面試周刊(3):面試不慌,這樣回答指針與引用,青銅秒變王者
一、背景
各位老師好
CPP面試沖刺周刊 (c++ weekly)第三期開始了
目標(biāo):不是成為C++專家,而是成為C++面試專家
本期內(nèi)容:指針與引用區(qū)別

c++周刊目的陪你一起快速沖擊大廠面試
小提示:不要把他看成一個出售給你產(chǎn)品,我只出售給自己
在公司做任何事情事情,
都必須清楚拆解需求功能,開發(fā)周期,最后得到什么結(jié)果,
同樣面試準(zhǔn)備也是如此,給自己一個期限 21 天,給自己大綱,然后給自己 21 天學(xué)習(xí)結(jié)果,這樣自己才能安心準(zhǔn)備下去。
第一周(換個角度看問題):

曾經(jīng)有一個讓我心跳加速的崗位放在我面前,
我沒有珍惜。
等到別人拿到 offer 的那一刻,
我才追悔莫及!
人世間,最痛苦的事情,
不是沒錢吃飯,
也不是沒房沒車,
而是——錯過了那個能讓我逆天改命的機會!
如果上天再給我一次機會,
我一定會對那個崗位說三個字:
“我要你!”
如果非要在這份“心動”上加一個期限,
一萬年太久了……
我只想要——21天!
你可能面臨兩種選擇
① 猶豫不前:準(zhǔn)備到天荒地老
“這個崗位太難了,我先準(zhǔn)備一下吧。”
于是你準(zhǔn)備1天、1周、1個月、1年……
等再回頭,3年就這樣過去了。
最后,錯過了一次又一次心動的崗位。
② 盲目回答:機會就在眼前,卻抓不住
終于等來一場面試,
你覺得問題很簡單,張口就答,
結(jié)果用“幾千元思維”回答“百萬年薪崗位”。
一次面試失利,也許就意味著和理想崗位失之交臂。
更殘酷的是
在你猶豫的這幾年里,
找工作的成本越來越高:
曾經(jīng)復(fù)雜的業(yè)務(wù)規(guī)則、先進的架構(gòu),早已被淘汰?市場上新的技術(shù)和面試要求,每年都在不斷升級
等你回過頭來,發(fā)現(xiàn)不僅機會沒了,
連準(zhǔn)備的方向都變了。
21天C++面試沖刺周刊
不是讓你成為C++專家, 而是讓你成為C++面試專家。
不是讓你瘋狂學(xué)習(xí)新知識, 而是幫你重新整理已有知識,
讓你的能力與面試題精準(zhǔn)對齊。
因為,21天就夠了,
足夠讓我火力全開,
核心方法論:
? 系統(tǒng)備戰(zhàn)讓你學(xué)到每個 c++知識,都關(guān)聯(lián)一個經(jīng)典面試,并對對應(yīng)開源項目實踐
每天 20~30 分鐘,聚焦 C++ 核心知識,
三周時間完成高效梳理。 ? 經(jīng)典面試題
每個知識點都關(guān)聯(lián)一個高頻面試題,
讓你知道“為什么考”和“怎么答”。 ? 開源項目實踐
通過真實項目理解底層原理,
不背答案,而是用實踐打動面試官。 ? 場景驅(qū)動學(xué)習(xí)
還原真實面試場景,
幫你學(xué)會“怎么說服面試官”。
21天,你會獲得什么?
? 一份完整的C++面試知識地圖 ? 一套高頻題+解析+項目實踐組合拳 ? 一次全鏈路模擬面試體驗 ? 三周后,面對面試官,你能自信說出:“問吧,準(zhǔn)備好了。”
?如果一開始就直接學(xué)某個知識點,我常常感覺不到它的實際價值。?所以我會先嘗試樹立一個整體的大局觀,就算過程中被現(xiàn)實“啪啪打臉”了又怎樣??把每一次面試都當(dāng)成一場陪練,用面試官的專業(yè)視角和真實項目來反推和校正自己的理解,不是更好嗎?這種即時、高質(zhì)量的反饋,是你看多少書、自己一個人悶頭琢磨多久,都很難獲得的。這也是我的面試方法:
二、從青銅 (小青)到王者(小王)回答:指針與引用區(qū)別
2.1 小青(青銅級別 工作 0-3 年)面試
1. 面試官:指針與引用區(qū)別
2. 小青回答(回答很棒了):
?我熟讀《C++ Primer》,引用本質(zhì)上是變量(變量也是內(nèi)存地址別名)的別名,定義時必須初始化;
?引用不允許為 NULL,而指針可以為 NULL;
原文里說:“A reference is not an object. Instead, a reference is just another name for an already existing object.” 這是最經(jīng)典的描述;
?我 <<CPU眼里的C/C++>> <<c++反匯編與逆向分析技術(shù)>>從底層看,引用在匯編層面仍是用指針實現(xiàn)的,可以理解為“常量指針”,所以二者在本質(zhì)上差別不大.
?但語法和使用上有明顯區(qū)別,引用更加安全


3. 面試官視角反問

于是小青繼續(xù)補充了幾點:
? 指針的大小:在 64 位系統(tǒng)中,sizeof(pointer) 固定是 8 個字節(jié);
?
自增運算的區(qū)別:
?
指針 ptr++ → 偏移一個對象的地址;
?
引用 ref++ → 直接讓變量本身加 1
4. 小青總結(jié)(這樣回答還不夠)
?這個時候,其實不用太擔(dān)心自己記不住所有指針的語法細(xì)節(jié)。
面試時,能回答多少就說多少,別陷入死記硬背。
更重要的是思路是否清晰,能不能結(jié)合項目經(jīng)驗去解釋。
面試官往往是項目經(jīng)理,他每天忙于業(yè)務(wù)推進,真的會關(guān)心你能背出多少條“指針 vs 引用”的區(qū)別嗎?
?這樣會到還不夠,為什么不能針針對區(qū)詳細(xì)說明
小青疑惑:為什么我全部回答了,面試官還是不滿意 ,我回答不高深嗎?
2.2 小白(白銀級別 工作 3-5 年)面試
1. 面試官:談?wù)勀銓χ羔樑c引用區(qū)別理解
2. 小白回答(工作怎么用就怎么回答):
核心原則:
?Use references when you can(能用引用就用引用 ?)?pointers when you have to(必須使用指針場景,不用指針無法解決問題?)2.1 必須使用指針的場景:需要頻繁更新/延長初始化的重要數(shù)據(jù)結(jié)構(gòu)設(shè)計
需要頻繁更新數(shù)據(jù)結(jié)構(gòu):
| 數(shù)據(jù)結(jié)構(gòu) | 為什么用指針 | 為什么不用引用 |
|---|---|---|
std::vector |
1. 內(nèi)部維護動態(tài)數(shù)組,存儲區(qū)可擴容。2. 擴容后舊地址失效,必須更新新的堆指針。3. 需要支持空容器狀態(tài),用空指針表示。 | - 引用必須綁定到對象,無法延遲綁定或置空。- 引用不能在擴容時重新綁定新內(nèi)存。 |
std::map |
1. 基于紅黑樹實現(xiàn),節(jié)點動態(tài)創(chuàng)建在堆上。2. 插入/刪除會頻繁申請和釋放節(jié)點。3. 需要通過指針把左右子樹、父節(jié)點串聯(lián)。 | - 引用無法重新指向新節(jié)點。- 引用不能天然支持“空子樹”場景。 |
std::list |
1. 雙向鏈表,節(jié)點分散在堆上,每個節(jié)點需指向前驅(qū)/后繼。2. 插入刪除常數(shù)復(fù)雜度,依賴指針重連。 | - 引用無法為空,不能用來表示鏈表末端或空表。- 插入/刪除時無法更新引用重新指向新節(jié)點。 |
| B+ 樹 / B 樹 | 1. 內(nèi)部節(jié)點、葉子節(jié)點動態(tài)分配。2. 葉子節(jié)點間通過指針快速遍歷。3. 插入/分裂/合并時,節(jié)點間指針頻繁更新。 | - 引用不能在運行時變更綁定關(guān)系。- 無法表示“空孩子指針”。 |
其他容器(unordered_map / unordered_set) |
1. 基于哈希桶實現(xiàn),桶和節(jié)點動態(tài)分配。2. 哈希沖突需要通過鏈表或指針串聯(lián)節(jié)點。 | - 引用無法處理哈希桶為空的場景。- 無法在 rehash 時更新綁定。 |
舉例說明: 為什么 std::vector 內(nèi)部必須用指針
源碼(libstdc++ 實現(xiàn),<bits/stl_vector.h>):
template<typename _Tp, typename _Alloc = std::allocator<_Tp>>
class vector {
_Tp* _M_start; // 指向首元素
_Tp* _M_finish; // 指向最后一個元素后
_Tp* _M_end_of_storage; // 指向容量的末尾
};
原因:
1 動態(tài)擴容 ?vector 會在容量不足時 realloc,需要把所有元素搬遷到新內(nèi)存區(qū)域。 ? 如果用引用(T&),原來的綁定會失效 → 語義崩潰。 ? 指針可以重新指向新的內(nèi)存 → 動態(tài)調(diào)整 OK。 2 空容器支持 ? 空 vector 初始化時 _M_start = nullptr。 ? 引用不能是空的,所以指針是唯一選擇。 3 泛型友好 ? vector<T> 支持任意類型 T,不要求 T 必須可引用。?用指針實現(xiàn)不會對模板參數(shù)類型提出額外約束。
2.2 必須使用指針場景-延遲初始化場景:
“延長初始化”是指 對象的真正初始化推遲到需要使用它的那一刻,
而不是在對象聲明的時候立刻初始化。
在數(shù)據(jù)庫、存儲系統(tǒng)、分布式架構(gòu)中非常常見,比如:
? Redis:數(shù)據(jù)量極大,不能一次性初始化所有數(shù)據(jù)結(jié)構(gòu),否則啟動時間、內(nèi)存占用都不可接受。 ? Ceph:對象緩存和元數(shù)據(jù)加載也是懶加載。 ? TiDB:索引、Region、DDL 相關(guān)信息按需初始化。在這種情況下,指針 比 引用 更適合,主要原因是引用一旦綁定,就必須立即指向一個已初始化的對象,而指針可以:
? 一開始是nullptr?后續(xù)按需動態(tài)分配?再次釋放并重建
| 系統(tǒng) | 延遲加載對象/數(shù)據(jù) | 延遲加載元數(shù)據(jù) | 指針/引用作用 |
|---|---|---|---|
| Ceph | ? ObjectCacher | ? OMAP / Metadata | 延遲分配 + 內(nèi)存管理 |
| TiKV | ? Block / SST | ? Region Info | 指針/block handle 延遲訪問 |
| 3FS | ? 對象頁 | ? 元數(shù)據(jù)頁 | 指針/智能指針管理內(nèi)存 |
3FS offers an innovative caching mechanism known as KVCache.
Traditional DRAM-based caching can be both expensive and limited in capacity,
but KVCache provides a cost-effective alternative that delivers high throughput and a larger cache capacity.
3FS 提供了一種名為 KVCache 的創(chuàng)新緩存機制。傳統(tǒng)的基于 DRAM 的緩存不僅價格昂貴,容量也有限,而 KVCache 則提供了一種經(jīng)濟高效的替代方案,能夠提供高吞吐量和更大的緩存容量
這不就是 stl map 結(jié)構(gòu),redis 嗎?kvCache 也不是神秘面紗
2.3 必須使用指針場景-資源所有權(quán):
?C++ 內(nèi)存管理:指針負(fù)責(zé)生死,引用只是別名在 C++ 的世界里,
??資源所有權(quán)??的界限非常清晰:?**?new和 delete必須成對出現(xiàn),
由指針全權(quán)負(fù)責(zé);
比如你 new 一個對象,系統(tǒng)給你分配內(nèi)存,這塊內(nèi)存的“所有權(quán)”屬于你。
你需要用 delete 手動釋放,否則就會內(nèi)存泄漏。
而 引用本質(zhì)上只是對象的別名,它不擁有資源,不負(fù)責(zé)釋放。賦值引用不會復(fù)制對象,也不會影響對象生命周期。
而引用根本不參與資源的創(chuàng)建與釋放?**?。
MyClass obj;
MyClass& ref = obj;
// 只是別名,不創(chuàng)建新對象
// 無 release() 或 delete ref 的操作!
delete ref;//語法報錯 從語法層面避免這個操作
所以:
**指針=擁有權(quán),需要管理生命周期;
引用=別名,不管理生命周期

2.4 什么場景必須使用引用
核心原理:

【Modern Cpp】從萬能引用到完美轉(zhuǎn)發(fā)
萬能引用(Universal Reference)由Effective C++系列的作者Scott Meyers提出,
其對萬能引用的定義如下:
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
void fun(int &&a) { // a為右值引用
// do sth
}
int main() {
int a = 1;
fun(a);
// 編譯器報錯 沒有函數(shù)重載 錯誤:無法將左值‘int’綁定到‘int&&’
fun(1); // OK
}
template <typename T>
fun(T &&a) 編譯時?
| 場景 | 為什么用萬能引用 |
|---|---|
vector::push_back |
接收左值/右值都能正確構(gòu)造對象 |
| 函數(shù)模板轉(zhuǎn)發(fā)參數(shù) | 避免不必要拷貝,保持原有值類別 |
| 泛型工廠函數(shù) | 構(gòu)造對象并返回,左值/右值都高效處理 |
你一定會很奇怪,為什么萬能引用的形式明明是T&&,
卻既可以代表左值又可以代表右值。這就要涉及到C++的引用折疊語法了。
1?? 普通右值引用 vs 萬能引用
? 普通右值引用:int&& x
?
只能綁定右值(臨時量、std::move結(jié)果)
?
萬能引用(Forwarding Reference):template<typename T> void f(T&& t)
?
可以綁定左值或右值
?
這是 模板類型推導(dǎo)機制 導(dǎo)致的,而不是“引用類型缺少”本身。2?? 模板推導(dǎo)規(guī)則
模板函數(shù)參數(shù) T&& 會觸發(fā)引用折疊規(guī)則:
| 調(diào)用情況 | T 推導(dǎo)結(jié)果 | 函數(shù)參數(shù)類型 |
|---|---|---|
傳左值 x |
T = int& |
T&& = int& && → int& |
傳右值 std::move(x) |
T = int |
T&& = int&& |
關(guān)鍵點:引用折疊使得原本的右值引用
T&&變成了左值引用,從而可以綁定左值。
所以萬能引用的能力來源于模板推導(dǎo) + 引用折疊,而不是缺少引用類型。
3. 面試官視角反問
為什么 T&& 還要 std::forward?
1?? 背景
template<typename T>
void wrapper(T&& t) {
func(t); // ? t 是左值還是右值?可能失去右值語義
func(std::forward<T>(t)); // ? 完美轉(zhuǎn)發(fā)
}
? T&& t 是萬能引用(forwarding reference) ? t 在函數(shù)體內(nèi)總是左值,即便原始傳入的是右值
2?? 問題
? 如果直接用func(t): ? 左值參數(shù) → 正確,調(diào)用拷貝構(gòu)造/左值版本 ? 右值參數(shù) → 也被視作左值 → 調(diào)用拷貝構(gòu)造,而非移動構(gòu)造 ? 右值語義丟失,影響性能
3?? 解決:std::forward<T>(t) 重載不同類型處理方法,很 easy
4. 小白總結(jié)(面試官知道,我也知道)
? 我并沒有新增 c++語法知識,深入 引用是變量別名 擴展到變量所有權(quán)管理,這個很大話題**指針=擁有權(quán),需要管理生命周期;引用=別名,不管理生命周期 ? 我并沒有新增 c++語法知識,深入引用初始化必須綁定 擴展到變量重要數(shù)據(jù)結(jié)構(gòu)設(shè)計 ,這個是和很大話題 b++tree,紅黑樹 這個都是高頻題目?在參數(shù)傳遞過程中,在過程過程中,右值引用類型丟失這個隱藏的內(nèi)容我怎知道的?但凡閱讀過源碼,就知道STL里面充斥著大量的T&&以及std::forward

2.3 小王(王者 工作 5-10 年)面試
1. 面試官:談?wù)勀銓χ羔樑c引用區(qū)別理解
2. 小王回答(我根本不 關(guān)心這個什么指針語法問題):
架構(gòu)師才 不關(guān)心基于指針語法,
關(guān)心可維護,上線后不出事故,
哪怕出了事故,必須可控制,而不是丟數(shù)據(jù)致命問題
區(qū)別1:語言層面--生命周期管理

| 特性 | C++ 指針 | C++ 引用 | Rust 引用 & 生命周期 |
|---|---|---|---|
| 本質(zhì) | 存儲地址 | 對象別名 | 對象借用,編譯器跟蹤生命周期 |
| 是否擁有資源 | 可擁有(需要 delete) | 不擁有 | 不擁有,所有權(quán)由編譯器追蹤 |
| 初始化要求 | 可為 nullptr,隨時賦值 | 必須初始化,不可為 nullptr | 必須指向有效對象,借用檢查保證安全 |
| 生命周期管理 | 手動(裸指針)或智能指針 | 不管理生命周期 | 編譯器管理,自動釋放 |
| 安全性 | 容易懸掛或泄漏 | 安全,但不可重新綁定 | 編譯期保證安全,無懸掛 |
| 使用場景 | 緩存、大對象、懶加載、動態(tài)分配 | 函數(shù)參數(shù)、別名訪問 | 所有引用/借用、函數(shù)參數(shù)、高安全要求 |
| 可變性控制 | 指針指向可變或不可變對象 | 與原對象一致 | 可借用(不可變)或可變借用,編譯器保證互斥 |
舉例:
fn safe() -> &i32 { // 編譯錯誤!需要生命周期注解。
let x = 5;
&x // 錯誤!編譯器拒絕:`x` 的生命周期不夠長。
}
接口設(shè)計:
? 公共接口層:優(yōu)先引用?內(nèi)部實現(xiàn)層:優(yōu)先指針
在對象內(nèi)部或容器中存儲時:
1 如果需要可選性(optional) → 用裸指針或std::unique_ptr 2 如果需要共享所有權(quán) → 用 std::shared_ptr 3 如果需要弱引用(避免循環(huán)引用) → 用 std::weak_ptr
三、歷史題目:c++高頻面試題
| 序號 | 知識地圖 | 題目 |
|---|---|---|
| 1 | 新特性 | 一分鐘講透:c++新特性string_view |
| 2 | 庫的編譯鏈接 | 如何給一個高速行駛的汽車換輪胎(實現(xiàn)一個可擴展c++服務(wù)) |
| 3 | STL | Traits 技術(shù) |
| 4 | 新特性 | if constexpr |
| 5 | 新特性 | 面試題:C++中shared_ptr是線程安全的嗎? |
| 6 | 模板 | C++17 新特性 std::optional |
| 7 | class | c++類的成員函數(shù),能作為線程的參數(shù)嗎 |
| 8 | 編譯器 | const 如何保證const不變 |
| 9 | 值語義 | 一道面試題看深拷貝構(gòu)造函數(shù)問題 |
| 10 | 值語義 | 智能指針究竟在考什么 |
| 11 | 指針 | 使用 C++ 智能指針遇到的坑 |
| 12 | 指針 | 指針與引用區(qū)別 |
最動人的作品,為自己而寫,剛剛好打動別人
1?? 如果有更多疑問,聯(lián)系小王,一起交流,進步

2?? 關(guān)注公眾號:后端開發(fā)成長指南(回復(fù)"面經(jīng)"獲取)獲取過去我全部面試錄音和面試復(fù)盤。

浙公網(wǎng)安備 33010602011771號