首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者

C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者

作者头像
早起的鸟儿有虫吃
发布2025-10-10 12:02:04
发布2025-10-10 12:02:04
700
代码可运行
举报
运行总次数:0
代码可运行

一、背景(可跳过直接看 2.2 章节)

各位老师好

CPP面试冲刺周刊 (c++ weekly)第三期开始了

目标:不是成为C++专家,而是成为C++面试专家

本期内容:指针与引用区别

c++周刊目的陪你一起快速冲击大厂面试

小提示:不要把他看成一个出售给你产品,我只出售给自己 在公司做任何事情事情, 都必须清楚拆解需求功能,开发周期,最后得到什么结果, 同样面试准备也是如此,给自己一个期限 21 天,给自己大纲,然后给自己 21 天学习结果,这样自己才能安心准备下去。

第一周(换个角度看问题):

曾经有一个让我心跳加速的岗位放在我面前, 我没有珍惜。 等到别人拿到 offer 的那一刻, 我才追悔莫及!

人世间,最痛苦的事情, 不是没钱吃饭, 也不是没房没车, 而是——错过了那个能让我逆天改命的机会!

如果上天再给我一次机会, 我一定会对那个岗位说三个字: “我要你!”

如果非要在这份“心动”上加一个期限, 一万年太久了…… 我只想要——21天!

你可能面临两种选择

① 犹豫不前:准备到天荒地老

“这个岗位太难了,我先准备一下吧。” 于是你准备1天、1周、1个月、1年…… 等再回头,3年就这样过去了

每天忙着搬砖,没时间系统复习

每次想起要准备,又感觉心里没底

面试知识点更新太快,拿着旧地图找新机会 最后,错过了一次又一次心动的岗位。

② 盲目回答:机会就在眼前,却抓不住

终于等来一场面试, 你觉得问题很简单,张口就答, 结果用“几千元思维”回答“百万年薪岗位”。

面试官问到C++底层实现,答不上来

设计题说到高并发架构,没实战经验

一紧张,连项目里真实做过的东西都讲不清

一次面试失利,也许就意味着和理想岗位失之交臂。

更残酷的是

在你犹豫的这几年里, 找工作的成本越来越高:

一个部门、一个领导,可能坚持一年就被解散

一个项目,可能在10年、20年后, 曾经复杂的业务规则、先进的架构,早已被淘汰

市场上新的技术和面试要求,每年都在不断升级

等你回过头来,发现不仅机会没了, 连准备的方向都变了

21天C++面试冲刺周刊

一万年太久,只争三周
一万年太久,只争三周

一万年太久,只争三周

不是让你成为C++专家, 而是让你成为C++面试专家

不是让你疯狂学习新知识, 而是帮你重新整理已有知识, 让你的能力与面试题精准对齐。

因为,21天就够了, 足够让我火力全开,

一边补齐 C++ 知识点,

一边刷爆经典面试题,

一边撸穿开源项目,

让自己变得不可替代!

核心方法论

让你学到每个 c++知识,都关联一个经典面试,并对对应开源项目实践

系统备战 每天 20~30 分钟,聚焦 C++ 核心知识, 三周时间完成高效梳理。

经典面试题 每个知识点都关联一个高频面试题, 让你知道“为什么考”和“怎么答”。

开源项目实践 通过真实项目理解底层原理, 不背答案,而是用实践打动面试官

场景驱动学习 还原真实面试场景, 帮你学会“怎么说服面试官”。

21天,你会获得什么?

一份完整的C++面试知识地图

一套高频题+解析+项目实践组合拳

一次全链路模拟面试体验

三周后,面对面试官,你能自信说出: “问吧,准备好了。”

这也是我的面试方法:

如果一开始就直接学某个知识点,我常常感觉不到它的实际价值。

所以我会先尝试树立一个整体的大局观,就算过程中被现实“啪啪打脸”了又怎样?

把每一次面试都当成一场陪练,用面试官的专业视角和真实项目来反推和校正自己的理解,不是更好吗?这种即时、高质量的反馈,是你看多少书、自己一个人闷头琢磨多久,都很难获得的。

二、从青铜 (小青)到王者(小王)回答:指针与引用区别

2.1 小青(青铜级别 工作 0-3 年)面试

1. 面试官:指针与引用区别

2. 小青回答(回答很棒了):

我熟读《C++ Primer》,引用本质上是变量(变量也是内存地址别名)的别名,定义时必须初始化;

引用不允许为 NULL,而指针可以为 NULL

原文里说:“A reference is not an object. Instead, a reference is just another name for an already existing object.” 这是最经典的描述

