C++ 面试题实战:编程题 + AI 时代高频问答(修订版)
覆盖数值字符串解析、链表、合并有序数组,并重点澄清 Pod 和 Container(Docker/K8s)的区别,附 AI 时代面试回答模板
C++ 面试题实战:编程题 + AI 时代高频问答(修订版)
你给出的面试清单很典型:前半段考编码基本功,后半段考系统理解和表达能力。 这版我重点修正一个易混点:这里的 pod 和 container,指的是 Docker / K8s 语境,不是 C++ 的 POD 类型。
一、编程题(C++)
1)数值字符串解析(正负数、复数、科学计数法)
面试策略:
- 先定义输入协议(允许空格、
a+bi、1e-3等); - 用标准库解析实数(
std::stod); - 对复数做结构化拆分(处理
e+/e-不能误判为实虚部分隔符)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <cmath>
#include <complex>
#include <cctype>
#include <optional>
#include <string>
#include <variant>
using Numeric = std::variant<double, std::complex<double>>;
static inline std::string trim(std::string s) {
size_t l = 0, r = s.size();
while (l < r && std::isspace(static_cast<unsigned char>(s[l]))) ++l;
while (r > l && std::isspace(static_cast<unsigned char>(s[r - 1]))) --r;
return s.substr(l, r - l);
}
std::optional<double> parseReal(const std::string& raw) {
std::string s = trim(raw);
if (s.empty()) return std::nullopt;
size_t pos = 0;
try {
double v = std::stod(s, &pos);
if (pos != s.size()) return std::nullopt;
if (!std::isfinite(v)) return std::nullopt;
return v;
} catch (...) {
return std::nullopt;
}
}
std::optional<std::complex<double>> parseComplex(const std::string& raw) {
std::string s = trim(raw);
if (s.empty()) return std::nullopt;
if (s.find('i') == std::string::npos) {
auto real = parseReal(s);
if (!real) return std::nullopt;
return std::complex<double>(*real, 0.0);
}
if (s.back() != 'i') return std::nullopt;
s.pop_back();
int split = -1;
for (int i = 1; i < static_cast<int>(s.size()); ++i) {
if ((s[i] == '+' || s[i] == '-') && s[i - 1] != 'e' && s[i - 1] != 'E') split = i;
}
double re = 0.0, im = 0.0;
if (split == -1) {
if (s.empty() || s == "+") im = 1.0;
else if (s == "-") im = -1.0;
else {
auto imag = parseReal(s);
if (!imag) return std::nullopt;
im = *imag;
}
} else {
auto real = parseReal(s.substr(0, split));
if (!real) return std::nullopt;
re = *real;
std::string is = s.substr(split);
if (is == "+") im = 1.0;
else if (is == "-") im = -1.0;
else {
auto imag = parseReal(is);
if (!imag) return std::nullopt;
im = *imag;
}
}
return std::complex<double>(re, im);
}
std::optional<Numeric> parseNumeric(const std::string& s) {
auto c = parseComplex(s);
if (!c) return std::nullopt;
if (std::abs(c->imag()) < 1e-12) return c->real();
return *c;
}
2)链表:头插、尾插、删除 index、在 index 插入 val
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdexcept>
struct Node {
int val;
Node* next;
explicit Node(int v) : val(v), next(nullptr) {}
};
class LinkedList {
public:
LinkedList() : head_(nullptr), tail_(nullptr), size_(0) {}
~LinkedList() {
Node* cur = head_;
while (cur) {
Node* nxt = cur->next;
delete cur;
cur = nxt;
}
}
void pushFront(int val) {
Node* n = new Node(val);
n->next = head_;
head_ = n;
if (size_ == 0) tail_ = n;
++size_;
}
void pushBack(int val) {
Node* n = new Node(val);
if (size_ == 0) head_ = tail_ = n;
else {
tail_->next = n;
tail_ = n;
}
++size_;
}
void eraseAt(size_t index) {
if (index >= size_) throw std::out_of_range("index out of range");
if (index == 0) {
Node* del = head_;
head_ = head_->next;
delete del;
--size_;
if (size_ == 0) tail_ = nullptr;
return;
}
Node* prev = head_;
for (size_t i = 0; i + 1 < index; ++i) prev = prev->next;
Node* del = prev->next;
prev->next = del->next;
if (del == tail_) tail_ = prev;
delete del;
--size_;
}
void insertAt(size_t index, int val) {
if (index > size_) throw std::out_of_range("index out of range");
if (index == 0) return pushFront(val);
if (index == size_) return pushBack(val);
Node* prev = head_;
for (size_t i = 0; i + 1 < index; ++i) prev = prev->next;
Node* n = new Node(val);
n->next = prev->next;
prev->next = n;
++size_;
}
private:
Node* head_;
Node* tail_;
size_t size_;
};
3)merge 两个有序数组(nums1 预留 m+n 空间)
1
2
3
4
5
6
7
8
9
10
11
12
#include <vector>
void merge(std::vector<int>& nums1, int m, const std::vector<int>& nums2, int n) {
int i = m - 1;
int j = n - 1;
int k = m + n - 1;
while (j >= 0) {
if (i >= 0 && nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
else nums1[k--] = nums2[j--];
}
}
复杂度:时间 O(m+n),额外空间 O(1)。
二、面试问答(重点修正版)
1)shared_ptr、unique_ptr、weak_ptr
unique_ptr:独占所有权,默认首选;shared_ptr:共享所有权,有控制块与原子计数开销;weak_ptr:观察者,不增加强引用计数,用于打破循环引用。
一句话模板:
默认
unique_ptr,共享语义才上shared_ptr,环引用场景用weak_ptr。
2)Pod 和 Container 的区别(Docker / K8s)
注意:这里是云原生问题,不是 C++ 的 POD 类型。
可以这样答:
- Container(容器):运行实例(典型由 Docker/containerd 拉起),封装应用进程及其依赖;
- Pod(K8s):Kubernetes 的最小调度单元,里面可以放一个或多个容器,这些容器共享网络命名空间(同一个 Pod IP)和部分存储卷。
面试可加一句:
- 在生产里常见“一个 Pod 一个主容器 + 若干 sidecar(日志、代理、监控)”;
- 也就是说 Pod 是调度与部署边界,Container 是运行时边界。
3)K8s 的 Pod、Service
- Pod:承载容器、会被重建,IP 可能变化;
- Service:给一组 Pod 提供稳定访问入口(ClusterIP / DNS),通过 label selector 选后端。
一句话:
Pod 不稳定,Service 提供稳定访问。
4)AI 时代传统程序员的优势,如何保持并提升
优势:
- 工程化能力(可维护性、性能、稳定性);
- 复杂系统排障能力;
- 业务抽象能力(把需求落成系统)。
提升:
- 从“写代码”升级为“定义问题 + 验收标准”;
- 强化架构设计、数据建模、可观测性;
- 与 AI 建立协作流:Prompt 模板、自动化测试、代码评审清单。
5)MCP 和 Function Calling
- Function Calling:模型根据 schema 产生函数调用参数,由宿主执行;
- MCP:统一工具与资源接入协议,更偏生态级标准化接入。
一句话:
Function Calling 偏单应用调用,MCP 偏跨工具生态互联。
6)Skill
在 Agent 语境中,Skill 是“可复用能力包”,通常包含:
- 触发条件;
- 步骤模板;
- 工具清单;
- 输出格式与质量标准。
7)AI 发展趋势
建议答 4 点:
- 多模态融合持续增强;
- Agent 化从问答走向任务执行;
- 行业化深入(通用模型 + 私域数据 + 专业工具链);
- 安全合规与成本优化并行。
三、30 秒收尾模板
- 编程题:先思路 + 复杂度,再写主路径,最后补边界;
- 概念题:先定义,再对比,最后讲落地场景;
- AI 题:围绕业务价值、可控质量、上线成本。
祝你面试顺利,拿到满意 offer。
本文由作者按照 CC BY 4.0 进行授权