ELF文件
可执行与可链接格式格式
用于可执行文件、目标代码、共享库和核心转储 (core dump) 的标准文件格式
ELF 文件都由一个 ELF header 和紧跟其后的文件数据部分组成
.text section:代码段
.rodatasection:只读数据段
.datasection:数据段
.bsssection: bss 段
内存分区
栈
堆
全局/静态存储区
常量存储区
代码区
堆与栈
栈溢出
申请方式
申请后系统响应
申请效率
实际的内存管理
作用域类型
变量定义与生存周期
变量类型
全局变量
作用域
生命周期
静态全局变量
作用域
生命周期
局部变量
作用域
生命周期
静态局部变量
作用域
生命周期
分配内存空间
静态变量一般存储在数据段,包括 data 段、bss 段、rodata 段
局部变量一般存储在栈区或者堆区
内存对齐
原理
原因
结构体内存对齐处理
智能指针 smart pointer
功能
三种智能指针
unique_ptr
shared_ptr
shared_ptr 中资源可以被多个指针共享
但是多个指针指向同一个资源不能被释放多次,因此使用计数机制表明资源被几个指针共享
线程安全问题
weak_ptr
auto_ptr
创建方式
make_unique 与 make_share
编译与链接
大端与小端
小端序
将低序字节存储在起始地址(低位编址)
将一个多位数的低位放在较小的地址处,高位放在较大的地址处
优点
大端序
将高序字节存储在起始地址(高位编址)
将一个多位数的低位放在较大的地址处,高位放在较小的地址处
优点
内存泄漏
原因
内存泄漏位置
内存泄漏检测与预防
valgrind memcheck
静态代码检测工具
gdb调试工具
hook掉malloc / free函数,以及重定义new / delete函数,记录申请代码的相应信息和堆栈
防止内存泄漏
内部封装
使用智能指针
遵循 RAII(Resource acquisition is initialization)原则
include " " 和 <>
include关键字
include <>
include " "
capture list:捕获列表
捕获变量
左值
右值
实际上无论是左值引用还是右值引用,从编译后的反汇编层面上,都是对象的存储地址的引用
旧版本C++编译器默认提供的函数
C++ 11 允许显式地表明采用或拒用编译器提供的内置函数
= default
= delete
常量表示式对编译器来说是优化的机会,编译器时常在编译期执行它们并且将值存入程序中
允许用户保证函数或是对象构造函数是编译期常量,编译器在编译时将去验证函数返回常量
C++ 11 将会提供产生伪随机数的新方法
函数返回值类型推导
函数返回类性也可以用 auto 类型,编译时会有编译器进行类型推导
lambda
在 C++ 14 中,lambda 函数的形式参数允许泛型
允许在 lambda 捕获列表中对变量进行表达式赋值,并且支持定义新的变量并进行初始化
constexpr 函数限制松动
deprecated 属性
结构化绑定
if-switch 语句初始化
constexpr lambda 表达式
namespace 嵌套
std::any
std::basic_string_view
sizeof 是 C++ 中的运算符
strlen 是头文件 中的函数
六大作用
static 全局静态变量
static 局部静态变量
static 静态函数
static 静态成员变量
static 静态成员函数
静态成员函数不能调用非静态成员变量或者非静态成员函数
作为类作用域的全局函数。
静态成员函数不能声明成虚函数(virtual)、const 函数和 volatile 函数。
static 对象
const 变量
const 指针
const 修饰指针
const 引用
const 成员变量
const 函数参数与返回值
const 成员函数
编译阶段区别
安全性区别
存储空间区别
调试
可以用于定义内联函数
内联函数
在调用点处展开,这样可以大大减少由函数调用带来的开销,从而提高程序的运行效率
类内定义成员函数默认是内联函数,除了虚函数以外
在类内定义成员函数,可以不用在函数头部加 inline 关键字
类外定义成员函数,若想定义为内联函数,需用关键字声明。关键字 inline 必须与函数定义体放在一起才能使函数成为内联,如果只是放在函数声前面不起任何作用
inline 函数工作原理
原理
inline 函数优缺点
优点
缺点
可能会在寄存器变量资源利用方面产生开销
二进制可执行文件的大小会很大,因为相同的代码重复
过多的内联也会降低指令缓存命中率,从而降低从缓存内存到主内存的指令获取速度
内联只是对编译器的请求,而不是命令
编译器可以忽略内联请求
delete 与 free 的区别
new
malloc
delete
free
场景
功能
define
typedef
三种访问权限
三种继承对应功能
准许访问
某些情况下,需要在子类中将一个或多个继承的成员恢复其在基类中的访问权限
方法
封装
继承
多态
has-A包含关系
use-A,一个类使用另一个类
is-A,继承关系,关系具有传递性
函数重载
函数隐藏
函数重写(覆盖)
重写和重载的区别
范围
参数
隐藏和重写,重载的区别
范围
参数
执行阶段
虚函数与子类隐藏
在类中用 virtual 关键字声明的函数叫做虚函数
虚函数的地址保存在虚函数表中,虚函数表的地址保存在含有虚函数的类的实例对象的内存空间中
当基类的指针指向派生类的对象时,通过派生类的对象的虚表指针找到虚函数表(派生类的对象虚函数表),进而找到相应的虚函数 Derive::f() 进行调用
纯虚函数必须实现,否则编译器会报错
构造函数不能定义为虚函数
原因
在构造函数和析构函数中调用虚函数
析构函数最好定义为虚函数,特别是对于含有继承关系的类
纯虚函数
实现原理
虚函数表与指针大小
指从多个直接基类中产生派生类。多重继承容易出现命名冲突和数据冗余问题
解决办法
深拷贝
浅拷贝
构造函数调用顺序
类对象的初始化顺序
类的成员初始化
类中可能含有静态变量和全局变量,由于静态变量和全局变量都被放在静态存储区,他们的初始化在 main 函数执行之前已被初始化,且 static 变量必须在类外进行初始化
初始化列表
成员变量在使用初始化列表初始化时,与构造函数中初始化成员列表的顺序无关,只与定义成员变量的顺序有关
如果类不使用初始化列表初始化,而在类的构造函数内部进行初始化时,此时成员变量的初始化顺序与构造函数中代码逻辑有关
类成员在定义时,是不能初始化的
类中 const 成员常量必须在构造函数初始化列表中初始化
类中 static 成员变量,必须在类外初始化
成员初始化列表效率高的原因
对于用户自定义类型,利用成员初始化列表效率高,直接调用该成员变量对应的构造函数即完成初始化
列表初始化会减少调用默认的构造函数的过程
析构顺序和类对象的初始化顺序相反
静态类型
量在声明时的类型,是在编译阶段确定的。静态类型不能更改
静态绑定
动态类型
目前所指对象的类型,是在运行阶段确定的。动态类型可以更改
动态绑定
编译时多态
在程序编译过程中出现,发生在模板和函数重载中(泛型编程)
运行时多态
实现方式不同
分配空间
初始化
赋值
对于拥有虚函数的类的对象,还需要给虚表指针赋值
使用const关键字修饰成员函数
相反的关键字功能:mutable
限制在堆上
原因
限制在栈上
空类声明时编译器不会生成任何成员函数
空类定义时编译器会生成 6 个成员函数
类的大小是指类的实例化对象的大小,用 sizeof 对类型名操作时,结果是该类型的对象的大小
对类大小有影响的
对类大小无影响的
普通成员函数,静态成员函数,静态数据成员,静态常量数据成员
空类的大小是一个特殊情况,空类的大小为 1,空类同样可以被实例化
左值
右值
使用 = 进行赋值时,= 的左边必须为左值,右值只能出现在 = 的右边。
左值引用
右值引用
左值转换成右值
通过 std::move 可以将一个左值强制转化为右值
std::move() 函数的实现原理
利用万能模板将传入的参数 t 进行处理
通过 remove_refrence 移除引用,得到参数 t 具体的类型 type
最后通过 static_cast<> 进行强制类型转换,返回 type && 右值引用
引用折叠
万能引用类型
forward 的实现
forward 与 move 的区别
指针的定义
指针用法
空指针
C 语言中定义了空指针为 NULL,实际是一个宏,它的值是 0
C++ 中使用 nullptr 表示,特殊类型的字面值,可以被转换成任意其他类型
函数类型到函数指针类型转换
this 指针
指针
引用
常量指针
指针常量
是个常量,常量的值是一个指针
函数指针本质是一个指针变量,指向了一个函数
对于 fun1 和 &fun1
值传递
指针传递
引用传递
从函数调用机制看,不管何种调用所有实参的传入时都在栈中开辟了空间
一种抽象的设计概念,在设计模式中有迭代器模式,即提供一种方法,使之能够依序寻访某个容器所含的各个元素,而无需暴露该容器的内部表述方式
是一种概念上的抽象,具有迭代器通用功能和方法的对象都可以叫做迭代器
5种迭代器模型
优点
悬空指针
野指针
指不确定其指向的指针,未初始化的指针为“野指针”
避免野指针
static_cast
const_cast
const 与非 const转换
volatile 与非 volatile转换
功能
reinterpret_cast
dynamic_cast
NULL:预处理变量,是一个宏,它的值是 0
nullptr:C++ 11 中的关键字,是一种特殊类型的字面值,可以被转换成任意其他类型
nullptr优势
需要重载操作符 == 判断两个结构体是否相等
不能用函数 memcmp 判断结构体是否相等,因为结构体可能会字节对齐,产生垃圾值
创建类或者函数的蓝图或者公式
类型
函数模板与类模板区别
实例化方式
默认参数
特化
调用方式
模板特化
介绍
原因
特化类型
全特化
偏特化
泛型编程基础
泛型编程优点
泛型编程缺点
二进制复用性差
list
list插入操作和结合才做都不会造成原有的list迭代器失效;
list不仅是一个双向链表,而且还是一个环状双向链表,所以只需一个指针
list和vector区别
deque
deque是一种双向开口的连续线性空间,所谓双向开口,意思是可以在头尾两端分别做元素的插入和删除操作;可以在头尾两端分别做元素的插入和删除操作;
deque和vector的差异
接受可变数目参数的模板函数或模板类
可变数目的参数称为参数包,包括
模板参数包
函数参数包
用省略号来指出模板参数或函数参数表示一个包
功能
线程全局变量同步机制
条件变量需要和互斥锁结合
线程从等待已发出信号的条件变量中醒来,却发现它等待的条件未满足
后果
解决办法
互斥
互斥量(支持超时加锁、递归加锁)
功能
mutex 只有锁定(locked)和未锁定(unlocked)两种状态
timed_mutex:在 mutex 的基础上增加了超时加锁的功能
recursive_mutex:在 mutex 的基础上增加了递归加锁的功能
互斥量包装器(基于 RAII 的思想)
共享互斥量
std::shared_mutex 是 C++ 17 标准中引入的,由 unique_lock 和 shared_lock 两个类模板配合 shared_mutex 使用,主要用于读写共享锁
unique_lock 用于写入时加锁
shared_lock 用于读取时加锁
主要用于读写共享锁
多个线程可共享同一个互斥锁的所有权
只有一个线程可以拥有互斥锁
使用场景
互斥量包装器
lock_guard:使用了 RAII 的机制对互斥量进行类模板封装,构造时加锁,析构时解锁
优点
缺点
读写锁(共享互斥量,也支持超时加锁)
条件变量
pthread_cond_wait函数
条件变量是利用线程间共享的全局变量进行同步的一种机制
主要流程
为防止竞争,条件变量需要和互斥锁结合在一起,通常使用std;;mutex或其对应的 RAII 模板类
接口
等待条件成立
condition_variable 类
唤醒信号
condition_variable 类
虚假唤醒以及如何避免虚假唤醒
后果
解决方案
信号量
二元信号量 binary_semaphore
计数信号量 counting_semaphore
barrier
类 latch 是 std::ptrdiff_t 类型的向下计数器,可用于同步线程
barrier与latch类似,会阻塞线程直到所有参与线程都到达一个同步点
生命周期
call_once
std::thread
join()
detach()
std::async
一个相对简单的异步接口
两种启动策略
std::future
提供了一种访问线程异步操作结果的机制
future_status 有三种状态
获取 future 方式
get()
wait()
wait_for()