我 <<CPU眼里的C/C++>> <<c++反汇编与逆向分析技术>>从底层看,引用在汇编层面仍是用指针实现的,可以理解为“常量指针”,所以二者在本质上差别不大.

但语法和使用上有明显区别,引用更加安全

来源:CPU眼里的C/C++ 变量
来源:CPU眼里的C/C++ 变量

来源:CPU眼里的C/C++ 变量

参数传递
参数传递

参数传递

3. 面试官视角反问

这个是像素级别的模仿

无论怎么回答,都是照抄课本(我自己不会忘了搜索呀)。哪怕换个角度,比如从汇编层面解释,最会回答相同,跑题呀

这个时候,面试官沉默大约两分钟,显然在等候选人继续补充,看是否还能说出更多区别。

如果没有新的观点,或者候选人只是反复重复原来的答案,那就很尴尬了。

于是小青继续补充了几点:

指针的大小:在 64 位系统中,sizeof(pointer) 固定是 8 个字节;

自增运算的区别

指针 ptr++ → 偏移一个对象的地址;

引用 ref++ → 直接让变量本身加 1

4. 小青总结(这样回答还不够)

这个时候,其实不用太担心自己记不住所有指针的语法细节。 面试时,能回答多少就说多少,别陷入死记硬背。 更重要的是思路是否清晰,能不能结合项目经验去解释。

面试官往往是项目经理,他每天忙于业务推进,真的会关心你能背出多少条“指针 vs 引用”的区别吗?

这样会到还不够,为什么不能针针对区详细说明

小青疑惑:为什么我全部回答了,面试官还是不满意 ,我回答不高深吗?

2.2 小白(白银级别 工作 3-5 年)面试

1. 面试官:谈谈你对指针与引用区别理解

2. 小白回答(工作怎么用就怎么回答):

核心原则:

Use references when you can(能用引用就用引用 ✅)

pointers when you have to(必须使用指针场景,不用指针无法解决问题✅)

2.1 必须使用指针的场景:需要频繁更新/延长初始化的重要数据结构设计

需要频繁更新数据结构:

数据结构

为什么用指针

为什么不用引用

std::vector

1. 内部维护动态数组,存储区可扩容。2. 扩容后旧地址失效,必须更新新的堆指针。3. 需要支持空容器状态,用空指针表示。

- 引用必须绑定到对象,无法延迟绑定或置空。- 引用不能在扩容时重新绑定新内存。

std::map

1. 基于红黑树实现,节点动态创建在堆上。2. 插入/删除会频繁申请和释放节点。3. 需要通过指针把左右子树、父节点串联。

- 引用无法重新指向新节点。- 引用不能天然支持“空子树”场景。

std::list

1. 双向链表,节点分散在堆上,每个节点需指向前驱/后继。2. 插入删除常数复杂度,依赖指针重连。

- 引用无法为空,不能用来表示链表末端或空表。- 插入/删除时无法更新引用重新指向新节点。

B+ 树 / B 树

1. 内部节点、叶子节点动态分配。2. 叶子节点间通过指针快速遍历。3. 插入/分裂/合并时,节点间指针频繁更新。

- 引用不能在运行时变更绑定关系。- 无法表示“空孩子指针”。

其他容器(unordered_map / unordered_set)

1. 基于哈希桶实现,桶和节点动态分配。2. 哈希冲突需要通过链表或指针串联节点。

- 引用无法处理哈希桶为空的场景。- 无法在 rehash 时更新绑定。

举例说明: 为什么 std::vector 内部必须用指针

源码(libstdc++ 实现,<bits/stl_vector.h>):

代码语言:javascript
代码运行次数:0
运行
复制
template<typename _Tp, typename _Alloc = std::allocator<_Tp>>
class vector {
    _Tp* _M_start;    // 指向首元素
    _Tp* _M_finish;   // 指向最后一个元素后
    _Tp* _M_end_of_storage; // 指向容量的末尾
};


原因

1

动态扩容

vector 会在容量不足时 realloc,需要把所有元素搬迁到新内存区域。

如果用引用(T&),原来的绑定会失效 → 语义崩溃。

指针可以重新指向新的内存 → 动态调整 OK。

2

空容器支持

vector 初始化时 _M_start = nullptr

引用不能是空的,所以指针是唯一选择。

3

泛型友好

vector<T> 支持任意类型 T,不要求 T 必须可引用。

用指针实现不会对模板参数类型提出额外约束。

2.2 必须使用指针场景-延迟初始化场景:

“延长初始化”是指 对象的真正初始化推迟到需要使用它的那一刻, 而不是在对象声明的时候立刻初始化。

