← 12套面试训练

LOOP 03 / INTERVIEW LOOP / 4H

带状态的回调:谁拥有,谁只是在看

一个组件要稍后返回配置值。问题不在于 lambda 能不能写出来,而在于回调保存了什么、原对象何时结束,以及观察者是否检查了对象存在。

完成单元 27 · 移动捕获与std::function及其先备后安排本次训练。不绑定日历周。

先写自己的答案,再展开参考。这里按技能编写问题,不声称公司真题;自评与阅读都不会自动通过 G0。

先备与学习位置

回到对应的路线里程碑 →

TIME WITH OUTPUTS

240 分钟怎样用

  1. 闭卷恢复先备 · 15min

    闭卷写值捕获/引用捕获区别;解释 RAII 析构时机、unique/shared/weak 三种角色。

    留下:先不查章节,写三条判断和一个不确定点;再只补读相关章节的解释段。

  2. 澄清题目与手算 · 25min

    读主问题,逐条写输入、输出、范围与失败行为。手算前两个公开用例,给自己的方案找一个反例。

    留下:一份接口合同、逐步状态表和最小反例;不要先打开参考推演。

  3. 独立实现或设计 · 55min

    语法桥:先包含 <functional>;std::function<int()> 是可保存“无参数、返回 int”的可调用对象的容器。默认构造的 read 尚未装入回调,不能直接调用;赋入 lambda 后,read() 才调用它。这是本题提供的包装器用法,不要求先掌握类型擦除的实现。 完成一份可运行的小实验及事件表:在外层声明 std::function<int()> read;内层创建 int threshold=4,用值捕获给 read 赋一个返回 threshold 的 lambda,再把内层 threshold 改为 9。分别在内层修改后和内层结束后调用 read。然后设计一个保存 weak_ptr<int> 的观察回调:对象存在返回其值,不存在返回 -1。不要使用移动捕获;那是下一周的复习内容。

    留下:保存个人代码或设计稿。编码题保留编译命令;设计题为每个事件编号,不能只画没有状态的箭头。

  4. 边界与反证 · 25min

    用题目的全部公开用例核对真实输出;另外创造一个与公开输入不同的边界。发现失败时缩小输入,再解释修复。

    留下:实际结果、原始失败和修复原因各一份;设计题记录一个被拒绝的执行顺序。

  5. 按表现进入追问树 · 35min

    从 n1 开始。每题先录下自己的回答,再展开标准;按满足或未满足条件跳转。树最多走一轮,卡住时把剩余时间用于分支中的纠错,不循环加时。

    留下:记录经过的节点、原话、缺失条件和修订;只会复述标准的节点仍标记待重做。

  6. 英文口述与打断 · 30min

    用本页英文提纲录制两轮 90 秒回答;第二轮在 30 秒处自问追问,先回答追问再回到主线。每轮回听并改写两个含糊句。

    留下:两轮录音或逐字稿;圈出输入、因果、边界和限制各一句。参考英文在录完第一轮后再读。

  7. 复盘与安排重做 · 25min

    对照强弱答案,选择一个真正出错的环节:合同、状态、实现、验证或表达。填本页记录模板,并约定下一天的 30 分钟。

    留下:一个有失败输入的错误记录和一个可观察修复标准;不要把全部问题归因为粗心。

  8. 次日无答案重做 · 30min

    次日不查主解答:把初始值改为 6、内层改为 1;观察对象初值改为 0。画出三次调用:值回调在 scope 外、观察回调在 reset 前、观察回调在 reset 后,再独立实现有效路径。

    留下:这是本周 240 分钟中的最后 30 分钟,不另加预算。先关闭解答,用 20 分钟独立做,再用 10 分钟核验并记录是否仍需复习。

包括次日重做,总计 240min。局部错因复盘属于本次练习,额外的计划复盘按实际需要另记。

SOLVE BEFORE READING THE ANSWER

先独立处理这个问题

