扬州门户网站开发,济南中京网站建设公司,百度承德,wordpress商城案例一、C内存管理 在C语言中我们曾学习过动态内存管理的相关知识#xff0c;通过malloc、calloc、realloc和free等对堆上的空间进行申请和释放。在C中我们同样会面临类似的需求#xff0c;因此C对动态开辟内存的方式进行了一些调整#xff0c;我们可以使用new和delete操作符来对…一、C内存管理 在C语言中我们曾学习过动态内存管理的相关知识通过malloc、calloc、realloc和free等对堆上的空间进行申请和释放。在C中我们同样会面临类似的需求因此C对动态开辟内存的方式进行了一些调整我们可以使用new和delete操作符来对堆空间进行空间管理。 ①首先要明确的是new和delete的使用方法。new操作符后跟随需要开辟空间的类型然后会返回一个指针。delete后跟随动态开辟出的指针并对其指向的空间进行释放。 ②new可以在其后使用方括号来一次性申请多个同类型的空间此时就是使用了操作符new[]。在释放空间的时候也应该对应匹配地使用delete[]来正确合理释放空间。 ③new操作符在开辟空间时可以进行初始化对于内置类型初始化值在类型后使用圆括号表示。对于一次申请了多个同类型空间初始化使用大括号。如果没有大括号则认为没有初始化得到的空间均为随机值如果初始化了但不完全那么没有初始化的部分会默认初始化为0。 ④当new的delete的类型为自定义对象时则会调用其对应的构造函数和析构函数。
class A
{
public:A(int a, int b):_a(a),_b(b){cout A(int, int) endl;}~A(){cout ~A(int, int) endl;}
private:int _a;int _b;
};
int main()
{//对于内置类型int* p1 new int; //动态申请int类型空间int* p2 new int(2); //使用圆括号动态申请的空间进行初始化int* p3 new int[4]; //使用方括号标识申请的数量int* p4 new int[6] {1, 2, 3}; //使用大括号进行初始化不完全初始化下new时使用了大括号未初始化的空间默认为0delete p1; //释放空间使用deletedelete p2;delete[] p3; //释放new[]的空间使用delete[]delete[] p4;//对于自定义类型A* pa1 new A(1, 2); //new自定义类型时会自动调用构造函数进行初始化//A* pa1 new A; //error,因为new自定义类型时会调用构造函数A没有默认构造函数所以会报错A* pa2 new A[4]{ {1,2},{2,3},{3,4},{4,5} }; //new多个自定义类型变量并初始化delete pa1; //delete自定义类型时会自动调用析构函数进行销毁delete[] pa2; //delete[]释放new[]的空间//new的使用不需要进行检查当出错时malloc会持续进行开辟空指针new则会抛异常return 0;
} ⑤使用new申请空间时不需要进行检查在C语言中我们会对malloc返回的指针进行非空检查因为当开辟空间出现问题时new会直接抛异常而不是返回空指针。 ⑥对于new和delete这二者是提供给我们所使用的动态内存申请和释放的操作符而实际上还有操作系统提供的operator new和operator delete两个全局函数。new在使用时调用了operator new来帮助自己进行空间申请同样的delete调用了operator delete来进行释放空间。而operator new和operator delete两个函数的开辟空间和释放空间实际上又是通过调用malloc和free来实现的。 这样一层层的调用看似复杂实际上是这样一层层的包含才有了我们可以方便使用功能完善的new。 同理对于new[]和delete[]而言调用了operator new[]和operator delete[]两个函数。然后由operator new[]和operator delete[]调用了operator new和operator delete。再然后调用了malloc和free来实现。
二、模板 在以往写代码的时候常常会遇到针对不同类型的功能相同的函数这些函数代码相同区别只在于类型不同但却因此要写好几份来满足我们的需求。C中引入模板的概念来很好的解决这个问题。
1. 函数模板 通过提供一个函数模板在后续使用函数的时候只需要标明所需要的类型即可由编译器根据我们所写的模板生成对应的函数。 在定义函数模板时需要使用template关键字然后使用尖括号和关键字typename或class来指定模板参数该参数在使用中会由编译器自动推演为合适的类型。具体的使用也就是函数模板实例化有两种方式由编译器自动推演或者直接显式实例化。 当存在函数的同时又存在模板的情况下若参数匹配则会优先选择函数否则会使用模板。如果采取显式实例化的方式则会强制使用模板。
//函数模板
templatetypename T
void Swap(T a, T b)
{T tmp a;a b;b tmp;
}
//模板和函数可以同时存在调用时优先选择函数
//如果函数不够匹配则使用模板
void Swap(int a, int b)
{int tmp a;a b;b tmp;
}
templateclass T1,class T2 //模板参数可以多个以逗号分隔关键字也可以是class
void func()
{}
int main()
{int a1 10, a2 20;cout a1 a2 endl;Swap(a1, a2); //函数模板实例化int类型 //使用函数cout a1 a2 endl;double d1 1.2, d2 3.7;cout d1 d2 endl;Swap(d1, d2); //函数模板实例化double类型 //使用模板cout d1 d2 endl;Swapdouble(d1, d2); //函数模板显式实例化 //显示实例化强制使用模板cout d1 d2 endl;return 0;
}
2. 类模板 类模板和函数模板非常相似在之后我们会频繁接触到类模板。需要注意的是类模板中的成员函数声明与定义分离时需要在定义处附加模板参数列表并且二者必须在同一文件中。
//类模板
templateclass T
class Stack
{
public:void Push(T x){}
private:T* _a;int _size;int _capacity;
};
int main()
{Stackint st1; //类模板实例化Stackdouble st2;return 0;
}