在数据库、存储系统、分布式架构中非常常见,比如:

Redis:数据量极大,不能一次性初始化所有数据结构,否则启动时间、内存占用都不可接受。

Ceph:对象缓存和元数据加载也是懒加载。

TiDB:索引、Region、DDL 相关信息按需初始化。

在这种情况下,指针引用 更适合,主要原因是引用一旦绑定,就必须立即指向一个已初始化的对象,而指针可以:

一开始是 nullptr

后续按需动态分配

再次释放并重建

系统

延迟加载对象/数据

延迟加载元数据

指针/引用作用

Ceph

✅ ObjectCacher

✅ OMAP / Metadata

延迟分配 + 内存管理

TiKV

✅ Block / SST

✅ Region Info

指针/block handle 延迟访问

3FS

✅ 对象页

✅ 元数据页

指针/智能指针管理内存

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 的创新缓存机制。传统的基于 DRAM 的缓存不仅价格昂贵,容量也有限,而 KVCache 则提供了一种经济高效的替代方案,能够提供高吞吐量和更大的缓存容量

这不就是 stl map 结构,redis 吗?kvCache 也不是神秘面纱

2.3 必须使用指针场景-资源所有权:

C++ 内存管理:指针负责生死,引用只是别名

在 C++ 的世界里, 资源所有权的界限非常清晰:newdelete必须成对出现, 由指针全权负责;

比如你 new 一个对象,系统给你分配内存,这块内存的“所有权”属于你。

你需要用 delete 手动释放,否则就会内存泄漏。

引用本质上只是对象的别名,它不拥有资源,不负责释放。赋值引用不会复制对象,也不会影响对象生命周期。

而引用根本不参与资源的创建与释放。

代码语言:javascript
代码运行次数:0
运行
复制
MyClass obj;
MyClass& ref = obj;
// 只是别名,不创建新对象 
// 无 release() 或 delete ref 的操作!

delete ref;//语法报错 从语法层面避免这个操作

所以:

指针=拥有权,需要管理生命周期;

引用=别名,不管理生命周期

2.4 什么场景必须使用引用

核心原理:

【Modern Cpp】从万能引用到完美转发

