内存管理+模板引入
- 一.内存管理:
- 1.内存区域划分图:
- 2.区域划分实例:
- 3.C++ 内存管理方式:new+delete
- 4.自定义类型的new和delete:
- 一.简单类:
- 二.日期类:
- 三.栈类:
- 四.队列类(栈实现队列):
- 5.operato new 和 operator delete 函数?
- 5-7:总结:
- 1.对于内置类型操作:
- 2.对于自定义类型
- 3.区别总结:
- 6.底层过程:
- 7.常见面试题目:
- 二.模板引入(泛型编程):
- 0.泛型编程:
- 1.函数模板:
- 2.类模板:
- 4.类模板的举例子!
一.内存管理:
1.内存区域划分图:
C/C++程序中的内存划分
2.区域划分实例:
总结:为什么有这么多的区域?
1.不同的数据变量需要不同的空间去存放:
2.不同的数据对于程序有不同的公用!
3.C++ 内存管理方式:new+delete
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
注意:
1.new和delete对应 :申请和释放单个元素空间 。
2.new 类型[] 和 delete[] 对应 :申请和释放多个元素空间。
3.对于上面的操作其实C语言中的开辟动态开辟也可以做的到那么为什么C++中要加入nwe和delete呢?
4.自定义类型的new和delete:
一.简单类:
1.单个开空间
2.单个的销毁:
3.多个开空间和多个销毁:
二.日期类:
三.栈类:
之前的类的delete都是去调用默认析构函数进行的空间释放因为类型比较简单不会涉及到深浅拷贝的问题,对于栈这个类就不一样了!
四.队列类(栈实现队列):
5.operato new 和 operator delete 函数?
大致概念:
new和delete是用户进行动态内存申请和释放的操作符,
operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
通过日期类和反汇编观察函数执行过程:
1.nwe去调用operator new函数:
2.jmp 命令跳到operator new 全局函数:
3.调用ooperator new 函数进行开辟空间不初始化!
注意观察:
1.operator new 里面调用了malloc函数:
2.本质使用了malloc函数进行空间开辟:
4.delet 调用ooperator delete
5.jmp 到对应operator delete:
6.使用定义operator delete
1.operator delete 里面调用了函数:
2.本质使用了free函数进行空间释放:
5-7:总结:
1.对于内置类型操作:
new和delete malloc和free 没有区别的,可以不用考虑new和delete不对应的情况!因为自定义类型不需要考虑调用多少次析构函数这个问题!
2.对于自定义类型
2-1.对于自定义类型操作:
new和delete malloc和free 有区别的,需要考虑new和delete不对应的情况?
观察一下栈类空间开的大小?
解释开辟多个数据只需要在new的时候去写[n]不需要在delete的时候去写数值,并且对于自定义类型是这样的是因为自定义类型可以知道调用多少次析构函数释放空间。
3.区别总结:
为什么使用operatot new 进行封装呢?
因为malloc 开辟空间失败会返回空指针,operator new 开辟空间失败会抛出异常!
为什么使用operator delete 进行封装呢 ?
1.经过封装可以找到需要调用析构函数的次数并且多次调用析构函数对于自定义类型看多个空间的情况
2.对于单个空间只需要调用一次析构函数就可以了!
6.底层过程:
new
1.调用operator new 进行空间开辟:
2.调用构造函数进行空间的初始化:
delete
1.调用析构函数进行空间的内层释放:
2.调用operator delete 进行空间的外层释放:
补充:
1.operator new 是不可以显示调用的!
2.operator delete 是可以进行显示调用的!
7.常见面试题目:
用法+原理:
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。
二.模板引入(泛型编程):
0.泛型编程:
有没有一种方法可以创造一个模板去使用自定义类型去使用!
那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?
1.函数模板:
1.使用关键字: template
2.放在函数定义的前面可以进行类型的控制了!
3.注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
4.函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器
- 隐式实例化:让编译器根据实参推演模板参数的实际类型.
3.底层原理:
那么这个函数是如何调用的呢?
两个swap会去使用同一个swap函数吗?
通过反汇编去观察具体的情况!
通过观察我们会发现两个swap函数调用去call的函数的参数不同(通过模板和传参确定的),并且地址也不相同说明通过模板和参数的不同实例化了两个swap函数这个操作是编译器自己完成的。
用不同类型的参数使用函数模板时,称为函数模板的实例化。
模板参数实例化分为:隐式实例化和显式实例化。
- 显式实例化:在函数名后的<>中指定模板参数的实际类型
4.优先级问题:
如果存在两个都可以满足的函数编译器优先匹配程度更高的内容
不去实例化就会节省空间:
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
void text()
{
//优先调用处理int的函数不会去用模板进行实例化!
int a,b;
Add(a,b)
double c,d;
Add(c,d)
}
2.类模板:
1.我们之前使用C语言实现的一个栈对于存贮数据类型的控制我们是使用typedef 进行类型的重命名去改变栈中的数据类型的方法!
2.这个方法其实不是很方便比如说我们有一个类需要两个类型数据内容的栈我们如果用之前的方法需要写两份相同的代码只有数据类型不相同。
3.解决:这个时候我们就可以使用类模板去解决这个问题:
4.注意:类的成员函数的定义和声明不可以分离!
4.类模板的举例子!
vector 和 list的使用: