【复习整理归纳】| C++面经(基础概念、线程、进程)
【复习整理归纳】| C++面经(函数相关)
【复习整理归纳】| C++面经(内存管理)
【复习整理归纳】| C++面经(STL及项目)
====》C++【STL】| GNU空间配置器alloc刨析,如何管理内存池(附图解)…
====》C++ |【总结归纳三本书籍系列】一文透彻资源管理,动态内存分配【上】…
====》C++ |【总结归纳三本书籍系列】一文透彻资源管理,动态内存分配【下】…
====》C++【内存管理】| 【智能指针】动态内存管理
====》一文搞懂堆和栈的区别
====》Linux内存分配
- malloc:申请指定
字节数的内存。申请到的内存中的初始值不确定;- calloc:为指定
长度的对象,分配能容纳其指定个数的内存,申请到的内存的每一位(bit)都初始化为 0;- realloc:更改以前分配的
内存长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定;- alloca:在
栈上申请内存,程序在出栈的时候,会自动释放内存。但是需要注意的是,alloca不具可移植性,而且在没有传统堆栈的机器上很难实现。alloca 不宜使用在必须广泛移植的程序中。C99 中支持变长数组 (VLA),可以用来替代 alloca。
# 申请内存,确认是否申请成功
char *str = (char*) malloc(100);
assert(str != nullptr);# 释放内存后指针置空
free(p);
p = nullptr;
- new / new[]:自动计算,先底层
调用 malloc 分配了内存,然后类型转换,在调用构造函数(创建对象);- delete/delete[]:也完成两件事,先调用
析构函数(清理资源),然后底层调用free 释放空间;- new 在申请内存时会
自动计算所需字节数,而 malloc 则需我们自己输入申请内存空间的字节数;- malloc返回void类型,new返回具体类型的指针;
- malloc异常返回nullptr,new通过bad_alloc处理;
3.1 new的种类
3.2 有了malloc为什么还需要new
3.3 被free回收的内存是立即返给操作系统吗?
3.4 new和delete的实现原理, delete是如何知道释放内存的大小的?
3.5 malloc申请的存储空间能用delete释放吗?
3.6 malloc与free的实现原理?
允许我们向 new 传递额外的
地址参数,从而在预先指定的内存区域创建对象;
new (place_address) type
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] { braced initializer list }# place_address 是个指针
# initializers 提供一个(可能为空的)以逗号分隔的初始值列表
========》delete this 合法吗?《========
合法,但:
5.1 只能在堆上
- 方法:将
析构函数设置为私有;- 原因:C++ 是
静态绑定语言,编译器管理栈上对象的生命周期,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性。若析构函数不可访问,则不能在栈上创建对象;
5.2 只能在栈上
- 方法:将
new 和 delete 重载为私有;- 原因:在堆上生成对象,使用 new 关键词操作,其过程分为两阶段:第一阶段,使用 new 在堆上寻找可用内存,分配给对象;第二阶段,调用
构造函数生成对象。将 new 操作设置为私有,那么第一阶段就无法完成,就不能够在堆上生成对象;
========》C++【内存管理】| 【智能指针】动态内存管理《========
7.1 static_cast
- 用于
非多态类型的转换,编译期,转换之间类型之间有联系的;不执行运行时类型检查(转换安全性不如 dynamic_cast);- 通常用于转换
数值数据类型(如 float -> int);- 可以在整个类层次结构中移动指针,
子类转化为父类安全(向上转换),父类转化为子类不安全(因为子类可能有不在父类的字段或方法);向上转换是一种隐式转换;
7.2 dynamic_cast
- 主要用于
多态类型的转换,运行期,支持RTTI类型识别的上下转换;- 执行行运行时
类型检查;- 只适用于
指针或引用;- 对不明确的指针的转换将失败(返回 nullptr),但
不引发异常;- 可以在整个类层次结构中移动指针,包括向上转换、向下转换;
- 由于强制转换为引用类型失败,dynamic_cast 运算符引发 bad_cast 异常;

7.3 const_cast
- 用于删除
const、volatile 和 __unaligned特性(如将 const int 类型转换为 int 类型 );- 模板参数只能是指针或引用;
7.4 reinterpret_cast
- 类似C风格;
- 用于
位的简单重新解释;- 滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。
- 允许将任何指针转换为任何其他指针类型(如 char* 到 int* 或 One_class* 到 Unrelated_class* 之类的转换,但其本身并不安全)
- 也允许将
任何整数类型转换为任何指针类型以及反向转换;- reinterpret_cast 运算符不能丢掉
const、volatile 或 __unaligned特性;- reinterpret_cast 的一个实际用途是在
哈希函数中,即,通过让两个不同的值几乎不以相同的索引结尾的方式将值映射到索引;
8.1 typeid
- typeid 运算符允许在
运行时确定对象的类型;- type_id 返回一个
type_info对象的引用;- 如果想通过基类的指针获得派生类的数据类型,基类必须带有
虚函数;- 只能获取对象的
实际类型;
8.2 type_info
- type_info 类描述编译器在程序中生成的
类型信息; 此类的对象可以有效存储指向类型的名称的指针。- type_info 类还可存储适合比较两个类型是否相等或比较其排列顺序的编码值。;
- 类型的编码规则和排列顺序是未指定的,并且可能因程序而异。
#include
using namespace std;class Flyable // 能飞的
{
public:virtual void takeoff() = 0; // 起飞virtual void land() = 0; // 降落
};
class Bird : public Flyable // 鸟
{
public:void foraging() {...} // 觅食virtual void takeoff() {...}virtual void land() {...}virtual ~Bird(){}
};
class Plane : public Flyable // 飞机
{
public:void carry() {...} // 运输virtual void takeoff() {...}virtual void land() {...}
};class type_info
{
public:const char* name() const;bool operator == (const type_info & rhs) const;bool operator != (const type_info & rhs) const;int before(const type_info & rhs) const;virtual ~type_info();
private:...
};void doSomething(Flyable *obj) // 做些事情
{obj->takeoff();cout << typeid(*obj).name() << endl; // 输出传入对象类型("class Bird" or "class Plane")if(typeid(*obj) == typeid(Bird)) // 判断对象类型{Bird *bird = dynamic_cast(obj); // 对象转化bird->foraging();}obj->land();
}int main(){Bird *b = new Bird();doSomething(b);delete b;b = nullptr;return 0;
}
11.1 变量
11.2 函数
11.3 初始化和赋值的区别
避免:
stl分配器有两级:一级:当申请大小大于128byte时使用;二级:当小于128byte时使用;
一级分配器:使用malloc、realloc:设置new_handler二级分配器:一个链表内有16个节点,从8字节到128字节(内存对齐);每个节点有一个嵌入式指针,用来挂载内存链表;【申请】:当申请一个内存时,会先经过1、内存对齐2、在相应的链表下查看是否有内存可以使用;【则查看内存池是否有内存】,若有,则尝试是否满足20个区块的大小,若不行,则递归尝试数量减少;若减少后也不满足,则将该内存池的碎片,切割挂到(碎片/8 - 1)节点上;【若内存池位空】:则申请按20 * 2 * byte * (申请余量%16),将前面20个进行切割挂在该节点下供该节点,第一个区块分配给客户使用,剩余交给内存池;并记录申请量;【若申请不到内存】:则从离该节点最近的下一个节点上拿取内存,将该节点上的区块切割出来,余量给内存池; 【若有】:则直接分配给客户;