模板初阶和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 函数模板概念
template<typename T1, typename T2,......,typename Tn>返回值类型 函数名(参数列表){}
template<typename T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}
1.3 函数模板的实例化

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++内存管理方式
//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;
}