字节跳动 C++ QT PC客户端面试
字节跳动 C++ Qt PC客户端面试,总共三轮技术面(一面10道、二面20道、三面20道)
为了帮助更多的同学拿到满意的 offer,我把一二三面共50道面试题整理发布出来~供大家学习参考~
很多同学认为,面试问理论知识,就是八股文,实际工作中没有太大用处。但事实上,所谓的“八股文”,本质是经过提炼和标准化的专业术语和通用认知体系,它是我们高效沟通、协作和解决问题的基础。
换句话说,掌握并能准确使用这些“八股文”,不是为了应付面试,而是为了让我们在真实的工作场景中,更高效地表达、理解和落地复杂的技术问题。
话不多说,直接进入正题(更多八股文面试题,关注公钟呺[Linux教程]发送“面试”获取):
Part1一面(10道题)
面试题1:父子继承时构造函数和析构函数的顺序?析构时如何让父子类里的所有析构都调用一遍?
✅ 构造顺序:
- 先父后子
- :当创建一个派生类对象时,首先调用基类的构造函数,然后是派生类自己的构造函数。
- 如果有多个基类(多继承),按照它们在继承列表中出现的顺序依次构造。
✅ 析构顺序:
- 先子后父
- :析构时与构造顺序相反。先执行派生类的析构函数,再执行基类的析构函数。
- 如果基类析构函数不是 virtual,则通过基类指针删除派生类对象时不会触发派生类的析构函数,导致内存泄漏。
🧠 如何确保所有析构函数被调用?
- 使用虚析构函数(在基类中定义为 virtual ~Base() {})。
- 不要手动调用 delete this 或者重复释放同一对象。
- 避免裸指针管理资源,优先使用智能指针(如 std::unique_ptr)。
面试题2:一个函数,如何让它在 main 函数之前执行?
✅ 方法一:全局变量初始化
int before_main = [](){// 这里写你想执行的代码std::cout << "Before main\n";return 0;
}();
✅ 方法二:静态局部变量 + 类型构造
struct BeforeMain {BeforeMain() {// 执行你想运行的代码std::cout << "Before main via static object\n";}
};
static BeforeMain bm; // 全局静态对象,在main前构造
⚠️ 注意事项:
- 初始化顺序依赖于编译单元,不同文件中的全局变量初始化顺序不确定。
- 可以使用 std::call_once 或设计模式控制顺序。
面试题3:可以在 C++ 的成员函数里调用 delete this 吗?
✅ 可以,但必须满足以下条件:
- 对象是动态分配的(即通过 new 创建)。
- 调用 delete this 后不能再访问该对象的任何成员(包括 this 指针)。
- 成员函数不能是虚析构函数的一部分。
- 必须确保没有其他线程或代码正在访问该对象。
📌 示例:
class MyClass {
public:void selfDestruct() {delete this;}
};
MyClass* obj = new MyClass();
obj->selfDestruct(); // 正确
// obj 已经无效,不能再访问
❌ 错误示例:
MyClass obj;
obj.selfDestruct(); // 错误!栈上对象不能 delete
面试题4: 如何保证只打开一个 exe,再次打开时激活旧窗口并最大化显示?
✅ Windows 实现方式(使用互斥量 + 窗口查找):
#include <windows.h>
#include <QApplication>
#include <QMainWindow>
int main(int argc, char *argv[]) {HANDLE hMutex = CreateMutexW(nullptr, FALSE, L"MyAppSingleInstanceMutex");if (GetLastError() == ERROR_ALREADY_EXISTS) {// 已存在实例,查找主窗口HWND hWnd = FindWindowW(L"Qt5QWindowIcon", nullptr); // 替换为你程序的类名if (hWnd) {ShowWindow(hWnd, SW_RESTORE);SetForegroundWindow(hWnd);ShowWindow(hWnd, SW_MAXIMIZE);}return 0;}QApplication app(argc, argv);QMainWindow window;window.setWindowTitle("Only One Instance");window.show();return app.exec();
}
🧠 原理:
- 使用系统级互斥量防止重复启动。
- 查找已有窗口并激活它。
- 使用 Win32 API 控制窗口状态(最大化/恢复/置顶)。
面试题5:浏览器前进后退功能,使用哪种数据结构存放历史网页合适?
✅ 推荐数据结构:双栈结构(两个栈)
- 一个栈保存“前进”路径。
- 一个栈保存“后退”路径。
🔄 操作流程:
- 访问新页面 → 当前页压入“后退栈”,清空“前进栈”。
- 点击“后退” → 弹出“后退栈”顶部,压入“前进栈”。
- 点击“前进” → 弹出“前进栈”顶部,压入“后退栈”。
🧩 替代方案:
- 双向链表
- :更灵活,支持任意方向导航,适合复杂浏览器行为。
- vector + index
- :简单实现,适用于轻量级应用。
面试题6:Qt 多线程技术有哪些?哪些只有 QThread 能做,QtConcurrent 办不到?
✅ Qt 支持的多线程技术:
技术 | 描述 |
QThread | 最底层的线程类,需要手动管理线程生命周期 |
QtConcurrent::run() | 简化版线程池任务提交,自动回收线程 |
QtConcurrent::map() / filter() / mappedReduced() | 并行处理容器元素 |
QRunnable + QThreadPool | 提交任务给线程池执行 |
QFuture + QFutureWatcher | 异步任务监控 |
✅ QThread 特有的能力:
- 自定义线程事件循环(可响应信号槽)
- 更细粒度控制线程优先级、亲和性等
- 可用于创建长期运行的后台服务线程
❌ QtConcurrent 的限制:
- 无法直接操作线程事件循环
- 不适合长时间运行的任务
- 不支持自定义线程池配置
面试题7:C++11 有哪些智能指针?
✅ C++11 标准库提供的智能指针:
智能指针类型 | 行为描述 |
std::unique_ptr<T> | 独占所有权,不可复制,可移动 |
std::shared_ptr<T> | 共享所有权,引用计数自动管理 |
std::weak_ptr<T> | 观察 shared_ptr,不增加引用计数,防止循环引用 |
在Qt框架中还扩展了QScopedPointer和QSharedPointer,它们功能类似标准库的unique_ptr和shared_ptr,但能和Qt的对象系统更好集成。这些智能指针的核心价值是自动管理内存,避免内存泄漏,让资源管理更安全高效。
面试题8:如何使用 C++ 发送 HTTP 请求,并下载文件?
✅ 方法一:使用 Qt 自带模块 QNetworkAccessManager
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QFile>
void downloadFile(const QString& urlStr, const QString& savePath) {QNetworkAccessManager manager;QUrl url(urlStr);QNetworkRequest request(url);QNetworkReply* reply = manager.get(request);QObject::connect(reply, &QNetworkReply::finished, [=]() {if (reply->error() == QNetworkReply::NoError) {QFile file(savePath);if (file.open(QIODevice::WriteOnly)) {file.write(reply->readAll());file.close();qDebug() << "Download finished.";}} else {qDebug() << "Error:" << reply->errorString();}reply->deleteLater();});// 主线程阻塞等待(仅用于测试)QEventLoop loop;connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);loop.exec();
}
✅ 方法二:使用更高级的QNetworkReply,它能自动处理重定向和SSL加密。关键点在于正确管理内存,确保响应数据能完整写入文件,同时要注意错误处理和超时控制
面试题9:QWidget 和 QML 的技术本质和使用上的区别?
维度 | QWidget | QML |
技术本质 | 基于 C++ 的传统 UI 框架 | 基于声明式语言(类似 JavaScript)的 UI 框架 |
渲染机制 | QPainter 直接绘制控件 | 场景图(Scene Graph)+ OpenGL 渲染 |
开发效率 | C++ 编码为主,UI 设计较繁琐 | QML 可视化布局,热重载方便 |
性能 | CPU 渲染,适合桌面传统界面 | GPU 加速渲染,适合动画和现代 UI |
适用场景 | 传统桌面软件、工具类应用 | 移动端适配、动画丰富、UI 动态变化频繁的应用 |
跨平台 | 支持良好 | 支持良好,尤其适合移动端 |
📌 何时选择?
- 需要高性能图形渲染、动画效果 → QML
- 需要快速开发传统桌面应用 → QWidget
面试题10:Qt 的信号与槽,有哪几种连接方式?对应的应用场景是什么?(第五个参数)
✅ 连接方式(第五个参数):
参数 | 类型 | 说明 |
Qt::AutoConnection | 默认值 | 自动选择 Direct 或 Queued,取决于线程 |
Qt::DirectConnection | 直接调用 | 槽函数立即执行(同步) |
Qt::QueuedConnection | 队列调用 | 槽函数异步执行,跨线程安全 |
Qt::BlockingQueuedConnection | 阻塞队列 | 槽函数异步执行,发送方线程阻塞直到完成(跨线程) |
Qt::UniqueConnection | 唯一连接 | 保证信号和槽之间只有一个连接(与其他组合使用) |
✅ 应用场景举例:
连接方式 | 使用场景 |
Direct | 同一线程内快速响应(如按钮点击) |
Queued | 跨线程通信(如主线程更新 UI) |
BlockingQueued | 需要返回结果的跨线程调用(谨慎使用) |
Auto | 通用默认选项,自动判断 |
🧠 示例:
connect(sender, &Sender::signalName, receiver, &Receiver::slotName, Qt::QueuedConnection);
Part2二面(20道题)
面试题11:GPU渲染和CPU渲染之间的区别是什么?
特性 | CPU 渲染 | GPU 渲染 |
运算核心 | 单核/多核通用处理器 | 大量并行计算单元 |
擅长任务 | 复杂逻辑控制、小数据处理 | 大规模并行计算(如图形像素处理) |
内存访问 | 使用系统内存 | 使用显存(VRAM) |
延迟 | 较高延迟 | 极低延迟,适合实时渲染 |
能耗 | 高功耗(复杂运算) | 更高效能比 |
典型用途 | 文本编辑器、非图形应用 | 游戏引擎、视频播放、动画 UI |
🧠 Qt 中:
- QWidget 默认使用 QPainter + CPU 渲染。
- QML 默认使用 Scene Graph + OpenGL/GLES + GPU 渲染。
面试题12:集成显卡用户进行渲染时,在编码阶段是走硬编还是软编?
✅ 硬编 vs 软编:
- 硬编(Hardware Encoding)
- :利用显卡硬件编码器(如 Intel Quick Sync、NVIDIA NVENC)加速视频压缩。
- 软编(Software Encoding)
- :完全由 CPU 计算完成,灵活性高但效率低。
✅ 编码阶段是否走硬编取决于:
- 是否调用了支持硬编的库(如 FFmpeg、Media SDK)。
- 当前平台是否支持该硬编接口(如 Windows 上 DirectX Video Acceleration)。
- 显卡驱动是否正常安装。
集成显卡在编码阶段可以走硬编,但需要开发者主动选择合适的 API 和库。
面试题13:硬编/软编 和 GPU/CPU 渲染是一回事吗?
❌ 不是一回事,它们属于不同层级的概念:
维度 | 硬编/软编 | GPU/CPU 渲染 |
层级 | 视频编码层 | 图形绘制层 |
功能 | 将视频帧压缩为 H.264/H.265 格式 | 在屏幕上绘制像素 |
硬件依赖 | 显卡编码器支持 | 显卡渲染管线支持 |
应用场景 | 视频录制、直播推流 | UI 绘制、游戏画面显示 |
🧠 举例说明:
- 一个程序可能使用 GPU 渲染 UI,但视频编码时仍使用软编(FFmpeg x264)。
- 另一个程序可能使用 GPU 渲染 UI,同时使用硬编(NVENC)进行录屏编码。
关联性:
-
- GPU渲染 + 硬编:最优路径(显存→编码器,零拷贝)
- GPU渲染 + 软编:需回传内存→CPU,性能损耗
- CPU渲染 + 硬编:需上传内存→显存,仍有损耗
- CPU渲染 + 软编:完全CPU路径,性能最低
面试题14:QCoreApplication::setAttribute作用
✅ 作用:
强制 Qt 应用程序使用 OpenGL ES(嵌入式系统使用的轻量级 OpenGL)而不是桌面版 OpenGL。
✅ 适用场景:
- 移动端开发(Android/iOS)
- 集成显卡或不支持完整 OpenGL 的设备
- 需要跨平台兼容性(尤其是移动端)
📌 示例:
QApplication::setAttribute(Qt::AA_UseOpenGLES);
QApplication app(argc, argv);
⚠️ 注意:不是所有平台都支持 OpenGL ES(如某些 Windows 平台需安装 ANGLE)。
面试题15:QWidget 和 QML 在渲染层面的区别?
渲染机制 | QWidget | QML |
底层技术 | QPainter + 软件光栅化 | Scene Graph + GPU加速 |
更新流程 | 脏矩形标记 → 逐层重绘 | 场景图差异 → 批量提交GPU |
透明度处理 | 逐层混合(性能差) | Shader一次合成(性能优) |
动画性能 | 依赖定时器+重绘(CPU 渲染,性能有限) | 属性绑定+GPU插值(GPU 加速,适合动画和复杂 UI) |
抗锯齿 | 慢(CPU计算) | 快(MSAA/FXAA) |
典型帧率 | 30-60 FPS(复杂UI卡顿) | 60+ FPS(流畅) |
- QWidget 更适合传统桌面工具类软件。
- QML 更适合现代 UI、动画丰富、响应式设计的应用。
面试题16:Win11 对Qt有哪些新特性支持?
而且从界面表现来看,Qt 程序在 Win11 上也能很好地适配,比如支持圆角窗口、深色模式、DPI 缩放优化等现代 UI 特性。只要注意一些系统级别的适配(比如高分辨率支持、任务栏集成),基本上写一次代码就可以直接运行。”
如果项目有特殊需求,比如想利用 Win11 的一些新特性(如 Direct3D 渲染、触控支持、通知机制等),也可以通过调用 Windows SDK 接口来扩展功能,Qt 提供了良好的底层支持能力。
面试题17:Windows 下窗口刷新机制是立即刷新还是异步刷新?
✅ 异步刷新机制:
- Windows 使用消息队列机制,UI 刷新请求被放入 WM_PAINT 消息中排队处理。
- 控件不会立即重绘,而是等待操作系统调度 WM_PAINT 消息。
✅ 刷新流程:
- 调用 update() → 触发区域无效化。
- 系统将其合并到更新区域。
- 发送 WM_PAINT 消息给窗口过程。
- 调用 paintEvent() 完成实际绘制。
🧠 如何强制刷新?
widget->update(); // 推荐
widget->repaint(); // 强制同步刷新(慎用)
面试题18:父子窗口间的刷新管理?兄弟窗口间的刷新管理?如何让子窗口刷新,父窗口不刷新?
✅ 刷新机制:
- 子窗口刷新不会影响父窗口。
- 父窗口刷新会触发子窗口刷新(除非子窗口设置了 Qt::WA_OpaquePaintEvent 等优化标志)。
✅ 如何只刷新子窗口?
- 调用 childWidget->update()。
- 设置 Qt::WA_StaticContents 或 Qt::WA_NoSystemBackground 避免父窗口连带刷新。
✅ 如何避免父窗口刷新?
- 使用 QWidget::setUpdatesEnabled(false) 临时禁用刷新。
- 使用局部刷新(仅刷新特定区域)。
面试题19:兄弟窗口刷新重叠部分的流程和顺序?
✅ 流程:
- 检测两个窗口是否有重叠区域。
- 每个窗口独立收到 WM_PAINT 消息。
- Windows 会根据 Z-order(Z轴顺序)依次绘制窗口内容。
✅ 刷新顺序:
- Z-order 最高的窗口最后绘制,覆盖前面的窗口。
- 重叠区域由后绘制的窗口决定最终显示内容。
🧠 如何精确控制刷新顺序?
- 使用 raise() 提升窗口层级。
- 使用 QWidget::update(const QRect&) 刷新指定区域。
面试题20:Windows 下 UAC 安全机制
✅ UAC(User Account Control)的作用:
- 防止恶意程序以管理员权限运行。
- 用户操作时提示确认,增强系统安全性。
✅ 工作原理:
- 所有进程默认以标准用户权限运行。
- 需要管理员权限的操作会弹出 UAC 对话框。
- 成功授权后,系统创建新的“提升令牌”启动进程。
✅ 开发者注意事项:
- 需要管理员权限的程序应添加 requestedExecutionLevel 到 .exe.manifest 文件。
- 避免频繁请求 UAC 权限,影响用户体验。
面试题21:Windows 内存管理与共享内存实现
✅ 内存管理机制:
- 虚拟内存:每个进程拥有独立地址空间。
- 分页机制:将物理内存划分为页,按需加载。
- 内存保护:防止非法访问其他进程内存。
✅ 共享内存实现方式:
- 文件映射(File Mapping)
- 使用 CreateFileMapping 和 MapViewOfFile。
- 多进程可读写同一块内存区域。
- 共享内存段(Shared Memory Segment)
- 通过注册表或命名对象共享句柄。
✅ Qt 实现共享内存:
#include <QSharedMemory>
QSharedMemory shared("my_shared_memory");
if (!shared.create(1024)) {qDebug() << "Failed to create shared memory";
}
面试题22:如何分析 dump 文件?
✅ 方法一:使用 Visual Studio
- 打开 .dmp 文件 → 自动识别崩溃上下文。
- 查看调用栈、寄存器状态、异常信息。
✅ 方法二:使用 WinDbg(推荐)
- 安装 Windows SDK。
- 使用命令:
.symfix
.reload
!analyze -v
- 输出详细的崩溃原因、堆栈、函数名。
✅ 方法三:使用第三方工具
- ProcDump
- :生成 dump 文件。
- CDB
- / ADPlus:脚本化调试工具。
面试题:23:如何排查代码中的内存泄漏问题(线上)?
✅ 本地调试方法:
- 使用 Valgrind(Linux)、Visual Leak Detector(Windows)。
- 使用 AddressSanitizer(ASan)静态检测内存问题。
✅ 线上排查方法:
- 日志记录
- :记录内存分配释放情况。
- 工具辅助
- :
- CRT Debug Heap(Windows)
- :_CrtSetDbgFlag 启用内存泄漏检测。
- Valgrind + Dr.Memory
- :远程执行测试。
- Core Dump + 分析工具
- :分析崩溃时的内存状态。
✅ Qt 方案:
- 使用 qDebug() 打印对象生命周期。
- 使用 QObject 的父子关系自动管理内存。
面试题24:QString 和 string 的区别及实现方式
维度 | QString | std::string |
字符编码 | UTF-16(基于 QChar) | ASCII/UTF-8 |
所属库 | Qt | C++ STL |
内存管理 | Qt 自定义分配器 | STL 默认分配器 |
可变性 | 可变字符串 | 可变(C++11 后) |
线程安全 | Qt 管理 | 无内建线程安全 |
性能 | Qt 优化过 | STL 通用优化 |
面试题25:Qt 下如何使用多线程?
✅ 方法总结:
方法 | 描述 |
QThread | 创建线程对象,继承并重写 run() |
QRunnable + QThreadPool | 提交任务到线程池,复用线程 |
QtConcurrent::run() | 简单任务异步执行 |
QFuture + QFutureWatcher | 异步监控任务状态 |
moveToThread() | 将 QObject 移动到另一个线程,配合信号槽通信 |
✅ 示例:
class Worker : public QObject {Q_OBJECT
public slots:void doWork() {// 执行耗时操作}
};
Worker* worker = new Worker();
QThread* thread = new QThread(this);
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::finished, thread, &QThread::quit);
connect(worker, &Worker::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
面试题26:show()和 exec()的区别
方法 | 所属类 | 行为描述 |
show() | QWidget | 显示窗口,不阻塞主线程 |
exec() | QDialog/QApplication | 显示模态对话框或进入主事件循环,阻塞当前线程 |
✅ 示例:
QDialog dialog;
dialog.exec(); // 阻塞直到对话框关闭
QMainWindow window;
window.show(); // 不阻塞
面试题27:QObjectparent 的用处
✅ 主要作用:
- 自动内存管理
- :当 parent 被删除时,所有 child 对象也会被递归删除。
- 组织结构
- :便于管理和查找对象树。
- 父子关系传递属性
- :如 enabled、visible 状态。
✅ 示例:
QObject* parent = new QObject;
QObject* child = new QObject(parent); // child 自动加入 parent 的 children 列表
delete parent; // child 会被自动 delete
面试题28:class 前向声明和 include 的区别
特性 | 前向声明 | include |
作用 | 告诉编译器某个类存在 | 包含类定义(成员变量、函数) |
优点 | 减少编译依赖,加快编译速度 | 可以访问类成员 |
缺点 | 不能访问类成员 | 增加编译时间,可能导致循环依赖 |
✅ 使用建议:
- 在头文件中尽可能使用前向声明。
- 在源文件中包含完整头文件。
面试题29:多继承的问题
✅ 多继承的优点:
- 代码复用性强。
- 支持多种接口组合。
✅ 多继承的缺点:
- 菱形继承问题
- :多个基类继承同一个祖先,导致歧义。
- 命名冲突
- :同名函数/变量难以分辨。
- 维护成本高
- :继承层次复杂,不易理解。
✅ 解决方案:
- 使用 virtual 关键字解决菱形继承。
- 使用接口(纯虚类)替代具体类继承。
- 优先使用组合代替继承。
面试题30:int,long long占多少字节
1. int:
- 在32位和64位系统中,通常占4字节(32位),表示范围约为±21亿。
- 在嵌入式系统或特殊架构中可能为2字节(16位)。
2. long long:固定占8字节(64位),这是C++11标准明确规定的,范围极大(约±9.2×10¹⁸)。
在Qt开发中,qint64(Qt对long long的封装)同样保证8字节。
Part3三面(20道题)
面试题31: QGraphicsScene/ QGraphicsItem的填充模式,任意一个多边形的填充模式有哪些?
✅ 填充模式(Brush Style):
Qt 提供了多种 Qt::BrushStyle 枚举值用于控制图形项的填充方式:
枚举 | 描述 |
Qt::NoBrush | 无填充 |
Qt::SolidPattern | 实心填充 |
Qt::Dense1Pattern ~ Qt::Dense7Pattern | 不同密度的点阵图案 |
Qt::HorPattern | 横向条纹 |
Qt::VerPattern | 纵向条纹 |
Qt::CrossPattern | 十字交叉线 |
Qt::BDiagPattern | 向右斜线 |
Qt::FDiagPattern | 向左斜线 |
Qt::DiagCrossPattern | 斜线十字交叉 |
✅ 多边形填充模式设置示例:
QPolygonF polygon;
polygon << QPointF(0, 0) << QPointF(100, 0) << QPointF(50, 100);
QGraphicsPolygonItem* item = new QGraphicsPolygonItem(polygon);
item->setBrush(Qt::red); // 默认是 SolidPattern
🧠 自定义纹理填充:
可以使用 QBrush(QPixmap) 或 QBrush(QImage) 设置图像作为填充图案。
面试题32:QGraphicsScene的内存开销与刷新性能
✅ 内存开销因素:
- 场景中图形项的数量。
- 图形项类型(如 QGraphicsPixmapItem 占用大)。
- 是否启用了缓存(setCacheMode())。
- 是否启用了碰撞检测或物理模拟。
✅ 刷新性能优化:
- 使用 setViewportUpdateMode() 控制更新策略(如 BoundingRectViewportUpdate)。
- 使用 setOptimizationFlag() 启用优化(如 DontSavePainterState)。
- 避免频繁添加/删除大量图形项,应复用对象。
- 对静态内容启用缓存(ItemCoordinateCache)。
QGraphicsScene 在处理大量动态图形时性能下降明显,适合中等复杂度的 UI 和图形编辑器。
面试题33:设计模式中的备忘录模式,如何实现撤销和恢复?
✅ 备忘录模式结构:
- Originator(发起人)
- :创建状态快照并恢复。
- Memento(备忘录)
- :保存状态数据。
- Caretaker(管理者)
- :负责存储备忘录,不查看其内容。
✅ 示例代码(简化):
class Memento {
public:explicit Memento(const QString& state) : m_state(state) {}QString getState() const { return m_state; }
private:QString m_state;
};
class Originator {
public:void setState(const QString& state) { m_state = state; }Memento saveToMemento() { return Memento(m_state); }void restoreFromMemento(const Memento& m) { m_state = m.getState(); }
};
class Caretaker {
public:void add(const Memento& m) { m_history.push_back(m); }Memento get(int index) { return m_history[index]; }
private:std::vector<Memento> m_history;
};
面试题:34:备忘录结构体如何保证扩展性?新增内容怎么办?
✅ 扩展性设计建议:
- 使用 struct 或类封装状态信息。
- 使用 JSON、Protobuf 等格式序列化状态,便于未来版本兼容。
- 使用接口抽象状态管理,避免直接暴露内部结构。
✅ 新增字段不影响旧系统:
- 使用版本号字段。
- 使用可选字段(如 Protobuf 的 optional)。
- 序列化时忽略未知字段。
面试题35:撤销20次后想一步跳到第20步怎么做?
✅ 解决方案:
- 将所有历史步骤保存为列表(如 std::vector<Snapshot>)。
- 直接访问索引 20 的状态进行恢复。
✅ 示例:
std::vector<State> history;
// ...
originator.restore(history[20]); // 一步到位
🧠 优势:
- 时间复杂度 O(1),不需要迭代。
- 支持“跳转”、“重做”等功能。
面试题36:云计算建立的连接是长连接吗?在哪一层建立?
✅ 长连接判断标准:
- 如果连接持续时间长、数据交互频繁 → 长连接。
- 如 WebSocket、gRPC、MQTT → 都是长连接。
✅ 连接建立层级:
- 传输层(TCP/UDP)
- :建立基本通信通道。
- 应用层(HTTP/HTTPS/WebSocket)
- :在 TCP 上建立业务连接。
✅ 举例:
- HTTP 是短连接,默认每次请求新建连接。
- WebSockets 是长连接,在 TCP 上建立持久通信。
面试题37:服务器轮询策略怎么做的?
✅ 轮询策略分类:
类型 | 描述 |
Round Robin | 依次轮流分配 |
Least Connections | 分配给当前连接最少的服务器 |
IP Hash | 根据客户端 IP 哈希分配 |
Weighted RR | 加权轮询,按服务器性能分配 |
✅ 实现方式:
- Nginx、HAProxy 等反向代理配置。
- 自定义负载均衡模块(如 C++ 编写)。
面试题38:Protocol Buffers 的问题(常见)
✅ 常见问题及解决方案:
问题 | 解决方法 |
字段变更导致兼容性问题 | 使用 optional 字段,避免 required;保留编号 |
数据解析失败 | 检查 proto 版本是否一致 |
序列化效率低 | 使用 flatbuffers、capnproto 等替代方案 |
无法跨语言兼容 | 使用官方支持的语言插件生成代码 |
面试题39:对性能和质量如何考量?
✅ 性能指标:
- CPU 使用率
- 内存占用
- 启动时间
- 响应延迟
- 渲染帧率(UI 应用)
✅ 质量保障:
- 单元测试 + 集成测试覆盖率
- 静态分析(Clang-Tidy、Cppcheck)
- 动态检测(Valgrind、AddressSanitizer)
- 日志监控 + 异常捕获 + Core Dump
面试题40:CPU 并行化的方法(说一个熟悉的)
✅ OpenMP 示例(C++):
#include <omp.h>
#pragma omp parallel for
for (int i = 0; i < 1000; ++i) {// 并行执行的任务
}
✅ 优点:
- 简单易用,适合循环并行。
- 支持多核 CPU。
面试题41:静态扫描 vs 动态检测:C++ 内存动态检测怎么做?
✅ 动态检测工具:
- Valgrind(Linux)
- AddressSanitizer(ASan)
- Dr.Memory(Windows)
- Visual Studio CRT Debug Heap
✅ 使用 ASan 示例:
g++ -fsanitize=address -g your_code.cpp
./a.out
面试题42:如何判断一块内存是否正在使用还是泄漏?
✅ 方法:
- 使用内存检测工具(如 Valgrind、ASan)。
- 使用智能指针自动释放资源。
- 使用引用计数(如 shared_ptr)。
- 使用调试标记记录内存分配/释放。
struct MemHeader {void* stack[10]; // 分配调用栈size_t size; // 分配大小bool isActive; // 使用标志
};// 自定义分配器
void* operator new(size_t size) {void* p = malloc(size + sizeof(MemHeader));MemHeader* h = static_cast<MemHeader*>(p);captureStackTrace(h->stack); // 记录调用栈h->size = size;h->isActive = true;return static_cast<char*>(p) + sizeof(MemHeader);
}
面试题43:堆内存分配不足怎么办?
✅ 处理方式:
- 抛出 std::bad_alloc 异常。
- 返回 nullptr(new(std::nothrow))。
- 触发内存回收机制(GC)。
- 增加虚拟内存(Swap)。
void* allocateLargeBlock(size_t size) {void* p = malloc(size);if (!p) {// 尝试释放预留缓存releaseEmergencyPool();p = malloc(size);if (!p) throw std::bad_alloc();}return p;
}// 预分配应急内存
static char emergencyBuffer[10 * 1024 * 1024]; // 10MB
void releaseEmergencyPool() {free(emergencyBuffer); // 释放应急池
}
系统级策略:
- Linux Overcommit:/proc/sys/vm/overcommit_memory
- Windows:MEM_RESERVE + MEM_COMMIT
面试题44:静态扫描检测的具体方法
✅ 工具与流程:
- Clang-Tidy
- :检查代码风格和潜在 bug。
- Cppcheck
- :静态分析逻辑错误。
- Coverity、Klocwork
- :商业级静态分析工具。
- CI 中集成
- :提交代码前自动扫描。
面试题45:工程效率方面 ,build system用的哪一块,qmake还是cmake.你们三方库管理,仓库管理,开发的模式,是怎么做的
✅ Build System:
- qmake
- :适用于小型项目,简单快捷。
- CMake
- :现代项目首选,跨平台,灵活强大。
✅ 三方库管理:
- vcpkg
- (微软推荐)
- Conan
- FetchContent(CMake 内置)
- Git Submodule / External Project
✅ 开发模式:
- Git Flow
- Feature Branch
- Code Review(GitHub/GitLab PR)
面试题46:算法工程师打包好动态库,是直接拖入你们的仓库里吗,你们是如何管理的
- 使用 Conan / vcpkg 管理依赖。
- 自建私有仓库(Artifactory、Nexus)。
- CI 流程中自动构建、上传、签名。
- 版本化命名(如 libalgo_v1.2.dll)。
面试题47:有没有自动化的过程,工程效率相关的
- CI/CD 流程(Jenkins、GitLab CI、GitHub Actions)
- 代码格式化(clang-format、prettier)
- 静态分析(Cppcheck、SonarQube)
- 文档自动生成(Doxygen)
- 单元测试自动运行
面试题48:有CI/CD的部署吗,持续集成Continuous Integration(CI)和持续交付Continuous Delivery(CD)
CI(持续集成)
-
- 代码提交触发构建
- 自动运行单元测试
- 生成构建产物(exe/dll)
CD(持续交付)
-
- 自动部署到测试环境
- 审核后发布到生产环境
✅ 工具:
- Jenkins
- GitHub Actions
- GitLab CI
- Azure DevOps
面试题49:sever的上线部署的流程是怎么样的
型上线流程:
- 开发完成 → 提交代码 → Pull Request
- CI 构建 → 单元测试通过 → 生成 release 包
- 测试环境部署 → UAT 测试
- 灰度发布 → 少量用户验证
- 全量上线 → 正式环境部署
- 日志监控 + 崩溃上报 + APM 监控
面试题50:一棵二叉树,节点都为正数,找出路径和等于 SUM 的所有路径
✅ 解法思路:
使用 DFS(深度优先搜索),递归遍历每个节点,并记录当前路径总和。
✅ 示例代码(C++):
struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:std::vector<std::vector<int>> pathSum(TreeNode* root, int sum) {std::vector<std::vector<int>> result;std::vector<int> path;dfs(root, sum, path, result);return result;}
private:void dfs(TreeNode* node, int remaining, std::vector<int>& path, std::vector<std::vector<int>>& result) {if (!node) return;path.push_back(node->val);if (!node->left && !node->right && remaining == node->val) {result.push_back(path);}dfs(node->left, remaining - node->val, path, result);dfs(node->right, remaining - node->val, path, result);path.pop_back();}
}
点击下方关注【Linux教程】,获取编程学习路线、项目教程、简历模板、大厂面试题、大厂面经、编程交流圈子等等。