万能引用(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.

代码语言:javascript
代码运行次数:0
运行
复制

void fun(int &&a) { // a为右值引用
  // do sth

}

int main() {

  int a = 1;

  fun(a); 
  // 编译器报错 没有函数重载 错误:无法将左值‘int’绑定到‘int&&’
  fun(1); // OK

}

template <typename T>
fun(T &&a) 编译时
代码语言:javascript
代码运行次数:0
运行
复制
接收左值/右值都能正确构造对象

你一定会很奇怪,为什么万能引用的形式明明是T&&, 却既可以代表左值又可以代表右值。这就要涉及到C++的引用折叠语法了。

1️⃣ 普通右值引用 vs 万能引用

普通右值引用int&& x

只能绑定右值(临时量、std::move结果)

万能引用(Forwarding Reference)template<typename T> void f(T&& t)

可以绑定左值或右值

这是 模板类型推导机制 导致的,而不是“引用类型缺少”本身。


2️⃣ 模板推导规则

模板函数参数 T&& 会触发引用折叠规则

调用情况

T 推导结果

函数参数类型

传左值 x

T = int&

T&& = int& && → int&

传右值 std::move(x)

T = int

T&& = int&&

关键点:引用折叠使得原本的右值引用 T&& 变成了左值引用,从而可以绑定左值。 所以万能引用的能力来源于模板推导 + 引用折叠,而不是缺少引用类型。

3. 面试官视角反问

为什么 T&& 还要 std::forward

1️⃣ 背景
代码语言:javascript
代码运行次数:0
运行
复制
template<typename T>
void wrapper(T&& t) {
    func(t);              // ❌ t 是左值还是右值?可能失去右值语义
    func(std::forward<T>(t)); // ✅ 完美转发
}

T&& t 是万能引用(forwarding reference)

t 在函数体内总是左值,即便原始传入的是右值

2️⃣ 问题

如果直接用 func(t)

左值参数 → 正确,调用拷贝构造/左值版本

右值参数 → 也被视作左值 → 调用拷贝构造,而非移动构造

右值语义丢失,影响性能

3️⃣ 解决:std::forward<T>(t) 重载不同类型处理方法,很 easy

4. 小白总结(面试官知道,我也知道)

我并没有新增 c++语法知识,深入 引用是变量别名 扩展到变量所有权管理,这个很大话题 指针=拥有权,需要管理生命周期;引用=别名,不管理生命周期

我并没有新增 c++语法知识,深入引用初始化必须绑定 扩展到变量重要数据结构设计 ,这个是和很大话题 b++tree,红黑树 这个都是高频题目

在参数传递过程中,在过程过程中,右值引用类型丢失这个隐藏的内容我怎知道的

但凡阅读过源码,就知道STL里面充斥着大量的T&&以及std::forward

1

引用 = 变量别名

引用本身不是对象,它只是已经存在对象的另一个名字。

编译器在底层可能用指针实现,但语义上它没有独立内存,也不能重新绑定

2

生命周期管理

引用 不拥有资源,不负责分配或释放对象内存。

引用的有效期 依赖于所引用对象的生命周期

如果引用指向的对象被销毁,再访问就是悬挂引用(undefined behavior)。

3

架构哲学

引用用于短期访问和接口传递,保证语法安全

不能作为缓存或懒加载的资源管理工具

资源控制仍然需要指针或智能指针来实现

2.3 小王(王者 工作 5-10 年)面试

1. 面试官:谈谈你对指针与引用区别理解

2. 小王回答(我根本不 关心这个什么指针语法问题):

架构师才 不关心基于指针语法, 关心可维护,上线后不出事故, 哪怕出了事故,必须可控制,而不是丢数据致命问题

区别1:语言层面--生命周期管理

特性

C++ 指针

C++ 引用

Rust 引用 & 生命周期

本质

存储地址

对象别名

对象借用,编译器跟踪生命周期

是否拥有资源

可拥有(需要 delete)

不拥有

不拥有,所有权由编译器追踪

初始化要求

可为 nullptr,随时赋值

必须初始化,不可为 nullptr

必须指向有效对象,借用检查保证安全

生命周期管理

手动(裸指针)或智能指针

不管理生命周期

编译器管理,自动释放

安全性

容易悬挂或泄漏

安全,但不可重新绑定

编译期保证安全,无悬挂

使用场景

缓存、大对象、懒加载、动态分配

函数参数、别名访问

所有引用/借用、函数参数、高安全要求

可变性控制

指针指向可变或不可变对象

与原对象一致

可借用(不可变)或可变借用,编译器保证互斥

举例:

代码语言:javascript
代码运行次数:0
运行
复制
fn safe() -> &i32 { // 编译错误!需要生命周期注解。
    let x = 5;
    &x // 错误!编译器拒绝:`x` 的生命周期不够长。
}

接口设计:

公共接口层:优先引用

内部实现层:优先指针

在对象内部或容器中存储时:

1

如果需要可选性(optional) → 用裸指针或 std::unique_ptr

2

如果需要共享所有权 → 用 std::shared_ptr

3

如果需要弱引用(避免循环引用) → 用 std::weak_ptr

三、历史题目:c++高频面试题

序号

知识地图

题目

1

新特性

一分钟讲透:c++新特性string_view

2

库的编译链接

如何给一个高速行驶的汽车换轮胎(实现一个可扩展c++服务)

3

STL

Traits 技术

4

新特性

if constexpr

5

新特性

面试题:C++中shared_ptr是线程安全的吗?

6

模板

C++17 新特性 std::optional

7

class

c++类的成员函数,能作为线程的参数吗

8

编译器

const 如何保证const不变

9

值语义

一道面试题看深拷贝构造函数问题

10

值语义

智能指针究竟在考什么

11

指针

使用 C++ 智能指针遇到的坑

12

指针

指针与引用区别

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端开发成长指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景(可跳过直接看 2.2 章节)
    • 第一周(换个角度看问题):
    • ① 犹豫不前:准备到天荒地老
    • ② 盲目回答:机会就在眼前,却抓不住
    • 更残酷的是
    • 核心方法论:
    • 21天,你会获得什么?
  • 二、从青铜 (小青)到王者(小王)回答:指针与引用区别
    • 2.1 小青(青铜级别 工作 0-3 年)面试
      • 1. 面试官:指针与引用区别
      • 2. 小青回答(回答很棒了):
      • 3. 面试官视角反问
      • 4. 小青总结(这样回答还不够)
    • 2.2 小白(白银级别 工作 3-5 年)面试
      • 1. 面试官:谈谈你对指针与引用区别理解
      • 2. 小白回答(工作怎么用就怎么回答):
      • 3. 面试官视角反问
      • 4. 小白总结(面试官知道,我也知道)
    • 2.3 小王(王者 工作 5-10 年)面试
      • 1. 面试官:谈谈你对指针与引用区别理解
      • 2. 小王回答(我根本不 关心这个什么指针语法问题):
      • 内部实现层:优先指针
  • 三、历史题目:c++高频面试题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档