字节跳动 C++ Qt PC客户端面试,总共三轮技术面(一面10道、二面20道、三面20道)
为了帮助更多的同学拿到满意的 offer,我把一二三面共50道面试题整理发布出来~供大家学习参考~
很多同学认为,面试问理论知识,就是八股文,实际工作中没有太大用处。但事实上,所谓的“八股文”,本质是经过提炼和标准化的专业术语和通用认知体系,它是我们高效沟通、协作和解决问题的基础。
换句话说,掌握并能准确使用这些“八股文”,不是为了应付面试,而是为了让我们在真实的工作场景中,更高效地表达、理解和落地复杂的技术问题。
话不多说,直接进入正题(更多八股文面试题,关注公钟呺[Linux教程]发送“面试”获取):
✅ 方法一:全局变量初始化
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前构造
class MyClass {
public:
void selfDestruct() {
delete this;
}
};
MyClass* obj = new MyClass();
obj->selfDestruct(); // 正确
// obj 已经无效,不能再访问
MyClass obj;
obj.selfDestruct(); // 错误!栈上对象不能 delete
#include
#include
#include
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();
}
技术 |
描述 |
QThread |
最底层的线程类,需要手动管理线程生命周期 |
QtConcurrent::run() |
简化版线程池任务提交,自动回收线程 |
QtConcurrent::map() / filter() / mappedReduced() |
并行处理容器元素 |
QRunnable + QThreadPool |
提交任务给线程池执行 |
QFuture + QFutureWatcher |
异步任务监控 |
智能指针类型 |
行为描述 |
std::unique_ptr |
独占所有权,不可复制,可移动 |
std::shared_ptr |
共享所有权,引用计数自动管理 |
std::weak_ptr |
观察 shared_ptr,不增加引用计数,防止循环引用 |
在Qt框架中还扩展了QScopedPointer和QSharedPointer,它们功能类似标准库的unique_ptr和shared_ptr,但能和Qt的对象系统更好集成。这些智能指针的核心价值是自动管理内存,避免内存泄漏,让资源管理更安全高效。
✅ 方法一:使用 Qt 自带模块 QNetworkAccessManager
#include
#include
#include
#include
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();
}
维度 |
QWidget |
QML |
技术本质 |
基于 C++ 的传统 UI 框架 |
基于声明式语言(类似 JavaScript)的 UI 框架 |
渲染机制 |
QPainter 直接绘制控件 |
场景图(Scene Graph)+ OpenGL 渲染 |
开发效率 |
C++ 编码为主,UI 设计较繁琐 |
QML 可视化布局,热重载方便 |
性能 |
CPU 渲染,适合桌面传统界面 |
GPU 加速渲染,适合动画和现代 UI |
适用场景 |
传统桌面软件、工具类应用 |
移动端适配、动画丰富、UI 动态变化频繁的应用 |
跨平台 |
支持良好 |
支持良好,尤其适合移动端 |
参数 |
类型 |
说明 |
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);
特性 |
CPU 渲染 |
GPU 渲染 |
运算核心 |
单核/多核通用处理器 |
大量并行计算单元 |
擅长任务 |
复杂逻辑控制、小数据处理 |
大规模并行计算(如图形像素处理) |
内存访问 |
使用系统内存 |
使用显存(VRAM) |
延迟 |
较高延迟 |
极低延迟,适合实时渲染 |
能耗 |
高功耗(复杂运算) |
更高效能比 |
典型用途 |
文本编辑器、非图形应用 |
游戏引擎、视频播放、动画 UI |
集成显卡在编码阶段可以走硬编,但需要开发者主动选择合适的 API 和库。
维度 |
硬编/软编 |
GPU/CPU 渲染 |
层级 |
视频编码层 |
图形绘制层 |
功能 |
将视频帧压缩为 H.264/H.265 格式 |
在屏幕上绘制像素 |
硬件依赖 |
显卡编码器支持 |
显卡渲染管线支持 |
应用场景 |
视频录制、直播推流 |
UI 绘制、游戏画面显示 |
关联性:
强制 Qt 应用程序使用 OpenGL ES(嵌入式系统使用的轻量级 OpenGL)而不是桌面版 OpenGL。
QApplication::setAttribute(Qt::AA_UseOpenGLES);
QApplication app(argc, argv);
⚠️ 注意:不是所有平台都支持 OpenGL ES(如某些 Windows 平台需安装 ANGLE)。
渲染机制 |
QWidget |
QML |
底层技术 |
QPainter + 软件光栅化 |
Scene Graph + GPU加速 |
更新流程 |
脏矩形标记 → 逐层重绘 |
场景图差异 → 批量提交GPU |
透明度处理 |
逐层混合(性能差) |
Shader一次合成(性能优) |
动画性能 |
依赖定时器+重绘(CPU 渲染,性能有限) |
属性绑定+GPU插值(GPU 加速,适合动画和复杂 UI) |
抗锯齿 |
慢(CPU计算) |
快(MSAA/FXAA) |
典型帧率 |
30-60 FPS(复杂UI卡顿) |
60+ FPS(流畅) |
而且从界面表现来看,Qt 程序在 Win11 上也能很好地适配,比如支持圆角窗口、深色模式、DPI 缩放优化等现代 UI 特性。只要注意一些系统级别的适配(比如高分辨率支持、任务栏集成),基本上写一次代码就可以直接运行。”
如果项目有特殊需求,比如想利用 Win11 的一些新特性(如 Direct3D 渲染、触控支持、通知机制等),也可以通过调用 Windows SDK 接口来扩展功能,Qt 提供了良好的底层支持能力。
✅ 异步刷新机制:
widget->update(); // 推荐
widget->repaint(); // 强制同步刷新(慎用)
✅ 流程:
#include
QSharedMemory shared("my_shared_memory");
if (!shared.create(1024)) {
qDebug() << "Failed to create shared memory";
}
.symfix
.reload
!analyze -v
维度 |
QString |
std::string |
字符编码 |
UTF-16(基于 QChar) |
ASCII/UTF-8 |
所属库 |
Qt |
C++ STL |
内存管理 |
Qt 自定义分配器 |
STL 默认分配器 |
可变性 |
可变字符串 |
可变(C++11 后) |
线程安全 |
Qt 管理 |
无内建线程安全 |
性能 |
Qt 优化过 |
STL 通用优化 |
✅ 方法总结:
方法 |
描述 |
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();
方法 |
所属类 |
行为描述 |
show() |
QWidget |
显示窗口,不阻塞主线程 |
exec() |
QDialog/QApplication |
显示模态对话框或进入主事件循环,阻塞当前线程 |
✅ 示例:
QDialog dialog;
dialog.exec(); // 阻塞直到对话框关闭
QMainWindow window;
window.show(); // 不阻塞
QObject* parent = new QObject;
QObject* child = new QObject(parent); // child 自动加入 parent 的 children 列表
delete parent; // child 会被自动 delete
特性 |
前向声明 |
include |
作用 |
告诉编译器某个类存在 |
包含类定义(成员变量、函数) |
优点 |
减少编译依赖,加快编译速度 |
可以访问类成员 |
缺点 |
不能访问类成员 |
增加编译时间,可能导致循环依赖 |
1. int:
2. long long:固定占8字节(64位),这是C++11标准明确规定的,范围极大(约±9.2×10¹⁸)。
在Qt开发中,qint64(Qt对long long的封装)同样保证8字节。
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) 设置图像作为填充图案。
QGraphicsScene 在处理大量动态图形时性能下降明显,适合中等复杂度的 UI 和图形编辑器。
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 m_history;
};
std::vector history;
// ...
originator.restore(history[20]); // 一步到位
类型 |
描述 |
Round Robin |
依次轮流分配 |
Least Connections |
分配给当前连接最少的服务器 |
IP Hash |
根据客户端 IP 哈希分配 |
Weighted RR |
加权轮询,按服务器性能分配 |
问题 |
解决方法 |
字段变更导致兼容性问题 |
使用 optional 字段,避免 required;保留编号 |
数据解析失败 |
检查 proto 版本是否一致 |
序列化效率低 |
使用 flatbuffers、capnproto 等替代方案 |
无法跨语言兼容 |
使用官方支持的语言插件生成代码 |
✅ 性能指标:
#include
#pragma omp parallel for
for (int i = 0; i < 1000; ++i) {
// 并行执行的任务
}
g++ -fsanitize=address -g your_code.cpp
./a.out
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(p);
captureStackTrace(h->stack); // 记录调用栈
h->size = size;
h->isActive = true;
return static_cast(p) + sizeof(MemHeader);
}
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); // 释放应急池
}
系统级策略:
✅ Build System:
CI(持续集成)
CD(持续交付)
使用 DFS(深度优先搜索),递归遍历每个节点,并记录当前路径总和。
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
std::vector> pathSum(TreeNode* root, int sum) {
std::vector> result;
std::vector path;
dfs(root, sum, path, result);
return result;
}
private:
void dfs(TreeNode* node, int remaining, std::vector& path, std::vector>& 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教程】,获取编程学习路线、项目教程、简历模板、大厂面试题、大厂面经、编程交流圈子等等。