横乐医疗科技 QT开发实习 一面
1. C++11 之后你常用的新特性有哪些,分别解决了什么问题
答案:比较常用的有 auto、范围 for、右值引用、移动语义、nullptr、lambda、thread、mutex、condition_variable、atomic、smart pointer、constexpr、optional、variant、结构化绑定这些。如果放到工程里看,真正高频的还是几类:一类是语法层降噪,比如 auto、lambda、结构化绑定;一类是资源管理和性能相关,比如移动语义、智能指针、完美转发;一类是并发库和原子操作;再往后像 constexpr、filesystem、string_view 这种更多是为了把运行期开销往编译期和轻量视图方向挪。面试里这题一般不只是问你背特性名,而是看你能不能说清楚它们在真实代码里替代了什么旧写法,解决了什么问题。
代码:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<string> v = {"im", "client", "qt"};
for (const auto& s : v) {
cout << s << endl;
}
auto add = [](int a, int b) { return a + b; };
cout << add(1, 2) << endl;
return 0;
}
2. 智能指针的原理是什么
答案:智能指针本质上是把资源所有权和对象生命周期包装进一个类里,通过构造、析构和运算符重载,让“什么时候释放资源”这件事自动发生。unique_ptr 本质是独占所有权,析构时直接释放;shared_ptr 会额外维护一个控制块,里面一般有强引用计数、弱引用计数、删除器和分配器;weak_ptr 不参与对象拥有,只观察控制块。工程上最关键的不是会不会背控制块结构,而是能不能把所有权关系设计清楚。智能指针只是表达工具,不是无脑替代裸指针的万能药。
3. 智能指针类型分别适用于什么场景
答案:unique_ptr 适合所有权非常明确的对象,比如某个模块私有持有的资源、工厂返回独占对象、RAII 封装句柄这类场景。shared_ptr 适合多个模块共同依赖同一个对象,而且对象释放时机很难由某一方单独决定的情况,比如异步回调链、资源缓存、对象跨线程投递。weak_ptr 主要用于观察对象是否还活着,或者解决循环引用。如果一个对象理论上只有一个主人,就不要为了图省事直接上 shared_ptr,这类代码后面通常都会变得难排查。
代码:
#include <iostream>
#include <memory>
using namespace std;
struct Task {
~Task() { cout << "~Task\n"; }
};
int main() {
unique_ptr<Task> p1 = make_unique<Task>();
auto p2 = make_shared<Task>();
weak_ptr<Task> wp = p2;
if (auto sp = wp.lock()) {
cout << "object still alive\n";
}
return 0;
}
4. shared_ptr 的引用计数为什么通常是线程安全的,但对象本身不一定线程安全
答案:因为 shared_ptr 内部控制块的计数增减通常是通过原子操作完成的,所以多个线程拷贝、销毁同一个 shared_ptr,引用计数这一层一般不会乱。但它只保证“这个对象什么时候能被安全释放”,并不保证“对象里的成员并发访问是安全的”。比如多个线程都拿着同一个 shared_ptr<MyConn> 去改 MyConn 里的缓存、状态机、发送队列,如果没有额外同步,照样会出现数据竞争。所以很多人说“shared_ptr 是线程安全的”,这句话只说对了一半。
5. 信号槽机制底层是怎么工作的
答案:Qt 的信号槽本质上依赖 MOC 生成的元对象系统。类里声明了 Q_OBJECT 之后,MOC 会为它生成元信息,包括信号、槽、属性、类型信息等。connect 之后,信号发射时会通过元对象系统找到对应连接,再根据连接类型决定是直接调用还是投递事件。同线程下如果是 DirectConnection,槽函数会直接执行;跨线程一般会转成队列连接,把调用封装成事件投递到目标线程的事件循环里。所以信号槽看上去像语法糖,实际上背后是元对象、事件循环和线程亲和性一起配合的。
6. Qt 跨线程发信号时,槽函数到底在哪个线程执行
答案:关键不在于信号从哪个线程发出来,而在于接收对象属于哪个线程,以及连接类型是什么。默认 AutoConnection 下,如果发送者和接收者在同一线程,会直接调用;如果不在同一线程,就会走队列连接,槽函数在接收对象所属线程执行。这也是为什么 Qt 里经常强调对象线程亲和性。很多跨线程崩溃不是信号槽本身的问题,而是对象被 move 到别的线程之后,开发者对执行线程判断错了。
代码:
connect(worker, &Worker::dataReady,
receiver, &Receiver::handleData,
Qt::QueuedConnection);
7. 如果会话列表特别多,你会怎么做加载和渲染优化
答案:这类问题核心不在“怎么一次性查出来”,而在于避免首屏和滚动过程把 UI 压死。一般会做分页拉取、懒加载、可视区域渲染、数据分层缓存、头像和未读数异步刷新,避免每次列表变化都整表重绘。如果是 Qt Widgets,通常会尽量减少复杂 paintEvent里的对象创建和文本布局;如果是 Model/View 架构,会把刷新粒度控制到行级甚至角色级;如果是 QML,还要注意绑定过多和 delegate 过重带来的抖动。真正麻烦的点通常不是数据量本身,而是“频繁更新 + 富文本样式 + 图
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.
查看13道真题和解析