跳转至

随着知识点越来越多,记忆难免会混乱、遗忘,一个好的知识点总结能有效帮助回忆。

C语言

1.数组和指针

  • 数组指针
  • 指针数组
  • 函数指针
  • const和指针
  • sizeof和指针和数组
  • strlen和字符数组
  • void* 指针:
    • 隐式转换抹去实例类型信息,强制转换变为其他类型。
    • 由于无类型信息,不可解引用(取内容),地址无法自增。
    • NULL,是宏,是强制转换的(void*)0指针(C++改为了0,避免了重载识别为int类型)

2.库函数的模拟实现

  • memcpy(void,const void,size):提供拷贝任何数组形式的内存的。
    • const作用是防止源字符串被修改
    • 判空,转换成char,循环赋值。
  • memmove(void,const void,size):相比cpy,新增了内存重叠的判定。
    • 先判空(assert),用ret指向dst(dst后面要自增发生变化)。
    • 防止指向同字符串不同位置(dst地址<=src或者强制转换到char*后,dst>=src+len)。
    • 如果不重叠,则从低地址开始复制(强制转换到char,dst自增,src自增)
    • 如果重叠,就从高地址开始倒过来赋值(类似于合并数组)
    • 返回ret。
  • strstr(const char,const char):是否是子串,是的话返回开始的首地址,否则返回NULL
    • str2是否为NULL?是的话直接返回str1地址
    • 双重while循环遍历,第二重循环遍历子串为空(遍历完了)时直接返回首地址。
  • strcpy/strlen/strcmp 出现相对少,上面三个要求轻松手撕

3.数据存储

  • 基本类型大小,循环越界与死循环问题。
  • 整型存储规则,原(符号位正负?)反(负数规则)补(区分0)。IEEE浮点数(考的少)
  • 大小端及如何判断(考的很多):存在原因?代码判断(强转或联合体)?端序转换(位)?
  • 类型提升和截断:截断因素(类型和大端小端),补位规则(补0和符号)

4.编译链接

  • 预编译--宏(考察很多,轻松实现宏函数)
  • 预编译、编译、汇编、链接的过程

C++

1.⭐⭐⭐面向对象基本概念

封装概念

  • 数据,方法以及对外提供接口。
  • private,protected,public级别。同类对象无权限隔离,可以访问private。

继承概念

  • (继承中)隐藏特点?隐藏后调用基类?
  • 公共继承、私有继承:权限问题?转换会发生什么?。

⭐⭐⭐多态

概念

静态绑定

  • 重载定义?特点? 函数签名?
  • 模版泛型?特化,偏特化。为什么放.h?\
  • (好玩的)lambda模版参数包展开c++17(结合ECS拷贝组件使用)
  • 调用虚函数的静态绑定?

虚函数virtual

  • 继承构造出多态的两个条件?this的隐式多态? 虚函数重写条件?两个例外(协变和析构)?
  • 析构为什么需要虚函数?什么是指针的切割?
  • 接口继承,实现重写?override和final的作用?
  • 虚表指针vptr:数量,大小,产生时机,类型
  • 虚表vtbl:和类对应的关系,数量,位置,存储内容(RTTI,函数指针),虚函数存放(重写和未重写?)?数组最后?
  • 纯虚函数?抽象类?有啥不同?= 0
  • 多继承重写的三个虚函数地址?未重写的虚函数的地址?
  • 虚继承内存排布?为什么要虚继承?菱形继承内存模型?
  • 虚基表:虚基表指针,虚表内存模型改变,偏移量,多份
  • 构造和析构中调用虚函数调用情况:存储角度、实用角度,调用后的情况
  • 虚函数的静态绑定机制
  • 虚函数能否内联:编译时(理论,实践),运行时(加final?)。
  • 派生类为什么不能调用基类?

2.⭐⭐⭐ 智能指针

