美团9.7后端面试

岗位

应该是成都的搞文件存储

流程

  1. 自我介绍
  2. C++怎么实现虚函数的
    1. 为了实现虚函数, C++使用了虚函数表来动态查找实际所需要执行的函数.
    2. 每个拥有虚函数的类(包括其派生类), 都有一个虚函数表, 这个虚函数表是编译器在编译时就已经建立好的
    3. 虚函数表中包含了类的实例所能调用的每个虚函数的指针, 指针指向了该类可以访问的最高级的对应函数.
    4. 编译器还向基类中隐式地添加了虚函数表指针, 当在对象上调用虚函数时, 通过虚函数表指针从虚函数表中查找正确调用的函数.
  3. 析构函数可以是虚函数吗?构造函数呢?
    1. 析构函数可以是虚函数, 当一个基类指针指向派生类, 并通过该指针删除对象时, 如果析构函数是虚函数, 就可以确保先调用的是派生类的析构函数, 保证派生类的资源被正确释放, 然后再调用基类的析构函数.
    2. 构造函数不能是析构函数, 当一个类的构造函数被执行时, 其虚函数表指针还为被正确初始化, 因此不能正确进行虚函数的执行, 所以构造函数不能是虚函数.
  4. C++面向对象的三大特性
    1. 封装
      1. 数据和代码捆绑在一起, 避免外界干扰和不确定性访问
      2. 把客观事物封装成抽象的类, 类还可以将自己的数据和方法只让可信的类或者对象操作, 对不可信的进行信息隐藏.
    2. 继承
      1. 让某种类型的对象获得另外一个类型对象的属性和方法
      2. 实现继承(使用基类的方法和属性)
      3. 接口继承(仅使用基类属性和方法的名称, 但子类必须自己进行实现)
      4. 可视继承(子窗体)使用基窗体(类)的外观和实现代码的能力.
    3. 多态
      1. 同一事物表现出不同的能力(向不同的对象发送同一消息, 不同的对象在接受时会产生不同的行为)
      2. 重载实现编译时多态, 虚函数实现运行时多态
      3. 覆盖: 子类重新定义父类虚函数的做法
      4. 重载: 存在多个重名函数, 但这些函数的参数表不同
  5. 我重载了一个函数, 使用基类指针调用派生类的这个函数, 实际上调用的哪个?
    1. 取决于该函数是否被声明为虚函数
    2. 如果函数不是虚函数, 则会调用基类的版本(静态绑定)
    3. 如果函数是虚函数, 则回去调用派生类的版本(动态绑定)
  6. 介绍下智能指针
    1. shared_ptr: shared_ptr可以与其他共享一个实例的访问权, 且多个指向同一个实例的shared_ptr共享一个引用计数, 在生成新的shared_ptr时, 指向的实例的引用计数要+1, 销毁shared_ptr要将引用计数-1, 并判断是否需要销毁指向的实例.
    2. unique_ptr: 独占对于一个实例的访问权, 在销毁时也会销毁所指向的实例, (但是可以通过使用release的方法转移一个即将被销毁的指针的实例)
    3. weak_ptr: 不控制器所指向对象生命周期的指针, 为了解决shared_ptr可能导致的循环引用问题而设计, 需要和shared_ptr一起使用.
    4. 使用智能指针可以帮助避免内存泄漏和空悬引用, 仍然需要谨慎对待内存管理问题
  7. weak_ptr不增加引用计数, 是为了解决什么问题?
    1. 循环引用问题, 两个实例都包含一个指向对方的指针, 导致两者的引用计数最小是1, 形成内存泄漏.
  8. malloc和new的区别
    1. malloc只分配内存空间, new同时执行构造函数
    2. malloc需要显式声明空间, new不需要
    3. malloc是标准库函数, new是运算符, 还可以重载
    4. malloc分配失败返回NULL, new分配失败抛出bad_alloc异常
    5. malloc返回的指针必须强制转换为目标实例的指针, new直接返回的是目标实例的指针.
  9. C++内存分布
    1. 从低地址到高地址
    2. 代码区--存放机器码
    3. 数据区--存放已初始化的全局和静态变量, 常量数据
    4. BSS(Block Started By Symbol)--存放未初始化的全局和静态变态
    5. 堆--动态分配内存的区域, 低地址向高地址增长, 容量大于栈
    6. 栈--从高地址向低地址增长, 存放局部变量, 参数函数, 返回变量.
  10. 进程和线程的区别
    1. 进程是操作系统资源分配的基本单位, 线程是处理器任务调度和执行的基本单位
    2. 线程共享本进程的地址空间和资源, 而内存之间有各自独立的地址空间和资源
    3. 多进程比多线程要更健壮, 多线程中一个进程崩溃会导致整个进程崩溃, 而一个进程崩溃并不会直接导致其他进程崩溃.
    4. 每个进程有一个程序运行的入口, 顺序执行序列, 但是线程不能独立执行, 必须依赖进程管理
    5. 两者都可以并发执行
    6. 进程切换时, 消耗的资源大, 效率低
  11. 进程和线程进行调度的时候, 谁开销大, 为什么?
    1. 进程切换的开销大
    2. 因为每个进程都有独立的代码和数据空间, 在进程切换时, 操作系统需要保存和恢复更多的信息, 例如切换硬件上下文, 刷新TLB, 切换内核态堆栈
    3. 因为同一进程中的线程共享代码和数据空间, 所以线程的切换主要涉及用户态与内核态的转化. 发生调度时, 先触发中断进入内核态, 切换完成后再回到用户态.
  12. Linux使用什么系统调用来创建新的进程?
    1. fork: 创建一个新的进程, 是原有进程的一个子进程, 也是原有进程的一个副本, 在父进程中, fork返回子进程的PID, 子进程复制了父进程的task_struct, 系统堆栈空间和页面表.
    2. vfork: 创建一个新的进程, 但是新的进程与父进程共享内存, 堆栈和数据资源都是共享的, 避免了大量的内存复制. 必须执行完毕后, 父进程才会被继续调度
    3. clone: 可以选择性地将资源赋值给子进程, 而没有复制的资源则会使用指针来共享给子进程.
  13. 创建了新的进程之后, 子进程是否会共享父进程的资源?
    1. vfork会共享内存, clone可以指定共享的资源, 也可以全部共享或不共享. 但同一组父子进程会,共享一些文件描述符, 进程组ID
  14. 什么是僵尸进程?
    1. 一个进程fork了一个子进程, 如果子进程退出, 而父进程没有调用wait或者waitpid来获取子进程的状态信息, 那么子进程的进程描述符仍然保存在系统当中, 成为僵尸进程.
    2. 僵尸进程会一直占用进程号, 系统的进程号是有限的, 如果产生了大量的僵尸进程, 可能会导致没有可用的进程号来继续系统的运行.
  15. 还用过哪些系统调用?
    1. 进程控制: fork, clone, execve(运行可执行文件), exit(终止), getpid(获取进程标识号), getppid(获取父进程标识号)
    2. 文件系统控制: open, read, write, close等
    3. 系统控制: ioctl, reboot
    4. 内存管理: brk(改变数据段空间的分配), mmap(映射虚拟内存页), munmap(去除内存页映射)
    5. 网络管理: gethostname(获取主机名称), sethostname(设置主机名称)
    6. Socket控制: bind(绑定端口), connect(连接远程主机)
  16. 有用过MySQL? KV存储?
  17. Linux下软链接和硬链接是什么? 有什么区别?
    1. 硬链接是通过索引节点进行连接, Linux的文件系统中, 每个文件都会有个编号, 称为索引节点号, 索引节点用于标识这个文件. 硬链接就是通过这个索引节点进行连接, 只有在同一文件系统中的文件之间才能创建硬链接. 可以理解为硬链接就是让一个文件拥有多个有效路径名, 只要有一个以上有效的链接, 文件的数据块就不会被释放.
    2. 软链接类似于Windows的快捷方式, 其本身是个文本文件, 包含了另一文件的位置信息. 可以跨文件系统使用.

手撕的二叉树层序遍历, 6分钟撕完.

结束一小时后回人才库.

作者

Giles

发布于

2023-09-07

更新于

2023-09-11

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×