当前位置: 首页 > news >正文

模板初阶和C++内存管理

一. 函数模板

1.1 泛型编程

模板是C++语言中很重要的一部分,再介绍模板时候我们先来引入之前学过的交换函数:

void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}
void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}

如上面的代码所示,仅仅是类型不一样但是我们却要写三个很相似的函数,其实操作是比较麻烦的,这时候就引入了模板,就是为了方便类似这样的函数,有一个模板可以套用的话,是不是会简单很多?这时候可能大家会想到使用函数重载,使用函数重载虽然可以实现,但是有一下几个不好的地方: 1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数 2. 代码的可维护性比较低,一个出错可能所有的重载均出错

1.2 函数模板概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生
函数的特定类型版本。注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
template<typename T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。
所以其实模板就是将本来应该我们做的重复的事情交给了编译器,在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

1.3 函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化
和显式实例化。
1. 隐式实例化:让编译器根据实参推演模板参数的实际类型

2. 显式实例化:在函数名后的<>中指定模板参数的实际类型 

int main(void)
{
int a = 10;
double b = 20.0;
// 显式实例化
Add<int>(a, b);
return 0;
}

二. 类模板

类模板的定义格式:

template<class T1, class T2, ..., class Tn>
class 类模板名
{
     // 类内成员定义
};

// Stack是类名,Stack<int>才是类型
Stack<int> st1; // int
Stack<double> st2; // double

同时在声明Push函数的时候我们不能直接使用Stack类名来当作类型,必须加上模板参数<T>,这一点需要自己额外记住。 

三. C++内存管理

3.1 内存分布

在学习C++的同时,掌握好它的内存空间是如何分配也是非常重要的,整个空间分为栈、堆、静态区(数据段)、常量区(代码段)

  • 栈(Stack):存储函数的局部变量、函数参数、返回地址等,生命周期随函数调用结束而销毁。
  • 堆(Heap):动态分配的内存(malloc/calloc/realloc 申请的内存),需要手动 free 释放,生命周期由程序员控制。
  • 数据段(静态区,Data Segment):存储全局变量和静态变量(static 修饰的变量),程序运行期间一直存在。
  • 代码段(常量区,Code Segment):存储程序的代码和只读常量(如字符串字面量 "abcd"),内容不可修改。
#include <algorithm>
using namespace std;
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{static int staticVar = 1;int localVar = 1;int num1[10] = { 1, 2, 3, 4 };char char2[] = "abcd";const char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof(int) * 4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);
}

对于上面的代码,大家能够准确的说出每一个变量都存储在哪里吗?

 3.2 C语言中动态内存管理方式:malloc/calloc/realloc/free 

void Test ()
{
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
free(p3 );
}

在回答代码中问题的时候,我先为大家解释清楚malloc、calloc、realloc的区别以及各自的作用。首先三者的作用分别是:malloc的原型是void* malloc(size_t size),例如(int*)malloc(5*sizeof(int));指的是分配5个int大小的空间,特点是分配指定大小(字节)的内存块,不初始化内存(内容为随机值)calloc的原型是void* calloc(size_t num,size_t size),例如(int*) calloc(5,sizeof(int));指的是分配5个int大小的空间,特点是分配num个元素、每个元素大小为size的内存块,自动初始化为 0。realloc的原型是void* realloc(void* ptr,size_t num),例如(int*) realloc(ptr,10);指的是把内存扩展为10个int大小,特点是调整已分配内存块的大小(扩大或缩小),可能移动内存位置。若原内存后空间不足,会重新分配并复制原内容

我们来回答为什么第二个问题,上述代码中还需要free(p2)吗?答案是不需要当 realloc 成功扩大内存时,若原内存块后空间充足,会直接在原地址上扩展,此时 p3 与 p2 指向同一地址。若原内存后空间不足,realloc 会在新位置分配内存,并自动复制原内容,然后释放原内存块(即 p2 不再有效),此时 p3 指向新地址。

3.3 C++内存管理方式

1. C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理
//new和delete
void Test()
{//动态申请一个int类型的空间int* ptr4 = new int;//动态申请一个int类型的空间并初始化为10int* ptr5 = new int(10);// 动态申请10个int类型的空间int* ptr6 = new int[10];delete ptr4;delete ptr5;delete[] ptr6;
}

在使用的过程中要注意如果是单独申请和释放的空间,使用new和delete操作符。申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。

2.  new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间,还会调用构造函数和析构函数。

#include <iostream>
using namespace std;
class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
int main()
{// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间,还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(1);free(p1);delete p2;//内置类型是几乎是一样的int* p3 = (int*)malloc(sizeof(int)); // Cint* p4 = new int;free(p3);delete p4;A* p5 = (A*)malloc(sizeof(A) * 10);A* p6 = new A[10];free(p5);delete[] p6;return 0;
}

 3.3.1operator new与operator delete函数

简单了解就是new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
http://www.lryc.cn/news/592574.html

相关文章:

  • 【软件重构】如何避免意外冗余
  • 高速公路自动化安全监测主要内容
  • A33-vstar报错记录:ERROR: build kernel Failed
  • 深入理解Linux文件I/O:系统调用与标志位应用
  • 广东省省考备考(第四十九天7.18)——判断推理:位置规律(听课后强化训练)
  • *SFT深度实践指南:从数据构建到模型部署的全流程解析
  • Linux | Bash 子字符串提取
  • Redis原理之哨兵机制(Sentinel)
  • Android性能优化之网络优化
  • 【锂电池剩余寿命预测】TCN时间卷积神经网络锂电池剩余寿命预测(Pytorch完整源码和数据)
  • 如何用Python并发下载?深入解析concurrent.futures 与期物机制
  • 安卓Android项目 报错:系统找不到指定文件
  • python学智能算法(二十四)|SVM-最优化几何距离的理解
  • 【52】MFC入门到精通——MFC串口助手(二)---通信版(发送数据 、发送文件、数据转换、清空发送区、打开/关闭文件),附源码
  • 『 C++ 入门到放弃 』- set 和 map 容器
  • Java Web项目Dump文件分析指南
  • 开源Docmost知识库管理工具
  • spring-cloud微服务部署转单体部署-feign直连调用
  • Windows Server 版本之间有什么区别?
  • 在断网情况下,网线直接连接 Windows 笔记本和 Ubuntu 服务器进行数据传输
  • 华为业务变革项目IPD基本知识
  • 【HCI log】Google Pixel 手机抓取hci log
  • 京东店铺入鼎的全面分析与自研难度评估
  • 70 gdb attach $pid, process 2021 is already traced by process 2019
  • CCF编程能力等级认证GESP—C++4级—20250628
  • 协作机器人操作与编程-PE系统示教编程和脚本讲解(直播回放)
  • 自动化面试题
  • 搜广推校招面经九十五
  • 基于 WinForm 与虹软实现人脸识别功能:从理论到实践
  • 关于我用AI编写了一个聊天机器人……(11)