C++ 进阶总复习 (1~15)
-
- 目的
-
- [1. 介绍下程序从编写到可执行的整个过程](#1. 介绍下程序从编写到可执行的整个过程)
- [2. C++中的auto和decltype的区别](#2. C++中的auto和decltype的区别)
- [3. 介绍下多态的实现原理](#3. 介绍下多态的实现原理)
- [4. C++中的new[] 和delete[] 为什么一定要配对使用?](#4. C++中的new[] 和delete[] 为什么一定要配对使用?)
- [5. C++中malloc申请的内存 可以使用delete释放嘛](#5. C++中malloc申请的内存 可以使用delete释放嘛)
- [6. 什么情况下会出现内存泄漏](#6. 什么情况下会出现内存泄漏)
- [7. C++ String内部是使用堆内存还是栈内存](#7. C++ String内部是使用堆内存还是栈内存)
- [8. 平时开发C++程序的时候错误码使用的多还是异常使用的多](#8. 平时开发C++程序的时候错误码使用的多还是异常使用的多)
- [9. C++中如何进行性能优化](#9. C++中如何进行性能优化)
- [10. 模板的实现一定要放在头文件中嘛?](#10. 模板的实现一定要放在头文件中嘛?)
- [11. 什么场景下使用继承 什么场景下使用组合](#11. 什么场景下使用继承 什么场景下使用组合)
- [12. 什么情况下会出现死锁 如何避免](#12. 什么情况下会出现死锁 如何避免)
- [13. 如何实现线程池 给出大致思路](#13. 如何实现线程池 给出大致思路)
- [14. C++针对返回值进行的优化](#14. C++针对返回值进行的优化)
- [15. C++中动静态库的区别](#15. C++中动静态库的区别)
目的
写这一系列文章的目的主要是为了秋招时候应对计算机基础问题能够流畅的回答出来 (如果不整理下 磕磕绊绊的回答会被认为是不熟悉)
本文章题目的主要来源来自于 面试鸭
部分面试鸭上没有而牛客网上有的博主会进行查缺补漏
题目编号按照面试鸭官网题号方便大家寻找
题解大部分是博主根据自己之前的博客再加上部分网上的内容进行口语化的表述 如果涉及到省略的部分博主会提供自己或者其他人的博客链接
1. 介绍下程序从编写到可执行的整个过程
参考博客
程序从编写到可执行分为下面几个过程
预处理 编译 汇编 链接
首先是预处理阶段 在这个阶段的时候程序会展开头文件 宏替换 去注释等操作
到了编译阶段 这个阶段最大的作用就是进行语法词法分析 并对程序进行优化 看看有没有什么错误的代码 之后转化为汇编语言
汇编阶段则是将汇编语言转化为二进制语言
链接阶段则会生成一个可执行程序 它会合并段表 符号表合并和重定位
2. C++中的auto和decltype的区别
auto关键字用于自动推导变量的类型
而decltype关键字用于推导表达式的类型 它会返回表达式对应的类型信息而不进行计算
decltype关键字在模板编程的时候十分好用
3. 介绍下多态的实现原理
参考博客
这里多态主要包括两点 一个是静态多态 一个是动态多态
静态多态的话 它的原理就是函数重载嘛 我们都知道在C语言链接阶段的时候是通过函数名来标记一个函数的(参数不论) 但是C++的话 参数的个数 顺序 类型都会影响函数标记 所以说这就是它的底层原理
然后动态多态的话就是通过虚指针和虚表来实现的嘛
如果说我们的类中有一个虚函数 那么实例化对象的时候 这个对象的大小就会多出四个字节 实际上就是多出一个指针的大小 这个指针就是虚指针
然后这个指针会指向一张表 我们把这张表叫做虚表每个类的虚表都不同 所以说自然会产生不同的函数效果
4. C++中的new[] 和delete[] 为什么一定要配对使用?
因为如果不这样做可能会造成内存泄漏的情况
我们一般来说是建议new[] 和 delete[] 配对使用的 但是说也有特殊情况
比如说如果我们new []了内置类型的话 我们使用delete删除就不会造成内存泄漏的情况
如果是自定义类型的话 就会造成这种情况
为什么自定义类型会造成而内置类型不会造成呢? 这里可以推断一下 可能是因为内置类型重载了opeartor new 和opeartor delete
5. C++中malloc申请的内存 可以使用delete释放嘛
不可以 malloc申请的内容应该使用free释放
虽然说delete底层是调用free函数 但是delete还做了更多的事情
它首先会调用析构函数 之后才会释放内存
如果说直接使用delete的话很可能会出现未定义行为 甚至报错
6. 什么情况下会出现内存泄漏
参考博客
其实很多情况下都会出现内存泄漏
比如说指针原本指向一块分配好的内存 但是指针丢失了
比如说循环引用
比如说创建对象之后没有释放
那么如何解决呢?
当然也有很多种方法解决
我们可以使用内存泄漏检测工具 valgrind
然后使用RAII思想去检测
有一个良好的代码编程习惯等等
7. C++ String内部是使用堆内存还是栈内存
参考博客
模拟实现过string之后就会知道 string的底层其实维护了一个指针 这个指向会指向堆上开辟的空间
所以说string内部使用的是堆内存
8. 平时开发C++程序的时候错误码使用的多还是异常使用的多
错误码使用的比较多
因为错误码相对于异常来说
- 性能开销更小
- 更适合处理复杂的流程 能够只管的反应错误信息
但是错误码也有缺点
就是我们写需求的时候 每次写新的需求 大概率就要写新的错误码 就会造成代码臃肿 如果不注释 就很难知道错误码表示什么信息 可能出现意料之外的错误
9. C++中如何进行性能优化
这个可以从很多点上去回答
从代码编写的角度来说
- 选取合适的数据结构 比如说在频繁查询的场景下使用哈希表来存储数据
- 选择合适的算法 优化算法的时空复杂度
- 使用RAII思想 使用对象管理资源 避免资源泄漏
从架构层面上来说
根据需求来设计合适的架构 比如redis和mysql的冷热数据分离
10. 模板的实现一定要放在头文件中嘛?
参考博客
我们一般来说是简历将模板和生命和定义放在一起的
但是也不是没有办法比如说我们可以显示的实例化
这样子链接的时候就不会报错了
11. 什么场景下使用继承 什么场景下使用组合
我们在is a的关系下使用继承
在has a的关系下使用组合
就比如说人类和学生的关系 学生是人类 所以说这个时候我们推荐使用继承
比如说轮胎和汽车的关系 轮胎是汽车的一部分 所以说这个时候我们推荐使用组合
12. 什么情况下会出现死锁 如何避免
参考博客
这里其实是考察死锁的四个必要条件
- 互斥 一个资源每次只能被一个执行流使用
- 请求与保持 一个执行流因请求资源而阻塞时 对已获得的资源保持不放
- 不剥夺 一个执行流已获得的资源 在未使用完之前 不能强行剥夺
- 循环等待 若干执行流之间形成一种头尾相接的循环等待资源的关系
至于如何避免死锁就很简单了 我们只需要破坏死锁的四个必要条件即可
13. 如何实现线程池 给出大致思路
这里具体参考我的这篇博客
这个问题是个很宏大的问题 能够引出很多小问题 可以把上面那篇博客吃透之后再去回答
14. C++针对返回值进行的优化
在返回一个临时对象的时候 如果按照一般的方式 可能要经历
析构函数 拷贝构造
而C++优化可能会直接在返回值位置构建一个对象 这就是C++针对返回值的优化
15. C++中动静态库的区别
参考博客
动静态库的本质其实都是半成品程序 也就是说.o文件 也可以说是二进制文件嘛
静态库的话其实就是在程序链接的时候将这个.o文件打包到程序里面嘛
动态库的话是程序运行的时候将动态库加载到共享区去
他们之前的区别主要是 静态库打包到程序里面 它就不需要依赖外部文件了嘛 但是有利有弊就会造成程序的膨胀
动态库就是相反嘛 节省磁盘空间 减少冗余代码 但是说如果缺少了库文件就无法运行
打个比方的话就是 将库文件比作电脑嘛
静态库就是电脑放在宿舍里面 随时都能玩 但是占用空间
动态库就是电脑放在网吧 想玩要去网吧玩 但是占用空间小