语法桥:先包含 <functional>;std::function<int()> 是可保存“无参数、返回 int”的可调用对象的容器。默认构造的 read 尚未装入回调,不能直接调用;赋入 lambda 后,read() 才调用它。这是本题提供的包装器用法,不要求先掌握类型擦除的实现。 完成一份可运行的小实验及事件表:在外层声明 std::function<int()> read;内层创建 int threshold=4,用值捕获给 read 赋一个返回 threshold 的 lambda,再把内层 threshold 改为 9。分别在内层修改后和内层结束后调用 read。然后设计一个保存 weak_ptr<int> 的观察回调:对象存在返回其值,不存在返回 -1。不要使用移动捕获;那是下一周的复习内容。

输入与输出合同

  • 第一个回调必须在内层对象结束后仍可调用;不运行悬垂引用版本。
  • 第二个回调用 lock 得到临时 shared_ptr 后再读取;过期时返回 -1。
  • 为这道题约定持有值只取 0..100,故 -1 可明确表示过期;不能把这个哨兵推广到任意整数接口。
手算之后,对照公开用例
输入或情形预期为什么
threshold 从 4 改 9;值捕获回调在内层调用4保存的是创建 lambda 时的值副本。
内层 scope 结束后再调用值捕获回调4副本属于闭包对象。
shared_ptr 拥有 7,观察回调 lock 成功7临时强引用在读取期间维持对象。
唯一外部 shared_ptr reset,之后再调用观察回调-1weak_ptr 不延长被观察对象的生命周期。

先保留自己的解法、状态轨迹与验证记录,再打开参考。允许用自然语言或伪代码补充解释;不能用参考输出代替真实运行。

保存独立尝试后,阅读解法与状态轨迹

解法依据

第一段按值捕获 threshold;read 保存可调用对象,其中副本为 4,原 threshold 改成 9 不会改副本。第二段把 weak_ptr 按值保存到闭包,每次调用都先 lock;得到非空 shared_ptr 才解引用,否则返回 -1。对比引用捕获版本只画生命周期图并说明错误,不通过运行未定义行为来猜结果。

逐步推演

t0:threshold=4,闭包副本=4。t1:原值=9,闭包仍 4。t2:内层对象结束,闭包继续存在。观察链:owner→整数 7,weak 只观察;调用时 temporary owner 存在。调用结束释放 temporary;外部 owner reset 后对象结束,下一次 lock 失败。

FOLLOW THE ANSWER, NOT A SCRIPT

从回答进入下一层追问

n1 开始。先录下回答,再展开判定;达到条件就走深入分支,未达到就按反馈缩小问题。一次最多走一轮,不靠反复查看同一答案累积“通过”。

把实际走过的节点记入模板 →

NODE n1

把第一个 lambda 改成引用捕获后,scope 外应期待输出 9 吗?

我已作答:查看判定与下一分支

需要解释清楚:不能期待任何固定结果:引用指向的局部对象已结束,调用会访问无效对象。

达到

结论正确,并解释本节点给出的具体状态或反例。

保留关键推理:不能期待任何固定结果:引用指向的局部对象已结束,调用会访问无效对象。

继续到 n2 →

待补

只报术语、漏掉条件,或不能解释题中的数值。

画出 lambda 保存的引用箭头和 scope 结束位置,不执行该错误版本。

继续到 r1 →

NODE r1

把第一个追问缩到最小输入,重新逐步说明。

我已作答:查看判定与下一分支

需要解释清楚:不能期待任何固定结果:引用指向的局部对象已结束,调用会访问无效对象。

修复后达到

用具体输入改正原回答,能指出之前错在哪里。

画出 lambda 保存的引用箭头和 scope 结束位置,不执行该错误版本。

继续到 n2 →

仍未达到

仍依赖答案复述,不能独立重建状态。

本次树到此结束;把 n1 的输入和缺口写入复盘,再进入英文练习与次日重做。

这一分支结束:对照并复盘 →

NODE n2

先判断 weak.expired() 为 false,再单独 lock 然后直接解引用,足够吗?

我已作答:查看判定与下一分支

需要解释清楚:最终必须检查 lock 的结果;expired 的旧观察不能替代本次读取拿到强引用。单线程实验也应写正确接口。

