侯捷 C++八部曲笔记汇总 - - - 持续更新 ! ! !
一、C++ 面向对象高级开发
1、C++面向对象高级编程(上)
2、C++面向对象高级编程(下)
二、STL 标准库和泛型编程
1、分配器、序列式容器
2、关联式容器
3、迭代器、 算法、仿函数
4、适配器、补充
三、C++ 设计模式
四、C++ 新标准
五、C++ 内存管理机制
1、primitives
2、malloc/free
3、std::allocator
4、other allocators
5、loki::allocator
六、C++ 程序的生前和死后
五、C++内存管理机制 —— 基础工具 primitives(侯捷)
- new、delete 表达式
- 基本用法
- array new、array delete
- placement new
👉 该课程能让你:从平地到万丈高楼!—— 侯捷
👉 做到:心中自有丘壑!
💪 源码之前,了无秘密!
所有内存管理的最终动作都要跑到malloc
去,所以malloc
的效率至关重要。
- 即,最高阶是使用STL中的内存分配器,而STL的内存分配器实现是通过
new
,new[]
,new()
等较为低阶的函数,但终归会到malloc
和free
这两种C函数上来! - 一般不会到最下面操作系统层次,不然程序不具有可移植性!
C++内存使用途径:
C++内存分配的基本工具如下:
🏃 四个层面使用栗子:
void* p1 = malloc(512); //512 bytes
free(p1);
complex<int>* p2 = new complex<int>; //one object
delete p2;
void* p3 = ::operator new(512); //512 bytes (低层就是调用malloc)
::operator delete(p3); //(低层就是调用free)
//以下使用C++ 标准库提供的allocators。
//GNUC2.9 (以下函数为static,定要通过object调用)
void* p4 = alloc::allocate(512);//分配512bytes
alloc::deallocate(p4, 512);
//GNUC4.9 (以下函数为non-static,定要通过object调用)
void* p5 = allocator<int>().allocate(5); //分配5个int
allocator<int>().deallocate((int*)p5, 5);//归还个数也要告诉它
new、delete 表达式
基本用法
⭐️ new
的过程分为三步:
- 分配内存(指定类大小的空间):
new
->operator new
->malloc
; - 将指针转化为对应的类类型;
- 通过指针调用构造函数。
在上图右上角operator new
的源码中:
- 当
malloc
没有成功时(一般很少出现),会调用你设定的函数new handler
, 在这里面释放可以释放的内存,然后再调用malloc
看是否成功; std::nothrow_t& _THROW0()
参数,表示这个函数不抛异常,取而代之的是返回一个空指针,用户通过判断是否为空指针来判断是否分配成功。
⭐️ delete
的过程:
- 先调用析构函数,对象本身的内存就会被释放掉,对象也就死亡了;
- 然后释放内存:
delete
->operator delete
->free
。
构造函数不能被直接调用,析构函数可以被直接调用!
array new、array delete
⭐️ array new
是分配一个对象数组:
- 通常容易犯得一个错误是在
delete
的时候忘记在delete
后面加[]
导致内存泄漏的问题。
正如上图所说的:
- 对于类中没有指针的类,不加
[]
可能问题不大,因为没有指针的类析构函数本来也就没有什么大的作用; - 但是,如果有指针,忘记写
[]
,那么delete
只会触发一次析构函数,delete
掉一个指针指向的内存,其他指针指向的内存就会泄露。- 如上图的
psa
析构,str2
和str3
指向的地址会发生内存泄漏(析构的顺序依编译器而定)。
- 如上图的
使用例子:
没有办法直接调用构造函数设初值,只能绕个弯使用placement new
来设初值,形式如下:
//定点的new
new(tem)A(i); //tem是一个指针
下面两张图分别表示有指针和没指针的对象数组分配内存的区别:
对于分配一个对象数组,他会把数组的大小也放到存放对象数组的内存块的开头:
- 如果在
delete
内存的时候不加[]
,编译器会把他当成一个一般的指针,指向一块对象; - 它按照一块对象的方式来解析布局,但是存储的是3块,整个布局就乱掉了,就会报错!
placement new
用array new
调用的是类的默认构造函数,还需要对数组中的对象进行真正的构造,这就需要placement new
。允许我们将对象分配在已经构建的内存中=。
placement new
就等同于调用构造函数。他不会进行内存分配,具体过程如下:
- 调用重载的
operator new
,和上面的形式不一样,还传入了第二个参数buf
(定点),用于返回已经分配好的内存; - 转型;
- 调用构造函数。