c/c++内存管理和模板
(一)C/C++内存管理
一,c语言动态内存管理
C 语言的动态内存管理是对堆内存的手动分配,使用与释放过程,堆内存提供了灵活的大小控制,但需要开发者完全负责其生命周期管理。所有动态内存函数都定义在<stdlib.h>头文件中,返回值均为void*类型,需要强制转换为具体类型使用。
1,malloc:申请未初始化的内存
| void* malloc(size_t size); |
向堆区申请size字节的连续内存空间,size是需要申请的内存字节数,申请成功返回指向申请内存起始地址的void*指针,需要强制转换为具体类型使用;申请失败返回NULL。申请的内存不会被初始化,内容是随机值。
2,calloc:申请并初始化内存
| void* calloc(size_t size, size_t size); |
向堆区申请num个大小为size字节的连续内存空间,num是元素个数,size是每个元素的字节数,返回值同malloc,申请的内存会被自动初始化为 0,这是与malloc的主要区别。
3,realloc:调整已申请内存的大小
void* realloc(void* ptr, size_t newSize); |
调整ptr指向的动态内存的大小为newSize字节,ptr是之前动态开辟的空间指针,newSize是调整后的新大小(字节)。申请成功返回指向调整后内存的指针,申请分为两种情况(原地扩容和异地扩容),原地扩容是原内存后面有足够的连续空间,直接在原内存基础上扩展,返回原指针;异地扩容是原内存后面没有足够空间,重新申请一块新的更大内存,将原数据复制过去,释放原内存,返回新指针(找到新空间,拷贝旧数据,释放旧空间)。申请失败,则返回NULL,原内存保持不变。如果ptr为NULL,则realloc等价于malloc(newSize)。
二,c++的动态内存管理
1,内置数据类型的动态分配
int* ptr1 = (int*)malloc(5 * sizeof(int));//不能初始化 int* ptr2 = new int[5] {5, 4, 3, 2, 1}; //可以初始化 free(ptr1); ptr1 = nullptr; delete[] ptr2; ptr2 = nullptr;分配数组用new 类型[大小],释放必须用delete[]。若用delete释放数组,只会调用第一个元素的析构函数,导致内存泄漏。new[]会额外存储数组的大小信息,delete[]会根据这个信息调用所有元素的析构函数。如果申请空间失败,就会抛出异常,就不用自己去判断分配空间是否成功了。
2,自定义类型的动态分配
class A{ public: A(int a) :_a(a){ cout << "A(int a)" << endl; } ~A(){ cout << "~A()" << endl; } private: int _a; }; int main(){ A* p1 = (A*)malloc(5 * sizeof(A)); free(p1); p1 = NULL; A* p2 = new A[5]{ 1,2,3,4,5 }; delete[] p2; return 0; }在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,在以上代码中,new 会调用五次构造函数,并初始化,delete会调用五次析构函数。而malloc和free不会调用。
(二)template(模板)
一,函数模板
1,单个模板参数
函数模板是 C++泛型编程的核心工具,允许你编写与类型无关的通用代码,编译器会根据调用时的实际类型自动生成对应的函数版本,极大地减少了代码重复,同时保持了类型安全。函数模板完美解决了这些问题:一份代码,适配所有类型,且编译期进行类型检查。
template<typename T> void Swap(T& x1, T& x2){ T tmp = x1; x1 = x2; x2 = tmp; } int main(){ int i = 3; int j = 7; cout << "交换前" << i << " " << j << endl;//3 7 Swap(i, j); cout << "交换后" << i << " " << j << endl;//7 3 double d1 = 3.5; double d2 = 1.89; cout << "交换前" << d1 << " " << d2 << endl;//3.5 1.89 Swap(d1, d2); cout << "交换后" << d1 << " " << d2 << endl;//1.89 3.5 return 0; }这样我们就不用再写多个交换函数了。
2,多个模板参数
调用时,参数可以是多种类型。
template<class T1,class T2,class T3> double Arithmetic(const T1& x1,const T2& x2,const T3& x3){ return x1 + x2 + x3; } int main() { cout << Arithmetic(1, 3.4, 1.256) << endl; cout << Arithmetic(1, 3, 5) << endl; cout << Arithmetic(2.19, 3.4, 1.256) << endl; return 0; }二,类模板
以下是实现了一个栈的类模板:
#include<iostream> #include<assert.h> using namespace std; namespace zS{ template<class SDatatype> class stack{ public: stack(int n = 4) :_arr(new SDatatype[n]) , _size(0) , _capacity(n) { } void ExpandCapacity(); void Push(SDatatype x); SDatatype stacktop()const; void stackPop(); bool Empty()const; int stackSize()const; ~stack(){ delete[] _arr; this->_size = _capacity = 0; } private: SDatatype* _arr; int _size; int _capacity; }; } template<class SDatatype> void zS::stack<SDatatype>::ExpandCapacity(){ SDatatype* NewCapa = new SDatatype[this->_capacity * 2]; memcpy(NewCapa, this->_arr, this->_capacity * sizeof(SDatatype)); delete[] _arr; _arr = NewCapa; this->_capacity *= 2; } template<class SDatatype> void zS::stack<SDatatype>::Push(SDatatype x){ if (this->_size == _capacity) { ExpandCapacity(); } this->_arr[_size++] = x; } template<class SDatatype> bool zS::stack<SDatatype>::Empty()const{ return _size == 0; } template<class SDatatype> SDatatype zS::stack<SDatatype>::stacktop()const{ assert(!Empty()); return _arr[_size - 1]; } template<class SDatatype> void zS::stack<SDatatype>::stackPop(){ assert(!Empty()); --this->_size; } template<class SDatatype> int zS::stack<SDatatype>::stackSize()const{ return this->_size; }