美团 C++开发 一面

1. 自我介绍

2. 介绍一下项目

3. 说一下智能指针,追问用法以及使用场景

答案:智能指针的核心价值是把资源释放时机和对象生命周期绑定起来,减少手动 new/delete 带来的泄漏、悬挂指针和异常路径遗漏问题。C++ 里最常见的是这几个:

  • std::unique_ptr:独占所有权,不能拷贝,可以移动
  • std::shared_ptr:共享所有权,靠引用计数管理
  • std::weak_ptr:弱引用,不增加引用计数,通常用来观察 shared_ptr
  • std::auto_ptr:已经废弃,不建议再提

使用场景上,一般这样区分:

  • 明确唯一拥有关系时,优先用 unique_ptr
  • 多个对象确实要共享同一资源时,用 shared_ptr
  • 需要打破循环引用,或者只是临时观察对象存活状态时,用 weak_ptr

工程上比较好的习惯是:

  • 能不用 shared_ptr 就尽量不用
  • 不要把所有指针都机械地替换成智能指针
  • 智能指针管理的是所有权,不是访问权限

代码:

#include <memory>

class Widget {};

int main() {
    std::unique_ptr<Widget> p1 = std::make_unique<Widget>();

    std::shared_ptr<Widget> p2 = std::make_shared<Widget>();
    std::weak_ptr<Widget> wp = p2;

    if (auto sp = wp.lock()) {
        // 对象还活着,可以安全使用
    }
}

4. 如果 shared_ptr 发生循环引用怎么办?enable_shared_from_this 又是解决什么问题的?

答案:这是智能指针里非常高频的追问。shared_ptr 的引用计数机制只能处理“没有环”的所有权结构,一旦两个对象互相持有 shared_ptr,它们的引用计数都不会归零,对象就不会释放。

典型解决方案是:

  • 其中一边改成 weak_ptr
  • 明确谁拥有谁,谁只是观察者
  • 不要为了图省事把成员全写成 shared_ptr

enable_shared_from_this 解决的是另一个常见问题:如果一个对象本身已经被 shared_ptr 管理,但你在对象内部又想安全地拿到“指向自己的 shared_ptr”,不能直接 shared_ptr<this>,否则会构造出新的控制块,最终可能 double free。正确方式是继承 std::enable_shared_from_this<T>,然后通过 shared_from_this() 拿到和外部共享同一控制块的智能指针。

代码:

#include <memory>

class B;

class A {
public:
    std::shared_ptr<B> b;
};

class B {
public:
    std::weak_ptr<A> a; // 用 weak_ptr 打破环
};

class Session : public std::enable_shared_from_this<Session> {
public:
    std::shared_ptr<Session> getSelf() {
        return shared_from_this();
    }
};

5. 说一下 C++ 中的容器,追问底层实现以及容器间的对比

答案:这题不要只是背 STL 名字,重点是讲容器分类、底层结构和选型理由。常见容器大概可以分成几类:

  • 顺序容器:vectordequelist
  • 关联容器:mapset
  • 无序关联容器:unordered_mapunordered_set
  • 适配器:stackqueuepriority_queue

底层实现上常见结论是:

  • vector:动态连续数组
  • deque:分段连续存储
  • list:双向链表
  • map/set:红黑树
  • unordered_map/set:哈希表

比较时一般从这几个角度讲:

  • 是否连续存储
  • 是否支持随机访问
  • 插入删除复杂度
  • 查找复杂度
  • 迭代器稳定性
  • 内存局部性

工程上最常见的经验其实是:如果没有非常明确的理由,优先考虑 vectorunordered_map,因为它们在大多数真实业务里更符合性能直觉。

6. 迭代器失效是怎么回事?不同容器为什么表现不一样?

答案:这题很适合接在容器后面,因为它比单纯问容器分类更能看出你是不是真的用过 STL。迭代器失效,本质上是容器内部结构发生变化后,原来持有的位置、地址或节点关系已经不再可靠

不同容器表现不一样,核心原因在于底层结构不同:

  • vector 扩容后可能整块搬迁,所以原来的迭代器、指针、引用都可能失效
  • deque 因为是分段存储,插入删除后规则比 vector 更复杂
  • list 节点独立分配,插入删除通常只影响被删节点本身,其他迭代器更稳定
  • unordered_map rehash 后,桶结构变化,迭代器通常会失效

代码:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {1, 2, 3};
    auto it = v.begin();

    v.push_back(4); // 可能触发扩容
    // it 这里可能已经失效
}

