美团移动端 C++开发 二面 面经

美团移动端二面(60分钟)

1. 说说进程和线程的区别,以及线程间通信的方式有哪些?

参考答案:

进程是资源分配的基本单位,线程是CPU调度的基本单位。

  1. 资源开销:进程拥有独立的地址空间、代码段、数据段、堆栈,创建和切换开销大,线程共享进程的地址空间和资源,只有独立的栈和寄存器,创建和切换开销小
  2. 通信方式:进程间通信需要IPC机制如管道、消息队列、共享内存、信号量,线程间通信更简单可以直接访问共享变量
  3. 安全性:进程间相互独立一个进程崩溃不影响其他进程,线程共享地址空间一个线程崩溃可能导致整个进程崩溃
  4. 并发性:多进程可以在多核CPU上真正并行执行,多线程也可以并行但需要注意数据竞争和同步问题

线程间通信方式:

  1. 共享内存:多个线程直接访问同一块内存区域,需要用互斥锁或信号量保护临界区,效率最高但需要处理同步问题
  2. 互斥锁(Mutex):保护共享资源,同一时刻只有一个线程可以访问临界区,防止数据竞争
  3. 条件变量(Condition Variable):配合互斥锁使用,线程可以等待某个条件满足,其他线程通过notify唤醒等待的线程
  4. 信号量(Semaphore):控制对共享资源的访问数量,可以实现生产者消费者模型
  5. 消息队列:线程通过队列传递消息,发送线程将消息放入队列,接收线程从队列取出消息
  6. 原子操作:使用atomic类型进行无锁编程,适用于简单的计数器或标志位

2. 什么是死锁?死锁的四个必要条件是什么?如何避免死锁?

参考答案:

死锁是指两个或多个线程互相持有对方需要的资源,导致所有线程都无法继续执行的状态。

死锁的四个必要条件:

  1. 互斥条件:资源不能被多个线程同时使用,一个线程占用资源时其他线程必须等待
  2. 请求与保持条件:线程已经持有至少一个资源,同时又请求其他线程持有的资源
  3. 不可剥夺条件:线程已获得的资源在使用完之前不能被强制剥夺,只能由线程自己释放
  4. 循环等待条件:存在一个线程资源的循环等待链,每个线程都在等待下一个线程持有的资源

避免死锁的方法:

  1. 破坏互斥条件:尽量使用无锁数据结构或读写锁,允许多个线程同时访问资源
  2. 破坏请求与保持条件:一次性申请所有需要的资源,要么全部获得要么全部不获得
  3. 破坏不可剥夺条件:如果线程请求资源失败,释放已持有的所有资源,稍后重试
  4. 破坏循环等待条件:对所有资源进行编号,线程必须按照固定顺序申请资源,这是最常用的方法
  5. 使用超时机制:线程在等待资源时设置超时时间,超时后释放已持有的资源
  6. 死锁检测与恢复:定期检测系统是否存在死锁,发现死锁后终止某些线程或回滚操作

3. 说说C++的内存布局,栈、堆、全局区、常量区、代码段分别存储什么?

参考答案:

C++程序的内存布局从低地址到高地址依次是代码段、数据段、BSS段、堆、栈。

  1. 代码段(.text):存储程序的机器指令,只读可执行,所有函数的代码都在这里,多个进程可以共享同一份代码段
  2. 数据段(.data):存储已初始化的全局变量和静态变量,程序启动时从可执行文件加载,可读可写
  3. BSS段(.bss):存储未初始化的全局变量和静态变量,程序启动时自动初始化为0,不占用可执行文件空间
  4. 堆(Heap):动态分配的内存区域,通过new/malloc分配,delete/free释放,从低地址向高地址增长,由程序员手动管理容易出现内存泄漏
  5. 栈(Stack):存储局部变量、函数参数、返回地址等,从高地址向低地址增长,由编译器自动管理,作用域结束自动释放,空间有限通常几MB
  6. 常量区:存储字符串常量和const修饰的全局变量,只读不可修改,通常在数据段或代码段附近

内存增长方向:堆向上增长地址增大,栈向下增长地址减小,当堆和栈相遇时会发生栈溢出或堆溢出。

4. 什么是内存泄漏?如何检测和避免内存泄漏?