达到

结论正确,并解释本节点给出的具体状态或反例。

保留关键推理:最终必须检查 lock 的结果;expired 的旧观察不能替代本次读取拿到强引用。单线程实验也应写正确接口。

继续到 n3 →

待补

只报术语、漏掉条件,或不能解释题中的数值。

把接口写成“获得有效 temporary 或报告过期”,省去把两次观察混成保证。

继续到 n3 →

NODE n3

如果函数值域允许 -1,这个错误返回还可靠吗?

我已作答:查看判定与下一分支

需要解释清楚:不可靠,合法 -1 与过期不可区分;应改为 bool 加输出参数,或下一周学习的显式缺失类型。

达到

结论正确,并解释本节点给出的具体状态或反例。

保留关键推理:不可靠,合法 -1 与过期不可区分;应改为 bool 加输出参数,或下一周学习的显式缺失类型。

这一分支结束:对照并复盘 →

待补

只报术语、漏掉条件,或不能解释题中的数值。

给两个情景各填返回值:对象值=-1,和对象不存在;发现无法区分后改合同。

这一分支结束:对照并复盘 →

答案强在哪里,弱在哪里

保存自己的回答后,打开对照

弱答案

“引用捕获省复制,所以更好;weak_ptr 也是智能指针,可以直接读。”

强答案

“回调可能晚于局部变量执行,因此我选择值捕获。观察共享对象时,weak_ptr 不保证对象仍在,我在每次读取前 lock,并在临时强引用的作用域内完成读取。过期按合同返回 -1。”

强答案说明延迟执行导致的生命周期要求,并给出读取期间的所有者。所谓省复制不能凌驾于对象必须存活这一前提。

在自己的原回答里划出一个缺失的条件或错误推理,再重说一遍。完整句子背熟,不代表能处理新约束。

ANSWER, THEN HANDLE AN INTERRUPTION

英文口述与打断

Explain why capture choice is a lifetime decision.

  1. State when the callback runs.
  2. Identify the owner of the saved state.
  3. Explain the weak observation contract.

“Is a reference always faster and therefore better?”

录完第一轮后,读英文示范
The callback may run after the local scope ends, so I save the configuration by value. The closure owns its copy, which remains four even after the original changes. For shared state, I lock the weak pointer and read only while the temporary strong owner is valid. An expired object follows the explicit failure contract.
  • 前 15 秒说明输入、输出和一个关键约定。
  • 至少用一个本题的数值解释因果,不只罗列英文术语。
  • 明确一个边界或尚未验证的限制;不能把教学推演说成项目经验。

次日关掉解答,换一个问题

次日不查主解答:把初始值改为 6、内层改为 1;观察对象初值改为 0。画出三次调用:值回调在 scope 外、观察回调在 reset 前、观察回调在 reset 后,再独立实现有效路径。

独立重做后核对

三次分别为 6、0、-1。0 是有效载荷,不表示过期。说明 lambda 副本与 weak_ptr 观察对象分别由谁保持生命;不运行悬垂引用分支。

  • 关闭主问题答案并使用新输入独立完成。
  • 给出结果与原因;失败时记录最小反例,不能只把完成标记改成通过。

重做的 30min 已计入本套4h;需要更多补课时记录实际用时,按缺口顺延。

记录实际回答与证据

本次面试练习模板
# W03 算法 / 英文训练记录(仅个人练习,不是能力认证)
日期:
本次先备与尚不确定点:
主问题接口合同:
我最初的方案与状态表:
运行命令或设计事件编号:
失败输入 / 预期 / 实际:
修复与理由:
追问路径(节点 → 自己原话 → 缺口):
英文第一轮两处含糊句:
英文第二轮修订:
次日重做日期(30 分钟已含本周预算):
重做输入 / 实际结果 / 解释:
仍需补练:

本次练习的检查依据

有效路径输出符合事件表;能拒绝悬垂调用并解释 lock 返回值检查;不把本题提升为并发线程安全证明。

模板在你的个人笔记中填写。网页只提供空模板;不会把这次训练写成项目经验或学习验收结果。