C++函数模板与内存管理详解

简介: C++函数模板与内存管理详解

情景引入:

现在假设我们要写一个swap函数交换两个值的大小,我们知道在之前讲过的函数重载是支持不同类型的参数重载的,因此我们只要写出int ,double ,char ,float ,bool的类型函数重载就行了,但是这个函数的功能差距不大,就因为类型不同我们就要写这么多,这显然不划算,因此C++里面提出了一种方法,支持模糊的类型匹配————模板



一:模板

我们前面知道模板支持模糊匹配,但是这并不代表模板的类型匹配不严格,相反模板里面是不允许类型的隐式转化(除非显示标注类型),从某种意义上来说,模板的类型匹配更加严格,那这是什么情况呢?看起来我们确实利用函数模板偷懒了很多,其实真正意义上并没有偷懒,只是我们偷的懒被系统做了而已。我们先看下面这个模板

template<class A,typename B>//类模板的定义
void swap(A& a,B& b){
A temp=a;             
a=b;
b=temp;
}          出了作用域若想要再次使用模板就需要重新定义,
            模板只管模板定义处的下一行或者下一个作用域

那么编译器到底是怎么帮我们偷懒的呢?在我们没有使用这个函数的时候是并没有这个函数的,当我们使用的时候编译器通过对类型的辨认如何形成这个函数,在通过这个函数去进行操作。

现在有这么一个函数的模板

template<typename B>
void swap(B& a,B& b){
B temp=a;             
a=b;
b=temp;
}

如果我们传一个这个参数过去会怎么样呢?

int a=1;
double b=0.0;
swap(a,b);

编译器会帮我们成功的交换吗?事实是编译器会报错,这是因为只有一个模板却有两个类型,有些情况就是这样的那我们怎么办呢?当然有办法,那就是使用的时候显示指定类型,如下

int a=1;
double b=0.0;
swap<int>(a,b);
swap<double>(a,b);

二:内存管理

1)内存分布

在C++中内存大概可以分为五块

向下生长的栈空间:栈是用来储存局部变量和函数的空间,因为使用的时候,地址是越来越小,所以称为向下生长,因为这一块空间用完了它就会向比它地址低的空间去申请,并且栈只支持静态增长。

向上生长的堆:堆是支持动态增长的,像malloc,calloc,realloc,new的空间都是在这里开辟的,这里的空间直到程序结束才会释放,因此需要我们手动释放开辟的空间,但要注意释放空间不能多次释放。使用这里的空间地址是越来越大的,因为这一块空间用完了它就会向比它地址高的空间去申请

内存映射段:内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

数据段:存储全局数据和静态数据

代码段:存储可执行代码和只读常量

2)new和delete

在C++中new和delete是类似与C里面的malloc和realloc还有calloc的开辟空间的手段

区别:new和delete是内置基本操作符,malloc等是函数,

new=开辟空间+调用构造函数

delete=调用析构函数+释放空间

malloc=开辟空间

free=释放空间

new,malloc,delete,free能混着用吗?

答案:不能,因为可能会造成内存泄漏,new的类里面可能还有在堆上开辟的空间,如果不调用析构函数释放会造成内存泄漏

有些人就要问了,为什么有了malloc和realloc和calloc还有发明new?

那我们想象一下,我们创建了一个类,我们使用malloc的方式给它开辟空间,我们该如果给它初始化呢?

这就暴露的malloc等的一个大弊端,malloc,calloc是无法给自定义类型初始化的,new应运而生,new支持对自定义类型的初始化。我们看代码,里面有从基本内置类型到类的new空间基本操作

int* a=new int;//只开辟空间,不初始化
int* a=new int(6);//给a开辟空间并且初始化为6
delete a;//释放空间
int* a=new int[6];//给a开辟一个数组大小为6
int* a=new int[6]{1,2,3,4,5,6};//给a开辟一个数组大小为6,并且全部赋值
int* a=new int[6];//数组里面全是随机值
int* a=new int[6]{};//数组里面全是0
delete[] a; //释放空间
Date a=new Date{2023,11,11};//日期类并且赋初值
delete a;释放空间
Date a[3]=new Date[3]{(2023,11,11),(2024,11,11),(2025,11,11)};//自定义类型数组赋初值
delete[] a;

看到上面,你有没有想为什么数组释放空间需要[],这个真的毫无用处吗?

错,首先我们想一下,编译器是如何判断这个数组有多大的空间,如果是你你会怎么做?编译器在开辟空间会多开四个字节,正好一个int大小,这个int用来储存数组的数量,通过这个int我们就可以准确的释放数组空间,这个int类型一般在开辟空间的前面,这样就可以快速准确拿到数组大小,释放空间,如果我们不使用[]这个,那么空间就会从中间被释放,导致报错。

3)简单介绍new和delete底层原理。

在new和delete的底层原理其实就是调用了被封装的malloc和free,malloc被封装在new的函数重载里面,free被封装在delete的函数重载里面,为什么要封装呢?这是因为malloc分配空间出错是直接返回0,而C++的要求的抛错,为了达到这个效果,必需封装一下malloc和free,并且不要忘记new里面会调用构造函数delete里面会调用析构函数。new的函数重载只负责开辟空间,delete的函数重载只负责释放空间,调用析构函数和构造函数还是在new运算符里面。

new=malloc+调用构造函数

delete=free+释放空间

malloc=开辟空间

free=释放空间

相关文章
|
3天前
|
存储 编译器 C语言
C++中的内存管理
C++中的内存管理
15 0
|
3天前
|
存储 编译器 C语言
C语言:字符函数 & 字符串函数 & 内存函数
C语言:字符函数 & 字符串函数 & 内存函数
11 2
|
4天前
|
自然语言处理 编译器 C语言
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
本文章是我对C++学习的开始,很荣幸与大家一同进步。 首先我先介绍一下C++,C++是上个世纪为了解决软件危机所创立 的一项面向对象的编程语言(OOP思想)。
30 1
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
|
4天前
|
存储 算法 编译器
C++的模板与泛型编程探秘
C++的模板与泛型编程探秘
9 0
|
5天前
|
编译器 C++
【C++从练气到飞升】08---模板
【C++从练气到飞升】08---模板
|
5天前
|
存储 Linux C语言
【C++从练气到飞升】07---内存管理
【C++从练气到飞升】07---内存管理
|
5天前
|
存储 算法 对象存储
【C++入门到精通】function包装器 | bind() 函数 C++11 [ C++入门 ]
【C++入门到精通】function包装器 | bind() 函数 C++11 [ C++入门 ]
14 1
|
5天前
|
算法 编译器 C++
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
22 1
|
5天前
|
存储 算法 数据安全/隐私保护
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
7 0
|
6天前
|
存储 自然语言处理 C++
刷题用到的非常有用的函数c++(持续更新)
刷题用到的非常有用的函数c++(持续更新)
14 1