参考答案:

内存泄漏是指程序动态分配的内存没有被释放,导致可用内存逐渐减少,最终可能导致程序崩溃或系统资源耗尽。

常见的内存泄漏场景:

  1. new了对象但忘记delete,或者delete之前程序异常退出
  2. 循环中重复分配内存但没有释放
  3. 智能指针的循环引用,两个shared_ptr互相引用导致引用计数永远不为0
  4. 容器中存储指针但没有释放指针指向的内存
  5. 单例模式中动态分配的资源没有释放

检测内存泄漏的方法:

  1. Valgrind:Linux下强大的内存检测工具,可以检测内存泄漏、越界访问、使用未初始化内存等问题
  2. AddressSanitizer(ASan):编译器内置的内存检测工具,编译时加上-fsanitize=address选项
  3. Visual Studio的内存泄漏检测:Windows下使用_CrtDumpMemoryLeaks函数
  4. 智能指针的use_count:检查shared_ptr的引用计数是否符合预期
  5. 自定义内存分配器:重载new/delete运算符记录内存分配和释放,程序结束时检查是否有未释放的内存

避免内存泄漏的方法:

  1. 使用智能指针:用unique_ptr和shared_ptr代替裸指针,自动管理内存
  2. RAII原则:资源获取即初始化,在构造函数中分配资源,在析构函数中释放资源
  3. 使用容器:用vector、string等容器代替手动管理的数组
  4. 避免循环引用:shared_ptr循环引用时用weak_ptr打破循环
  5. 异常安全:使用智能指针或RAII确保异常发生时资源也能正确释放
  6. Code Review:团队成员互相审查代码,发现潜在的内存泄漏问题

5. 说说C++的多态是如何实现的?静态多态和动态多态有什么区别?

参考答案:

C++支持两种多态:静态多态(编译期多态)和动态多态(运行期多态)。

静态多态:

  1. 函数重载:同一作用域内多个同名函数,参数列表不同,编译器根据参数类型和数量选择调用哪个函数
  2. 模板:通过模板参数实例化不同的函数或类,编译期确定具体类型
  3. 运算符重载:为自定义类型定义运算符的行为
  4. 优点:编译期确定调用关系,没有运行时开销,性能高
  5. 缺点:缺乏灵活性,无法根据运行时类型动态选择

动态多态:

  1. 虚函数:基类声明虚函数,派生类重写虚函数,通过基类指针或引用调用时根据对象实际类型调用对应的函数
  2. 实现机制:编译器为每个包含虚函数的类生成虚函数表,对象中有vptr指向虚函数表,调用虚函数时通过vptr查表找到实际函数地址
  3. 优点:灵活性高,可以根据运行时类型动态选择函数,支持接口和抽象类
  4. 缺点:有运行时开销,需要查虚函数表,对象大小增加(vptr),不能内联优化

区别总结:

  1. 绑定时机:静态多态在编译期绑定,动态多态在运行期绑定
  2. 性能:静态多态性能高无运行时开销,动态多态有虚函数表查找开销
  3. 灵活性:静态多态灵活性低编译期确定,动态多态灵活性高运行时确定
  4. 使用场景:静态多态适合性能敏感的场景,动态多态适合需要运行时多态的场景如插件系统、框架设计

6. 说说智能指针的实现原理,shared_ptr是线程安全的吗?

参考答案:

智能指针通过RAII原则自动管理内存,在构造函数中获取资源,在析构函数中释放资源。

unique_ptr实现原理:

  1. 独占所有权,不允许拷贝只允许移动
  2. 内部持有一个裸指针,析构时delete该指针
  3. 移动构造和移动赋值时转移所有权,原指针置为nullptr
  4. 开销最小,几乎等同于裸指针

shared_ptr实现原理:

  1. 共享所有权,使用引用计数管理资源
  2. 内部有两个指针:一个指向管理的对象,一个指向控制块(包含引用计数、弱引用计数、删除器等)
  3. 拷贝构造和拷贝赋值时引用计数加1
  4. 析构时引用计数减1,当引用计数为0时释放资源
  5. 控制块单独分配,可能导致两次内存分配,使用make_shared可以优化为一次分配

