
1) 【一句话结论】
std::shared_ptr通过引用计数管理对象生命周期,循环引用会导致内存泄漏;自定义内存池通过预分配大块内存并管理空闲链表,减少频繁malloc/free的性能开销,适用于视频帧等高频临时对象。
2) 【原理/概念讲解】
std::shared_ptr的引用计数机制是核心:创建时,对象计数器初始为1。
3) 【对比与适用场景】
| 特性/场景 | std::shared_ptr | 自定义内存池 |
|---|---|---|
| 核心机制 | 引用计数(管理生命周期) | 直接管理内存块(预分配+链表) |
| 循环引用 | 会导致内存泄漏 | 无引用计数,不会因循环引用泄漏 |
| 内存分配 | 默认new/delete(或自定义分配器) | 预分配大块内存(如mmap),切分固定大小缓冲区 |
| 适用场景 | 对象需多部分共享,所有权转移(如资源管理) | 高频分配/释放的临时缓冲区(如视频帧、网络包) |
| 注意点 | 循环引用需用weak_ptr | 预分配大小需工程计算(如帧率、缓冲区大小、并发数),多线程需加锁(可优化锁粒度) |
4) 【示例】
循环引用及解决:
class Node {
public:
std::weak_ptr<Node> other; // 持有weak_ptr
~Node() { std::cout << "Node destroyed\n"; }
};
int main() {
auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();
a->other = b;
b->other = a;
if (a->other.lock()) { // 转换为shared_ptr
std::cout << "other still alive\n";
}
}
内存池伪代码(考虑预分配计算与动态调整):
class VideoFramePool {
private:
size_t buffer_size;
size_t pool_size;
std::vector<char*> buffers;
std::list<char*> free_list;
std::mutex mtx;
public:
VideoFramePool(size_t size, size_t fps, size_t concurrency) {
buffer_size = size;
pool_size = fps * concurrency * 2; // 预估最大并发数
buffers.resize(pool_size);
for (size_t i = 0; i < pool_size; ++i) {
buffers[i] = new char[buffer_size];
free_list.push_front(buffers[i]);
}
}
char* allocate() {
std::lock_guard<std::mutex> lock(mtx);
if (free_list.empty()) return nullptr;
char* buf = free_list.front();
free_list.pop_front();
return buf;
}
void release(char* buf) {
std::lock_guard<std::mutex> lock(mtx);
free_list.push_front(buf);
}
~VideoFramePool() {
for (char* b : buffers) delete[] b;
}
};
5) 【面试口播版答案】
(约90秒)
“std::shared_ptr通过引用计数管理对象生命周期,当引用数减为0时释放。循环引用时,多个shared_ptr互相持有,计数永远不会归零,导致内存泄漏。解决循环引用可以用weak_ptr,比如一个shared_ptr持有另一个的weak_ptr,当需要访问时再转换。对于视频帧等高频分配释放的缓冲区,可以设计内存池:预分配大块内存,切分为固定大小的缓冲区,用链表管理空闲状态。分配时从空闲链表取,释放时放回。预分配大小根据帧率(如30fps)、缓冲区大小(如1KB)和并发数(如10个线程)计算,比如30102≈600个缓冲区,减少malloc/free的性能开销。多线程环境下用互斥锁保护链表操作,避免数据竞争。”
6) 【追问清单】
7) 【常见坑/雷区】