一、内存管理
实例分析
二、堆和栈
- 对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制
- 对于栈来讲,生长方向是向下的,也就是向着内存地址减小的方向;对于堆来讲,它的生长方向是向上的,是向着内存地址增加的方向增长
- 对于堆来讲,频繁的
new/delete
势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题 - 一般来讲在 32 位系统下,内存可以达到4G的空间,并不会把所有空间分配给堆空间,但是对于栈来讲,一般都是有一定的空间大小的
- 在堆上频繁的调用
new/delete
容易产生内存碎片,栈没有这个问题 - 只有栈可以静态分配,堆和栈都可以动态分配
三、malloc和new
从属性上来讲:
new/delete
是C++关键字,需要编译器支持。malloc/free
是库函数,需要头文件支持。
从参数上来讲:
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
从返回类型来讲:
new符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new
是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*
指针转换成我们需要的类型。
从分配失败来讲:
new内存分配失败时,会抛出bac_alloc异常。malloc
分配内存失败时返回NULL。
从自定义类型来讲:
new
会先调用operator new
函数,申请足够的内存(通常底层使用malloc
实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete
先调用析构函数,然后调用operator delete
函数释放内存(通常底层使用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
特性分析
使用 char* p = new char[100]
申请一段内存,然后使用delete p释放,有什么问题?
会在程序编译过程中报错,new char[]
过程中系统会在申请内存前面开辟4个字节的空间用来存储调用了多少次构造函数,并返回更新之后的地址。delete时不是利用delete [] p
,会导致free
时访问的地址出错。
析构函数调用顺序
C c;
void main()
{
A*pa=new A();
B b;
static D d;
delete pa;
}
调用的顺序依次是A,B,D,C
程序结束会访问从下往上调用,B在D前面的原因是,D静态类型,C是全局类型,且在最上面
模板
- 可用来创建动态增长和减小的数据结构,但并不是必须使用模板的原因
- 它是类型无关的,因此具有很高的可复用性
- 它运行时检查数据类型,保证了类型安全
- 它是平台无关的,可移植性
- 编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
模板分为函数模板和类模板
- 类模板中的成员函数全是模板函数
- 类模板与模板类所指的不是同一概念,类模板是一个模板,是类的抽象,模板类是一个类,用类模板生成的类
- 函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具
函数模板
template<class T>
T add(const T& left,const T& right){
return left + right;
}
int main(){
int a1=10;
double b1=10.0;
//add(a1,b1);
add(a1,(int)b1);//强制类型转换
return 0;
}
类模板
template<class T1,class T2,...,class Tn>
class 类模板名{
//类内成员定义
};
例如使用类模板实现不同的动态顺序表
template<class T>
class Vector{
public:
Vector(size_t capacity=10):_pData(new T[capacity]),_size(0),_capacity(capacity)
{}
//使用析构函数演示:在类中声明,在类外定义。
~Vector();
size_t Size(){
return _size;
}
T& operator[](size_t pos){
assert(pos<_size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
//注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
Vector<T>::~Vector(){
if(_pData)
delete[] _pData;
_size = _capacity = 0;
}
Vector
是虚拟类型名,<>
里面有了类型合起来才是一个实例化的类
Vector<int> s1;
Vector<double> s2;