C++ 面试常问问题汇总:基础语法、面向对象、STL、多线程与设计模式
本文系统整理 C++ 面试中的高频问题,覆盖基础语法、面向对象、内存管理、智能指针、STL、C++11/14/17、多线程并发、编译链接、设计模式以及项目实战追问。适合算法工程师、机器人控制工程师、嵌入式 C++、后端 C++ 等方向面试复习使用。
目录
一、C++ 基础语法高频问题
| 问题 |
面试重点 |
| C 和 C++ 的区别? |
C++ 支持面向对象、模板、异常、STL、RAII、泛型编程等特性 |
struct 和 class 的区别? |
struct 默认访问权限是 public,class 默认访问权限是 private |
const 的作用? |
可修饰变量、指针、函数参数、返回值、成员函数,用于限制修改 |
static 的作用? |
可用于静态局部变量、静态成员变量、静态成员函数、文件作用域限制 |
inline 的作用? |
建议编译器将函数内联展开,减少函数调用开销,但不是强制 |
extern 的作用? |
声明外部变量或函数,常用于多文件共享全局变量 |
volatile 的作用? |
告诉编译器变量可能被外部改变,避免编译器过度优化 |
sizeof 和 strlen 的区别? |
sizeof 求对象所占内存大小,通常编译期确定;strlen 运行期计算字符串长度 |
new/delete 和 malloc/free 的区别? |
new/delete 会调用构造和析构函数,malloc/free 只负责内存分配和释放 |
| 引用和指针的区别? |
引用必须初始化且不能重新绑定;指针可以为空,也可以改变指向 |
常见追问
1. const 修饰指针有哪些情况?
const int* p1; // 指向常量的指针,不能通过 p1 修改值,但 p1 可以改变指向
int const* p2; // 与 const int* 等价
int* const p3; // 指针常量,p3 不能改变指向,但可以修改指向的值
const int* const p4;// 指向常量的指针常量,指向和值都不能改
2. new 和 malloc 的核心区别
int* p1 = new int(10); // 分配内存并初始化
int* p2 = (int*)malloc(sizeof(int)); // 只分配内存,不调用构造函数
delete p1;
free(p2);
二、面向对象高频问题
| 问题 |
面试重点 |
| 面向对象三大特性? |
封装、继承、多态 |
| 重载、重写、隐藏的区别? |
重载是同作用域函数参数不同;重写是子类覆盖父类虚函数;隐藏是子类同名函数遮蔽父类函数 |
| 虚函数是什么? |
使用 virtual 修饰,通过虚函数表实现运行时多态 |
| 纯虚函数是什么? |
virtual void f() = 0;,含纯虚函数的类是抽象类 |
| 构造函数可以是虚函数吗? |
不可以,对象构造过程中虚表尚未完整建立 |
| 析构函数为什么常设为虚函数? |
防止通过父类指针删除子类对象时,只调用父类析构函数导致资源泄漏 |
| 虚函数表是什么? |
存储虚函数地址的表,对象中通常含有虚表指针 |
| 多态的实现原理? |
父类指针或引用 + 虚函数 + 虚函数表动态绑定 |
| 菱形继承问题? |
多继承导致公共基类被重复继承,可通过虚继承解决 |
| 拷贝构造和赋值运算符区别? |
拷贝构造用于初始化新对象,赋值运算符用于已有对象之间赋值 |
多态示例
#include <iostream>
#include <memory>
class Controller {
public:
virtual void computeControl() = 0;
virtual ~Controller() = default;
};
class PIDController : public Controller {
public:
void computeControl() override {
std::cout << "PID control" << std::endl;
}
};
class MPCController : public Controller {
public:
void computeControl() override {
std::cout << "MPC control" << std::endl;
}
};
int main() {
std::unique_ptr<Controller> controller = std::make_unique<MPCController>();
controller->computeControl();
return 0;
}
三、内存管理高频问题
| 问题 |
面试重点 |
| C++ 内存分区有哪些? |
栈区、堆区、全局/静态区、常量区、代码区 |
| 栈和堆的区别? |
栈自动管理、速度快、空间较小;堆手动管理、空间较大、容易内存泄漏 |
| 什么是内存泄漏? |
申请的内存没有被释放,导致长期运行时内存占用不断增加 |
| 什么是野指针? |
指向无效内存地址的指针 |
| 什么是悬空指针? |
指针指向已经释放的对象 |
| 如何避免内存泄漏? |
使用 RAII、智能指针、容器管理资源,减少裸 new/delete |
| 深拷贝和浅拷贝区别? |
浅拷贝只复制指针地址;深拷贝会重新申请内存并复制数据内容 |
| 析构函数作用? |
对象生命周期结束时释放资源 |
| RAII 是什么? |
Resource Acquisition Is Initialization,用对象生命周期自动管理资源 |
| 内存对齐是什么? |
按特定边界存储数据,提高 CPU 访问效率 |
RAII 示例
#include <iostream>
#include <fstream>
class FileGuard {
public:
explicit FileGuard(const std::string& file_name) {
file_.open(file_name);
}
~FileGuard() {
if (file_.is_open()) {
file_.close();
}
}
private:
std::fstream file_;
};
RAII 的核心思想是:资源在对象构造时获取,在对象析构时自动释放。这样可以减少资源泄漏,也能提高异常安全性。
四、智能指针高频问题
| 问题 |
面试重点 |
unique_ptr 是什么? |
独占所有权,不能拷贝,只能移动 |
shared_ptr 是什么? |
共享所有权,通过引用计数管理对象生命周期 |
weak_ptr 是什么? |
弱引用,不增加引用计数,常用于解决 shared_ptr 循环引用 |
shared_ptr 循环引用是什么? |
两个对象互相持有 shared_ptr,导致引用计数无法归零 |
make_shared 有什么好处? |
一次内存分配,提高效率,并且异常安全性更好 |
| 智能指针能完全替代裸指针吗? |
不能。非拥有关系、性能敏感场景仍可使用裸指针或引用 |
shared_ptr 是否线程安全? |
引用计数操作通常是线程安全的,但被管理对象本身不是线程安全的 |
unique_ptr 示例
#include <memory>
class Planner {};
int main() {
std::unique_ptr<Planner> planner = std::make_unique<Planner>();
// std::unique_ptr<Planner> p2 = planner; // 错误,不能拷贝
std::unique_ptr<Planner> p3 = std::move(planner); // 可以移动
return 0;
}
shared_ptr 和 weak_ptr 示例
#include <memory>
class Node {
public:
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 用 weak_ptr 打破循环引用
};
五、STL 高频问题
| 问题 |
面试重点 |
vector 底层原理? |
连续内存,支持随机访问,容量不足时动态扩容 |
vector 扩容机制? |
容量不足时重新申请更大内存,然后拷贝或移动旧元素 |
vector 和 list 区别? |
vector 随机访问快,插入删除慢;list 插入删除快,随机访问慢 |
map 和 unordered_map 区别? |
map 底层常为红黑树,有序;unordered_map 底层为哈希表,无序 |
set 和 unordered_set 区别? |
set 有序去重;unordered_set 哈希去重,无序 |
deque 和 vector 区别? |
deque 分段连续,支持头尾高效插入删除 |
| 迭代器失效是什么? |
容器修改后,原迭代器可能指向无效位置 |
push_back 和 emplace_back 区别? |
emplace_back 原地构造对象,减少临时对象 |
| STL allocator 是什么? |
STL 的内存分配器,用于控制容器内存申请和释放 |
priority_queue 底层是什么? |
默认使用大根堆 |
push_back 和 emplace_back 对比
#include <vector>
#include <string>
struct Student {
std::string name;
int age;
Student(const std::string& n, int a) : name(n), age(a) {}
};
int main() {
std::vector<Student> students;
students.push_back(Student("Tom", 18)); // 先构造临时对象,再放入 vector
students.emplace_back("Jack", 20); // 直接在 vector 内部构造对象
return 0;
}
六、C++11/14/17 高频问题
| 问题 |
面试重点 |
auto 的作用? |
自动类型推导 |
decltype 的作用? |
推导表达式类型 |
nullptr 和 NULL 区别? |
nullptr 是空指针类型,更类型安全 |
| 右值引用是什么? |
T&&,用于移动语义和完美转发 |
| 移动语义是什么? |
转移资源所有权,减少深拷贝开销 |
std::move 做了什么? |
将对象强制转换为右值引用,本身不移动数据 |
| 完美转发是什么? |
保持参数的左值或右值属性进行转发 |
| Lambda 表达式是什么? |
匿名函数对象,常用于回调、排序、自定义逻辑 |
std::function 和 std::bind 是什么? |
通用函数包装器和参数绑定工具 |
constexpr 是什么? |
用于编译期常量计算 |
override 作用? |
显式声明重写父类虚函数,避免误写 |
final 作用? |
禁止类被继承或虚函数继续被重写 |
移动语义示例
#include <iostream>
#include <vector>
int main() {
std::vector<int> a = {1, 2, 3, 4};
std::vector<int> b = std::move(a);
std::cout << "b size: " << b.size() << std::endl;
std::cout << "a size: " << a.size() << std::endl;
return 0;
}
注意:std::move 本身不移动数据,它只是把对象转换成右值引用,真正的资源转移发生在移动构造或移动赋值中。
七、多线程并发高频问题
| 问题 |
面试重点 |
| 进程和线程区别? |
进程是资源分配单位,线程是调度执行单位 |
| C++ 如何创建线程? |
使用 std::thread |
mutex 是什么? |
互斥锁,用于保护共享资源 |
lock_guard 和 unique_lock 区别? |
lock_guard 简单自动加解锁;unique_lock 更灵活,可延迟加锁、手动解锁 |
| 死锁是什么? |
多个线程互相等待对方释放资源,导致程序无法继续执行 |
| 如何避免死锁? |
固定加锁顺序、减少锁粒度、使用 try_lock、使用 RAII 管理锁 |
condition_variable 是什么? |
条件变量,用于线程等待和通知 |
atomic 是什么? |
原子变量,支持无锁原子操作 |
| 线程安全是什么意思? |
多线程访问共享资源时,程序行为仍然正确 |
| 生产者消费者模型? |
一个线程生产数据,另一个线程消费数据,常用队列、锁和条件变量实现 |
生产者消费者简化示例
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
std::queue<int> data_queue;
std::mutex mtx;
std::condition_variable cv;
void producer() {
for (int i = 0; i < 5; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
data_queue.push(i);
}
cv.notify_one();
}
}
void consumer() {
for (int i = 0; i < 5; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !data_queue.empty(); });
int value = data_queue.front();
data_queue.pop();
std::cout << value << std::endl;
}
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
八、编译、链接、程序运行高频问题
| 问题 |
面试重点 |
| C++ 程序从源码到可执行文件经历哪些阶段? |
预处理、编译、汇编、链接 |
| 头文件和源文件作用? |
头文件放声明,源文件放实现 |
#include "" 和 #include <> 区别? |
"" 优先搜索当前目录,<> 通常搜索系统路径 |
| 静态库和动态库区别? |
静态库编译进可执行文件;动态库运行时加载 |
| 编译期和运行期区别? |
编译期生成程序,运行期执行程序 |
| 链接错误常见原因? |
函数只有声明没有定义、库未正确链接、符号重复定义 |
宏和 inline 函数区别? |
宏是文本替换,没有类型检查;inline 函数有类型检查 |
| 模板为什么通常写在头文件? |
模板实例化需要看到完整定义 |
编译流程
源代码 .cpp
↓ 预处理
预处理文件 .i
↓ 编译
汇编文件 .s
↓ 汇编
目标文件 .o/.obj
↓ 链接
可执行文件
九、设计模式高频问题
设计模式是 C++ 面试中非常常见的内容,尤其是项目经历中涉及控制器、规划器、传感器模块、通信模块时,面试官很容易追问架构设计。
1. 单例模式 Singleton
核心作用
保证一个类在全局只有一个实例,并提供一个全局访问点。
常见场景
| 场景 |
示例 |
| 日志系统 |
Logger |
| 配置管理器 |
ConfigManager |
| 线程池管理器 |
ThreadPool |
| 全局资源管理 |
ResourceManager |
C++11 线程安全单例
class Logger {
public:
static Logger& instance() {
static Logger logger;
return logger;
}
void log(const std::string& msg) {
// 输出日志
}
private:
Logger() = default;
~Logger() = default;
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
};
面试常问
| 问题 |
回答重点 |
| 单例模式怎么实现? |
构造函数私有化,提供静态访问接口 |
| 懒汉式和饿汉式区别? |
懒汉式用到时创建,饿汉式程序启动时创建 |
| 单例如何保证线程安全? |
C++11 局部静态变量初始化是线程安全的 |
| 单例模式缺点? |
全局状态较强,测试困难,可能破坏依赖关系 |
2. 工厂模式 Factory
核心作用
工厂模式用于封装对象创建过程。调用者不直接 new 对象,而是通过工厂根据类型或配置创建具体对象。
适用场景
- 多种对象具有统一接口;
- 对象创建逻辑比较复杂;
- 需要根据配置动态选择具体实现;
- 希望降低调用者和具体类之间的耦合。
示例:根据配置创建不同规划器
#include <iostream>
#include <memory>
#include <string>
class Planner {
public:
virtual void plan() = 0;
virtual ~Planner() = default;
};
class DWAPlanner : public Planner {
public:
void plan() override {
std::cout << "DWA Planner" << std::endl;
}
};
class MPPIPlanner : public Planner {
public:
void plan() override {
std::cout << "MPPI Planner" << std::endl;
}
};
class PlannerFactory {
public:
static std::unique_ptr<Planner> createPlanner(const std::string& type) {
if (type == "DWA") {
return std::make_unique<DWAPlanner>();
} else if (type == "MPPI") {
return std::make_unique<MPPIPlanner>();
}
return nullptr;
}
};
int main() {
auto planner = PlannerFactory::createPlanner("MPPI");
if (planner) {
planner->plan();
}
return 0;
}
面试常问
| 问题 |
回答重点 |
| 工厂模式解决什么问题? |
解耦对象创建和对象使用 |
| 简单工厂、工厂方法、抽象工厂区别? |
创建复杂度和扩展方式不同 |
| 工厂模式优点? |
隐藏创建细节,方便扩展,降低耦合 |
| 工厂模式缺点? |
类数量增加,结构更复杂 |
| 什么时候适合用工厂模式? |
有多个同类对象,需要根据配置或输入动态创建 |
在机器人项目中的典型使用
createController("PID");
createController("MPC");
createController("MPPI");
createLocalPlanner("DWA");
createLocalPlanner("TEB");
createLocalPlanner("RPP");
3. 策略模式 Strategy
核心作用
策略模式将不同算法封装成不同策略类,使算法可以在运行时自由切换。
常见场景
| 场景 |
示例 |
| 路径跟踪控制器切换 |
PID / MPC / MPPI |
| 局部规划器切换 |
DWA / TEB / RPP / MPPI |
| 代价函数切换 |
距离代价 / 障碍物代价 / 平滑代价 |
| 排序规则切换 |
升序 / 降序 / 自定义优先级 |
| 轨迹评分方式切换 |
安全优先 / 平滑优先 / 速度优先 |
策略模式示例
#include <iostream>
#include <memory>
class ControlStrategy {
public:
virtual void compute() = 0;
virtual ~ControlStrategy() = default;
};
class PIDStrategy : public ControlStrategy {
public:
void compute() override {
std::cout << "PID compute" << std::endl;
}
};
class MPCStrategy : public ControlStrategy {
public:
void compute() override {
std::cout << "MPC compute" << std::endl;
}
};
class RobotController {
public:
void setStrategy(std::unique_ptr<ControlStrategy> strategy) {
strategy_ = std::move(strategy);
}
void run() {
if (strategy_) {
strategy_->compute();
}
}
private:
std::unique_ptr<ControlStrategy> strategy_;
};
面试常问
| 问题 |
回答重点 |
| 策略模式和工厂模式区别? |
工厂负责创建对象,策略负责算法替换和行为切换 |
| 策略模式优点? |
避免大量 if-else,方便扩展算法 |
| 策略模式缺点? |
策略类数量增加,调用方需要了解不同策略 |
4. 观察者模式 Observer
核心作用
当一个对象状态发生变化时,自动通知多个依赖对象。
常见场景
| 场景 |
示例 |
| ROS 订阅发布 |
Publisher / Subscriber |
| 事件回调 |
键盘事件、传感器事件 |
| GUI 系统 |
按钮点击通知 |
| 状态监控 |
机器人状态变化后通知多个模块 |
面试常问
| 问题 |
回答重点 |
| 观察者模式解决什么问题? |
一对多通知问题 |
| 观察者模式和发布订阅模式区别? |
发布订阅通常有中间消息总线,耦合更低 |
| 缺点是什么? |
通知链复杂,调试困难,可能出现循环通知 |
5. 适配器模式 Adapter
核心作用
适配器模式用于把一个类的接口转换成客户端期望的接口。
机器人项目中的典型场景
Gazebo 接口 → 统一 RobotInterface
MuJoCo 接口 → 统一 RobotInterface
真实底盘接口 → 统一 RobotInterface
不同雷达驱动接口 → 统一 SensorInterface
面试常问
| 问题 |
回答重点 |
| 适配器模式解决什么问题? |
接口不兼容问题 |
| 类适配器和对象适配器区别? |
类适配器通过继承实现,对象适配器通过组合实现 |
| 适配器和代理模式区别? |
适配器改变接口,代理控制访问 |
6. 代理模式 Proxy
核心作用
代理模式通过代理对象控制对真实对象的访问。
常见场景
| 场景 |
示例 |
| 网络代理 |
远程调用 |
| 权限控制 |
登录校验 |
| 延迟加载 |
大对象按需创建 |
| 日志统计 |
调用前后记录日志 |
7. 装饰器模式 Decorator
核心作用
装饰器模式可以在不修改原始类代码的情况下,动态增强对象功能。
控制器中的理解
BaseController
↓
SafetyControllerWrapper
↓
FilterControllerWrapper
↓
LogControllerWrapper
也就是说,可以给原始控制器逐层增加功能:
- 输出限幅;
- 输出滤波;
- 安全检查;
- 日志记录;
- 异常保护。
8. 模板方法模式 Template Method
核心作用
父类定义整体流程骨架,子类实现具体步骤。
局部规划器流程示例
1. 读取地图
2. 读取机器人状态
3. 读取目标点
4. 生成候选轨迹
5. 评价候选轨迹
6. 输出控制量
不同局部规划器可以复用整体流程,只重写核心算法步骤。
十、常见设计模式对比
| 模式 |
核心作用 |
典型关键词 |
机器人/算法项目示例 |
| 单例模式 |
保证全局唯一实例 |
全局配置、日志 |
参数管理器、日志系统 |
| 工厂模式 |
封装对象创建 |
create、new 解耦 |
创建 PID/MPC/MPPI 控制器 |
| 策略模式 |
封装算法切换 |
多算法、多策略 |
控制算法切换、局部规划器切换 |
| 观察者模式 |
状态变化通知 |
回调、订阅发布 |
ROS Topic、传感器事件回调 |
| 适配器模式 |
接口转换 |
兼容旧接口 |
Gazebo/MuJoCo/实物接口统一 |
| 代理模式 |
控制访问 |
权限、远程、缓存 |
远程控制接口、通信代理 |
| 装饰器模式 |
动态增强功能 |
Wrapper、包装器 |
控制器加限幅、滤波、安全检查 |
| 模板方法模式 |
固定流程,子类实现细节 |
流程复用 |
规划器统一流程框架 |
十一、C++ 项目常见追问
| 问题 |
面试重点 |
| 你项目中用到了哪些 C++ 特性? |
智能指针、STL、多态、模板、多线程、RAII |
| 你怎么管理对象生命周期? |
RAII、智能指针、避免裸 new/delete |
| 你的模块之间怎么解耦? |
接口类、多态、工厂模式、策略模式 |
| 你的代码怎么避免内存泄漏? |
智能指针、容器、析构函数释放资源 |
| 程序崩溃怎么排查? |
gdb、core dump、日志、断点、AddressSanitizer |
| 程序如何提高实时性? |
减少动态分配、预分配内存、降低锁竞争、减少拷贝 |
| 你用过多线程吗? |
传感器读取、控制循环、日志线程、通信线程 |
| 控制周期如何保证? |
定时器、实时线程、减少阻塞、监控循环耗时 |
十二、机器人/控制算法岗 C++ 追问
| 问题 |
面试关注点 |
| 控制器为什么要用多态? |
统一接口,方便切换 PID、MPC、MPPI 等控制器 |
| 局部规划器如何设计类结构? |
基类接口 + 子类实现 + 工厂创建 |
| 实物和仿真接口如何统一? |
使用适配器模式,将不同接口封装为统一抽象接口 |
| 控制循环怎么保证实时性? |
固定频率、预分配、避免阻塞、减少动态内存申请 |
| ROS 回调和主控制循环如何通信? |
线程安全队列、mutex、atomic、双缓冲 |
| 怎么避免控制器输出突变? |
滤波、限幅、平滑约束、控制增量约束 |
| 怎么组织参数配置? |
YAML/JSON + Config 类 + 参数校验 |
| C++ 中如何封装一个控制器? |
初始化、状态更新、轨迹输入、控制计算、输出控制量、reset |
控制器接口设计示例
class ControllerBase {
public:
virtual bool initialize() = 0;
virtual void reset() = 0;
virtual void updateState(const RobotState& state) = 0;
virtual void setReference(const Trajectory& ref) = 0;
virtual ControlCommand computeCommand() = 0;
virtual ~ControllerBase() = default;
};
这种设计的好处是:
- PID、MPC、MPPI 可以继承同一个接口;
- 上层模块不用关心具体控制器类型;
- 后续新增控制算法时,只需要增加子类,不需要大幅修改主流程。
十三、面试常见手写代码题
| 类型 |
高频内容 |
| 手写单例模式 |
C++11 静态局部变量版本 |
| 手写智能指针 |
简化版 shared_ptr |
| 手写线程池 |
任务队列 + mutex + condition_variable |
| 手写生产者消费者 |
队列 + 锁 + 条件变量 |
| 手写 LRU 缓存 |
list + unordered_map |
| 手写字符串函数 |
strcpy、strlen、memcpy |
| 手写排序算法 |
快排、归并、堆排 |
| 手写二叉树遍历 |
前序、中序、后序、层序 |
| 手写工厂模式 |
基类指针 + 子类对象创建 |
| 手写观察者模式 |
注册、注销、通知 |
十四、推荐复习顺序
建议按照下面顺序准备 C++ 面试:
1. C++ 基础语法
2. 面向对象:继承、多态、虚函数
3. 内存管理:new/delete、RAII、智能指针
4. STL:vector、map、unordered_map、迭代器失效
5. C++11:右值引用、move、lambda、thread
6. 多线程:mutex、condition_variable、atomic
7. 设计模式:单例、工厂、策略、观察者、适配器
8. 项目八股:模块解耦、实时性、内存泄漏、调试方法
如果时间有限,建议优先掌握下面几块:
| 优先级 |
内容 |
| 第一优先级 |
面向对象、虚函数、多态、析构函数、智能指针 |
| 第二优先级 |
STL 容器底层、迭代器失效、C++11 特性 |
| 第三优先级 |
多线程、锁、条件变量、原子操作 |
| 第四优先级 |
设计模式和项目架构设计 |
十五、设计模式面试总结模板
如果面试官问"你项目中用过哪些设计模式",可以这样回答:
在项目中,我主要使用了工厂模式、策略模式、适配器模式和观察者模式。工厂模式用于根据配置动态创建不同的控制器或局部规划器,例如 PID、MPC、MPPI、DWA 等;策略模式用于封装不同算法,使多个控制策略可以在统一接口下切换;适配器模式用于统一仿真环境和实物底盘接口,例如将 Gazebo、MuJoCo 和真实机器人底盘封装成统一的 RobotInterface;观察者模式则常见于传感器数据回调、事件通知以及 ROS 发布订阅机制。通过这些设计方式,可以降低模块之间的耦合度,提高代码扩展性、可维护性和复用性。
总结
C++ 面试并不是只考语法细节,更重要的是考察对工程代码的理解能力。对于机器人、自动驾驶、控制算法、嵌入式等方向,除了掌握 C++ 基础,还需要重点理解以下内容:
- 如何使用多态设计统一接口;
- 如何用智能指针管理对象生命周期;
- 如何用 STL 提高开发效率;
- 如何避免内存泄漏和野指针;
- 如何设计线程安全的数据传递方式;
- 如何用工厂模式、策略模式、适配器模式降低模块耦合;
- 如何结合项目经历讲清楚 C++ 在工程中的实际作用。
如果能够把"语法知识"和"项目架构"结合起来回答,面试效果会更好。