weak_ptr实现原理:

  1. 弱引用,不增加引用计数,用于打破shared_ptr的循环引用
  2. 不能直接访问对象,需要通过lock()转换为shared_ptr
  3. 可以判断对象是否还存在,expired()返回true表示对象已被释放

shared_ptr的线程安全性:

  1. 引用计数的增减是线程安全的,使用原子操作保证
  2. 多个线程可以安全地拷贝和销毁shared_ptr
  3. 但是对shared_ptr管理的对象的访问不是线程安全的,需要额外的同步机制
  4. 对同一个shared_ptr对象的读写操作不是线程安全的,一个线程读另一个线程写会有数据竞争
  5. 总结:引用计数操作是线程安全的,但对象访问和shared_ptr本身的修改不是线程安全的

7. 说说左值和右值的区别,什么是移动语义?为什么需要移动语义?

参考答案:

左值和右值是C++中表达式的分类。

左值和右值的区别:

  1. 左值:有明确内存地址的对象,可以取地址,可以出现在赋值语句的左边,如变量、数组元素、返回左值引用的函数调用
  2. 右值:临时对象或字面量,没有明确的内存地址,不能取地址,只能出现在赋值语句的右边,如字面量、临时对象、返回值类型的函数调用
  3. 左值引用:用&声明,只能绑定到左值,const左值引用可以绑定到右值
  4. 右值引用:用&&声明,只能绑定到右值,用于实现移动语义和完美转发

移动语义:

  1. 定义:将资源的所有权从一个对象转移到另一个对象,而不是拷贝资源
  2. 实现:通过移动构造函数和移动赋值运算符实现,参数是右值引用
  3. 过程:将源对象的资源指针转移给目标对象,将源对象的指针置为nullptr,避免了深拷贝
  4. std::move:将左值转换为右值引用,强制使用移动语义

为什么需要移动语义:

  1. 性能优化:避免不必要的深拷贝,特别是对于管理大量资源的对象如容器、字符串
  2. 临时对象优化:函数返回对象时,使用移动语义避免拷贝,编译器会自动优化
  3. 资源管理:对于不可拷贝的资源如文件句柄、互斥锁,只能通过移动转移所有权
  4. 容器性能:vector扩容时使用移动语义可以大幅提升性能,避免拷贝所有元素

使用场景:

  1. 函数返回大对象时,编译器自动使用移动语义
  2. 容器插入临时对象时,使用移动避免拷贝
  3. 智能指针转移所有权时,unique_ptr只能移动不能拷贝
  4. 交换两个对象时,使用std::move提高效率

8. 说说HTTP和HTTPS的区别,HTTPS的性能开销在哪里?

参考答案:

HTTP和HTTPS的主要区别在于安全性和传输方式。

HTTP和HTTPS的区别:

  1. 安全性:HTTP是明文传输数据容易被窃听和篡改,HTTPS使用TLS/SSL加密传输数据安全性高
  2. 端口:HTTP默认使用80端口,HTTPS默认使用443端口
  3. 证书:HTTPS需要CA颁发的数字证书,HTTP不需要
  4. 连接建立:HTTP直接建立TCP连接,HTTPS需要先建立TCP连

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

C++八股文全集 文章被收录于专栏

本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。

全部评论
愿意试试多多吗,看我住叶,进度随时帮看
点赞 回复 分享
发布于 昨天 20:18 上海

相关推荐