【C++】详谈C++智能指针的前世今生-CSDN博客 - 深拷贝、浅拷贝、野指针悬空指针RAII设计思想 - 创造的原因:RAII->auto_ptr(多次析构、copy+NULL)->boost(scoped_ptr那三个)->C11(正式引入,标准废除autoptr)->C14(控制块)->C17(移除auto_ptr) - 智能指针共性:RAII、operator*operator-> - unique: - 删除拷贝赋值、删除拷贝构造。 - sharedptr: - 两个指针:一个资源,一个控制块 - 控制块内容:强引用计数、弱引用计数、其他数据(删除器、分配器等) - make_shared和(new)构建: - 仿照make_pair,一个是构建资源后拷贝,一个是直接分配内存块在里面new 那块资源。(资源是否分散两次构建的问题) - 不能使用定制删除器和数组new【】 - 线程安全:原子自增自减(加锁)。同时修改等问题(外部加锁) - 循环引用问题(小tip,为什么不能弱循环引用) - 拷贝赋值怎么做的?自检、旧控制块释放(包含自减)、swap、新控制块计数增加。 - 释放过程怎么做的:自减,为0删除指针、计数 - 构造、拷贝构造、拷贝赋值,引用块自增。 - 析构,引用块自减。 - 弱引用计数有啥用?延长生命周期。share拷贝weak不会+,weak拷贝weak、shared才会加+。 - 定制删除器:存在del的参数,底层是函数包装器function,支持仿函数、lambda、函数指针操作。 - boost本来有智能指针数组的,不过c11没加,所以提供了这个 - 方便实现delete【】。

  • (超进阶)代码实现(尽量能讲STL源码):线程安全保障,
  • shared_ptr循环引用解决:weak,手动释放指针打破,鸵鸟策略。
  • weak_ptr单独使用:另一引用被销毁时,weak_ptr自动失效,避免野指针。
  • shared_ptr线程安全问题:
  • (进阶)unique_ptr实现,shared_ptr 实现
  • (补充)一个引擎如何将智能指针改成自己的指针:别名功能,符号重构
  • 智能指针内存泄漏场景

3.⭐⭐⭐ new/delete、malloc/free,STL二级分配

malloc和free是库函数,需要头文件支持 - malloc:可用内存块(结构体:可用标识+size+前一块大小+空闲链表指针)与空闲链表。遍历链表找不到则请求延时,整理内存片段合并。内存分配成果返回==void*,失败返回NULL - 小内存brk(小于等于128kb),大内存直接mmap映射独立内存页。剩下的部分切成新空闲块加入链表。 - free:通过当前指针参数-内存块结构体大小获取内存块指针,设置为可用并释放size大小的内存。 new和delete是C++关键字,是操作运算符,支持operator重载实现: - new运算符: 1.operator new:(可单独拿出来使用,不)自动计算内存,调用malloc,失败返回bad_alloc()异常,成功则严格的返回指针==void* 2.调用构造函数:获取内存指针,通过placement new机制调用构造函数 - nothrow new:不返回badalloc异常,而是nullptr - delete的过程:调用析构,operator delete调用free()删除对应的指针 - new[]:会多分配8的内存,记录数组长度n - delete[]:根据记录的n值,逐个调用析构。误用delete会造成内存泄漏

  • 什么时候使用delete this?主动释放资源,完成某个特定任务后销毁自身,异步回调
    • 注意:必须在堆上分配、必须是最后操作、避免析构调用、确保该对象不会再被访问
  • 成员函数delete this的后果?

STL的两级分配器:小于128B内存池(链表管理)技术,大于128Bmalloc()。 C++的内存池技术:优化内存碎片问题,针对小对象,申请一定数量内存块(通常8B),构成链表。申请后改内存块从空闲链表去除。

4.⭐⭐⭐ STL数据结构

Vector

  • push_back
  • emplaca_back:万能引用,完美转发,容器数据获取,迭代器判断,扩容判断,内部断言判断,编译期条件分支选择(无异常且默认构造,扩展区域内存保护并使用自定义构造),分配内存,传入指针和对象进行构造,迭代器失效,设置模版类型的返回值,迭代器移动,返回返回值。
  • vector扩容reserve:memcpy,memmove。

map和set 红黑树

  • avl是什么

unordered_map和set 哈希表

5.⭐⭐左值右值,移动构造,万能引用,完美转发

  • 左值:类型--引用,右值引用。
  • 右值:值类别--(无地址)纯右值,(无持久地址)将亡值(区分一下右值引用)
  • std::move:本身不移动,源码只有静态强制转换为右值引用类型返回,但实际上是将亡值。实际内存操作在移动拷贝和移动赋值中实现

6.⭐⭐类型转换