7. lambda 表达式

答案:Lambda 本质上是一个匿名函数对象,编译器会为它生成一个闭包类型。它最常见的价值是让一些“就地定义、短小逻辑”的代码更简洁,特别适合:

  • 回调
  • 算法配合
  • 局部策略封装
  • Qt 或异步框架里的短逻辑处理

一个 lambda 主要包括几部分:

  • 捕获列表
  • 参数列表
  • 返回值
  • 函数体

最值得讲的点通常是捕获方式:

  • 值捕获:复制一份外部变量
  • 引用捕获:直接引用外部变量
  • [=][&]:默认捕获
  • mutable:允许修改值捕获的副本

高分点一般在于补一句:lambda 不是“只是语法糖”,它背后是一个有 operator() 的对象,所以能和模板、泛型算法、函数对象体系很好地结合。

代码:

#include <iostream>

int main() {
    int x = 10;
    auto f = [x](int y) { return x + y; };
    std::cout << f(5) << std::endl;
}

8. lambda 和 std::function 的关系是什么?std::function 为什么有额外开销?

答案:这题放在 lambda 后面很合适,因为很多人只会写 lambda,但说不清它和 std::function 的关系。lambda 本质上是一个具体类型的函数对象,而 std::function 是一个通用可调用对象包装器,它可以装:

  • 普通函数
  • lambda
  • 仿函数
  • bind 的结果
  • 成员函数适配后的对象

std::function 的优势是统一接口、使用方便,但代价通常也更高,原因主要有:

  • 需要做类型擦除
  • 可能发生额外的对象封装
  • 某些情况下可能触发堆分配
  • 内联优化机会可能变差

所以如果你在高频调用、性能敏感路径里,通常更推荐:

  • 直接用模板接收可调用对象
  • 或者直接保存具体 lambda 类型
  • 只有在确实需要统一抽象时再用 std::function

代码:

#include <functional>
#include <iostream>

void run(const std::function<int(int)>& f) {
    std::cout << f(10) << std::endl;
}

int main() {
    int base = 5;
    

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论

相关推荐

03-06 17:03
北京大学 C++
1.C++11&nbsp;的新特性有哪些1.1说下智能指针1.1.1shared_ptr&nbsp;它是线程安全的吗1.2nullptr&nbsp;有什么作用,以前有&nbsp;NULL,为什么要加一个&nbsp;nullptr2.const&nbsp;和&nbsp;static&nbsp;的作用2.1还有哪些其他作用,如修饰函数,修饰类里面的函数、类里面的变量。2.2声明类的时候加一个&nbsp;const,能调用它里面的成员函数吗3.C++&nbsp;有哪些类型转换,如&nbsp;static_cast&nbsp;之类的3.1reinterpret_cast&nbsp;的作用是什么3.2了解过&nbsp;dynamic_cast&nbsp;吗4.C++&nbsp;有哪些&nbsp;STL&nbsp;容器4.1vector&nbsp;和&nbsp;list&nbsp;有什么区别5.C++&nbsp;一些网络你知道吗,epoll&nbsp;你用过吗6.你用&nbsp;Linux&nbsp;多吗,看一些网络、磁盘&nbsp;IO、CPU&nbsp;性能的工具了解吗6.1那网络流量,一个节点到另一个节点的占比的工具用过吗7.进程和线程有什么区别7.1进程之间通信有哪些方式7.1.1信号量了解吗7.2线程之间有哪些通信方式7.2.1锁了解过吗,条件变量了解过吗8.TCP&nbsp;和&nbsp;UDP&nbsp;的区别8.1TCP&nbsp;和&nbsp;UDP&nbsp;都发送若干个网络包,都能确保完整收到这些包吗,收到的顺序对不对9.git&nbsp;用的是命令还是&nbsp;GUI9.1写了一些代码还没有存进去,但是另一个人提了一个版本要合进来,该怎么操作9.2git&nbsp;stash&nbsp;有什么作用10.MySQL&nbsp;的事务隔离级别有哪些11.怎么知道哪些线程占用的&nbsp;CPU&nbsp;比较多12.用过哪些&nbsp;AI&nbsp;编程工具12.1改变量的名字的时候用的惯吗13.LeetCode128&nbsp;最长连续序列吓哭了,沉浸式拷打,自己掌握的还是太薄弱了,面试官人好实力又强,我菜菜😭😭😭
查看30道真题和解析
点赞 评论 收藏
分享
评论
1
8
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务