当前位置: 首页 > 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.dtcms.com/a/286603.html

相关文章:

  • Linux网络:序列化与反序列化
  • web-storage-cache离线缓存技术的详细讲解与使用
  • 虚拟商品自动化实践:闲鱼订单防漏发与模板化管理的技术解析
  • 零售行业 AI 客户咨询对话系统实战指南
  • 深入理解Linux文件操作:stdin/stdout/stderr与C语言文件函数全解析
  • 【实战】一次出口连接数超限事故引发的架构反思:强制代理、NAT 网关与大厂最佳实践
  • 网络编程(modbus,3握4挥)
  • 【C#】引用(Reference)句柄(Handle)
  • 库的制作与原理
  • 退信、延迟、遇攻击?CACTER 邮件安全海外中继:让跨境通邮 “零障碍”
  • 【前后端】沙箱机制
  • gcc 源码分析:从IR-RTL 到汇编输出
  • C++ 程序 AddressSanitizer:DEADLYSIGNAL
  • 自动化面试题
  • spring-cloud微服务部署转单体部署-feign直连调用
  • 磁悬浮轴承系统中由不平衡力引发的恶性循环机制深度解析
  • 初探:C语言FILE结构之文件描述符与缓冲区的实现原理
  • 前端 SSE 实战应用:用最简单的方式实现实时推送
  • Python基础④-装饰器、迭代器及常用函数篇
  • 在断网情况下,网线直接连接 Windows 笔记本和 Ubuntu 服务器进行数据传输
  • 高性能数据库-Redis详解
  • verilog tb文件 美化terminal输出
  • Webpack 项目构建优化详解
  • 雪豹大模型驱动效率革命 华鼎冷链科技重构餐饮供应链神经网络
  • 进程 线程 并发 并行
  • 安达发|从救火到未雨绸缪:APS生产计划排产软件重塑制造业“危机免疫力“
  • 2025年6月电子学会全国青少年软件编程等级考试(Python一级)真题及答案
  • 添加DNS解析记录时,提醒记录冲突是怎么回事?
  • Python练习2-格式化输出基本数据类型及变量的详细使用
  • Aqara 携手西门子西碳迹SiTANJI,发布亚马逊 CPF 绿标解决方案标杆案例