拼多多-客户端开发(C++) 二面 面经
1. 介绍一下你做过的最有挑战性的项目,遇到了什么技术难点,如何解决的?
答案要点:
- 项目背景:业务场景、技术栈、团队规模
- 核心挑战:性能瓶颈、技术难题、业务复杂度
- 解决方案:技术选型、架构设计、优化策略
- 量化成果:性能提升百分比、用户体验改善、代码质量
- 个人成长:学到的技术、思维方式的转变
2. 如何设计一个高性能的图片加载框架?需要考虑哪些关键问题?
答案:
- 核心功能: 三级缓存:内存缓存(LRU)、磁盘缓存、网络加载图片解码:异步解码,避免阻塞主线程尺寸适配:根据ImageView大小加载合适分辨率生命周期管理:Activity/Fragment销毁时取消请求
- 性能优化: 线程池管理:网络线程池、解码线程池分离内存优化:Bitmap复用池、inSampleSize降采样流量优化:WebP格式、渐进式加载预加载:预测用户行为,提前加载
- 容错处理: 占位图、错误图、重试机制OOM保护:内存不足时清理缓存
- 参考实现:Glide、Picasso、Fresco的设计思路
3. 客户端如何实现秒杀场景的高并发请求?如何防止接口被刷?
答案:
- 客户端优化: 请求合并:短时间内多次点击只发一次请求本地限流:防抖、节流,限制请求频率按钮置灰:请求中禁用按钮,防止重复提交倒计时精确控制:提前获取服务器时间,本地倒计时
- 接口防刷: Token机制:页面加载时获取一次性token签名验证:请求参数+时间戳+密钥生成签名设备指纹:收集设备信息,识别异常设备验证码:高风险操作增加验证码
- 降级策略: 排队机制:超过阈值进入等待队列限流提示:友好提示用户稍后重试本地模拟:先展示成功,后台异步确认
4. 手写代码:实现一个线程安全的单例模式(要求懒加载、高性能)
答案:
// 方案1:C++11 局部静态变量(推荐)
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // C++11保证线程安全
return instance;
}
// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() {} // 私有构造函数
~Singleton() {}
};
// 方案2:双重检查锁定(DCLP)
class Singleton {
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 第一次检查
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) { // 第二次检查
instance = new Singleton();
}
}
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() {}
~Singleton() {}
static std::atomic<Singleton*> instance;
static std::mutex mtx;
};
std::atomic<Singleton*> Singleton::instance{nullptr};
std::mutex Singleton::mtx;
5. TCP的拥塞控制算法有哪些?分别在什么场景下触发?
答案:
- 慢启动(Slow Start): 初始cwnd=1,每个RTT翻倍增长达到ssthresh后进入拥塞避免场景:连接建立初期、超时重传后
- 拥塞避免(Congestion Avoidance): cwnd线性增长,每个RTT增加1探测网络容量上限
- 快速重传(Fast Retransmit): 收到3个重复ACK立即重传不等超时,减少延迟
- 快速恢复(Fast Recovery): 快速重传后,ssthresh=cwnd/2,cwnd=ssthresh+3避免慢启动,快速恢复吞吐量
- 改进算法: BBR:基于带宽和RTT,而非丢包CUBIC:适合高带宽长延迟网络
6. 如何排查客户端的内存泄漏问题?有哪些工具和方法?
答案:
- 工具: Valgrind:检测内存泄漏、越界访问AddressSanitizer(ASan):编译时插桩,运行时检测Instruments(iOS):Leaks工具检测泄漏Android Profiler:内存分析、堆转储
- 排查方法: 复现场景:找到稳定复现路径内存曲线:观察内存持续增长堆快照对比:前后对比,找出未释放对象引用链分析:查看对象被谁持有
- 常见原因: 循环引用:智能指针相互持有单例持有:单例持有Activity/Fragment引用监听器未移除:注册后忘记反注册静态变量持有:生命周期过长Native内存泄漏:JNI层未释放
7. 客户端如何实现离线缓存?如何保证数据一致性?
答案:
- 缓存策略: 网络优先:先请求网络,失败读缓存缓存优先:先读缓存,后台更新仅网络:实时数据不缓存仅缓存:静态资源
- 存储方案: SharedPreferences/UserDefaults:小量配置SQLite:结构化数据文件缓存:图片、视频等大文件MMKV:高性能KV存储
- 数据一致性: 版本号机制:每次更新递增版本号时间戳:记录缓存时间,过期失效ETag/Last-Modified:HTTP缓存头增量更新:只更新变化的数据冲突解决:服务器时间戳优先
- 缓存淘汰: LRU:最近最少使用大小限制:超过阈值清理时间过期:定期清理过期数据
8. 手写代码:实现一个生产者-消费者模型(使用条件变量)
答案:
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
template<typename T>
class BlockingQueue {
private:
std::queue<T> queue;
std::mutex mtx;
std::condition_variable notEmpty;
std::condition_variable notFull;
size_t capacity;
public:
BlockingQueue(size_t cap) : capacity(cap) {}
void push(const T& item) {
std::unique_lock<std::mutex> lock(mtx);
// 队列
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++八股文全集 文章被收录于专栏
本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。