string、char,char{}类型万物互转 隐式转换条件:值类型-低精度转高精度,有符号无符号混合转无符号,赋值表达式,传参和返回值 非基本类型:NULL转任意,任意转void,void转任意 指针/引用: 1.派生转基类(不改变const或volatile属性)2.非常量转为常量 3.类的隐式转换:单参数构造函数创建,Initialize_list多参数构造函数创建。类型转换运算符自定义。 显式转换条件: - const:不能去除变量的常量性,只能去除指针或引用的。移除const和volatile - static:静态转换(编译期确定),基本的隐式转换工作。不然一般编译期会给警告精度损失。指针地址不会改变。 - dynamic:根据RTTI,进行向上,向下(更加的安全),横向(指针偏移)的转换。必须有虚函数。也可以把类转void。 - reinterpret:比特位的简单拷贝和重新解释。如不同类型指针和引用互转,指针整数间互转。

7.⭐函数调用过程

堆栈,

8.拷贝构造、移动构造、拷贝赋值、移动赋值代码实现

9.⭐Lambda和仿函数

  • 底层实现?仿函数是什么?
  • 值引用、引用,auto&&推导
  • 捕获、闭包概念

10.C11 function<>和std::bind和元组std::tuple

  • function函数包装器:传入返回值和形参
  • bind::目标函数,变量,占位符

11.⭐⭐内联函数和宏

都可以实现替换的目的 - 引用库#include - 宏定义 # define X Y - 条件编译 inline函数失效 - 代码体积过大(十行) - 递归函数 - 复杂控制流 - 虚函数 - 含指针或引用调用 - 动态库(地址无法确定) 为什么尽量以const、enum、inline替代宏:记号表追踪问题、作用域、取地址问题,函数检查和难以预料的行为。

11.⭐⭐static和全局变量和extern'C'和全局静态。

  • 全局(+static)静态和全局变量区别:作用域为单个文件
  • 函数默认为extern,加了static就不能被外部访问了。头文件不要用
  • static不需要初始化,默认为0值。使用时才会分配到内存。
  • extern‘C’:使代码按照C语言方式编译,主要是为了区分C++的重载底层函数命名。
    • 可以结合动态链接,和C#(Unity)进行相互调用。
  • 全局静态变量
    • cpp定义全局变量,.h文件extern一下,其他想用只需要引用这个有文件
    • 定义一个静态变量,提供get和set函数
    • 提供一个类的静态成员变量,直接通过类获得。

12.⭐⭐内存分配和二进制问题

  • 结构体的大小,union的大小,类的大小,空类大小。
  • enum和enumClass(C11)
  • 内存对齐的意义
  • 无符号整数溢出导致的死循环
  • 栈溢出的原因:递归、还有什么?

13.⭐const

  • const作用于函数:
  • 常量指针和指针常量
  • 修改方式
  • constexpr常量表达式

14.⭐⭐指针和引用区别

指针传递:本质是值传递,传递的是地址值的副本,可以多级。 引用:本质为指针常量, - 必须初始化绑定 - 不能更改绑定,对外没有内存地址 - 不能多级。 - 返回值?和值传递的区别?(对象生命周期问题) - 传参相比值传递如何?

15.内存大小、内存对齐、sizeof原理

  • int类型、long、指针
  • 对齐方式
  • sizeof原理:编译期计算。

15.⭐内存泄漏的定位

16.C++普通函数与成员函数区别

普通函数: - 作用域为全局或命名空间,类外定义,访问全局变量或传入参数。 - 无封装性。不依赖对象,独立存在于代码区。无法继承,不支持多态(但可以重载) 成员函数: - 作用域为类,类内定义,类外需要作用域解析符(::)。可访问所有类成员,调用隐含this指针。 - 支持封装,通过对象(.)或对象指针(->)调用(除了静态成员函数)。支持继承多态与重载。 - 代码区仅一份副本,默认内联。

17.auto和decltype

18.⭐STL sort实现

  • 默认快速排序
  • 数据量过小:插入排序
  • 避免递归深度过深,堆排序
  • 能对哪些容器进行排序?

19. emplace_back设计思想

20.友元函数,友元类

21.Constexpr 编译期常量

  • 替代#define和const,具备类型安全和作用域
  • 强制编译期计算,避免运行时开销,传参到函数直接获得答案替换成常量。(支持模版元编程简化逻辑,允许编译期构造)
  • C11单行,C14局部变量与循环,C17编译条件分支,C20允许虚函数和动态类型分配。
  • 默认的隐式inline,可以但没必要一起写,关注编译期。inline关注运行时。

22.迭代器是什么

  • 各容器迭代器的差别

23.volatile是什么?

类型修饰符,告诉编译期每次必须取地址,防止一些过度的优化(如循环直接把之前的值拿过来用)导致的问题。