03-11 20:19
已编辑
门头沟学院 Java
太压力了,面了2个多小时,本菜比已经被拷打的瑟瑟发抖面完两个小时后通知过了1.算法题三道(1)leetcode124 二叉树中最大路径和hard题 因为不久前才刷过撕出来了,又来了一道(2)leetcode 300 最长递增子序列变种除了递增之外还加了一个权重因素,但是思路没变,dp就行(3)寻找词汇库里符合固定长度前缀的匹配单词应该是他们自己题库的题。给了一串单词列表,然后又给了一个单词,一个下标,根据这个下标的前缀去单词列表里面找到所有匹配的单词再返回思路是创建一个单词前缀树,然后根据树找,但是可能是构件树数有问题没撕出来2.全方位项目拷打基本没有问八股,全部都是项目企业场景题,哎哟我操,完全不会。我就纯八股战士,结果没想到一道八股都没问反正尽可能把企业场景往八股上引吧。。1. 微服务多点部署其中一个宕机了怎么办2. 要是mq占据大量CPU该怎么排查?MySQL占据大量CPU该怎么排查?3. 假如说让你实现视频点赞功能,你打算怎么设计?讲讲思路(我知道多级缓存,但是碰巧没背……寄)4. Redis延迟双删是什么,分布式锁,哨兵模式5. MySQL到es同步的延迟该怎么优化6. Rabbit mq的队列是怎么实现的?(这个完全没整明白,可能是队列的底层结构? 反正我硬扯的讲了一下rabbit mq的架构)还扯了很多,但是往后完全就慌了),记住的是这些
不知道怎么取名字_:2小时确实有压力,持续性的脑力劳动啊
查看9道真题和解析
点赞 评论 收藏
分享
收到了QQ部门的面试,昨天晚上面完,电话面试40分钟,无手撕,面试官非常友善,答不上来也会给你提示,给予充足的思考时间,感觉像是朋友间的聊天。1.开局自我介绍2.问了大概15分钟的项目(分布式系统一类的)3.问了一下是否了解过ai相关的技术栈(不了解)4.系统调用和库函数的区别?(我有点没想起来,然后提示了一下fwrite和write)5.关键字volatile有什么作用6.大端序小端序有什么区别(也想不起来了,只知道顺序相反)7.UDP包的最大长度8.讲一下三次握手的过程9.如果第三次握手ack包丢失但发送方又立马发送了数据会发生什么?10.static静态变量,如果写static int c,然后直接输出c的值是多少?11.设计题:如果有100万个学生的成绩,需要知道前top100,怎么去快速统计出来?(脑抽了没想到堆排序上来,前一天刚看过这道算法题,扯了一些其它排序,分析了下时间复杂度)12.场景题:有一个产品提了一个登陆模块的需求,希望同一个用户30分钟内如果重复登陆会给用户发一个提醒,怎么设计?(不知道,瞎扯了一下定时,token之类的)13.redis有哪些特性?性能的数量级有了解吗?腾讯云阿里云亚马逊的redis容量实力?14.热key大key是什么,怎么解决?15.vim编辑器怎么查找,命令是什么?16.后面就是闲聊了,问我最近有没有看什么技术文档,家是哪里的,未来的职业规划基本都是围绕简历上来问的,感觉是寄了,答得不太好
查看18道真题和解析
点赞 评论 收藏
分享
刚刷到字节跳动官方发的消息,确实被这波阵仗吓了一跳。在大家还在纠结今年行情是不是又“寒冬”的时候,字节直接甩出了史上规模最大的转正实习计划——ByteIntern。咱们直接看几个最硬的数,别被花里胡哨的宣传词绕晕了。首先是“量大”。全球招7000多人是什么概念?这几乎是把很多中型互联网公司的总人数都给招进来了。最关键的是,这次的资源分配非常精准:研发岗给了4800多个Offer,占比直接超过六成。说白了,字节今年还是要死磕技术,尤其是产品和AI领域,这对于咱们写代码的同学来说,绝对是今年最厚的一块肥肉。其次是大家最关心的“转正率”。官方直接白纸黑字写了:整体转正率超过50%。这意味着只要你进去了,不划水、正常干,每两个人里就有一个能直接拿校招Offer。对于2027届(2026年9月到2027年8月毕业)的同学来说,这不仅是实习,这简直就是通往大厂的快捷通道。不过,我也得泼盆冷水。坑位多,不代表门槛低。字节的实习面试出了名的爱考算法和工程实操,尤其是今年重点倾斜AI方向,如果你简历里有和AI相关的项目,优势还是有的。而且,转正率50%也意味着剩下那50%的人是陪跑的,进去之后的考核压力肯定不小。一句话总结: 27届的兄弟们,别犹豫了。今年字节这是铁了心要抢提前批的人才,现在投递就是占坑。与其等到明年秋招去千军万马挤独木桥,不如现在进去先占个工位,把转正名额攥在手里。
喵_coding:别逗了 50%转正率 仔细想想 就是转正与不转正
字节7000实习来了,你...
点赞 评论 收藏
分享
评论
2
12
分享

创作者周榜

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