首先我们先要了解,动态内存是在堆上分配的,内存大小可以变化。动态内存的分配很好的解决了传统静态内存分配不能动态增长的缺陷(静态内存分配是在栈上,由系统自动释放)而且栈来说相对较小(为什么栈较小,假如一个函数递归死循环,会产生栈溢出,如果栈小很快就会栈溢出停下来,而如果栈很大的话,就要很久才会出错)
一 C语言中的动态内存管理
void *malloc ( size_t n)---------------向系统申请分配指定size个字节的空间 如果分配成功,则返回指向被分配内存的指针。如果分配失败,将返回空指针
void* calloc (size_t num,size_t size)-----------------------num元素数量,size每个元素的长度(以字节为单位)
void* realloc(void* ptr,size_t size)--------------------------改变以前分配的内存块的大小,可以使用realloc调整以前由malloc或者calloc分配的内存(也就是改变ptr所指这个内存块的大小) 1. 如果ptr为空,相当于malloc 2.realloc不对增加的内存块进行初始化。3.如果第二个参数如果为0,则表示释放原来的空间。4.如果不能扩大内存块,就返回NULL
,而且保持原来的数据不动
总结以上三者的区别:
malloc不会对开辟的空间进行初始化。
calloc会对开辟的空间进行初始化。
realloc有三个作用1.ptr为空,相当于malloc 2.重新开空间 ,返回值不是ptr 3.扩容,返回值依旧是ptr
注意动态开辟出来的内存,一定要释放,否则会造成内存泄漏
我们知道C++是兼容C语言的,因此在C++中我们当然可以用malloc,calloc,realloc函数来动态开辟内存,但是C++中我们还引出了new, new[],delete,delete[].但是需要注意new,delete等不是函数而是操作符。
4.malloc/free需要手动计算类型大小且返回值为void*,new和delete可自己计算类型的大小,返回对应类型的指针注意:int char double这些内置类型有构造函数吗?C语言中没有,因为C语言中都没有构造函数这个概念 。C++中有,但是不能重写。怎么证明呢?
void Test ( )
{
int* p1 = new int;//代表动态分配,四个字节(1个int)的空间 没初始化
int* p2 = new int ( 3 );//代表动态分配4个字节,(一个int)的空间并且初始化为3
int* p3 = new int[3];//动态分配12个字节,(3个int)的空间 没初始化
delete p1;
delete p2;
delete[] p3;
}
切记上面这四个函数可不是new,delete运算符的重载。怎样证明
int main ( )
{
double *p = new double;
//int*p = (int*)new(4);//如果是运算符重载,则说明可以这样用。但是经过验证 这样编译都通过不了,因此不是运算符的重载
int *p1 = (int*)operator new(4);
system ( "pause" );
return 0;
}
我们发现上面这些函数和malloc和free的用法及其相似。其实C++中动态申请内存的函数是operator new 和operator new [] 有同学肯定会说动态申请不是new 吗,其实new /delete只是动态申请内存的操作符,实际上调的是operator new 和operator delete等
思考:C++既然兼容了C,那么已经有了C库的malloc和free等来动态管理内存,为什么还要new/delete运算符来动态管理内存。也就是说为什么可以调malloc和free但是却还要有new /delete去调operator new /operator delete等
总结:1.operator new/operator delete/operator new[]/operator delete[]和malloc,free的用法一样
。。new做了两件事
class AA
{
public:
AA ( )
{
cout << "A()" << endl;
_a = new int[10];
}
/*~AA ( )
{
cout << "~A()" << endl;
delete[] _a;
}*/
int* _a;
};
int main ( )
{
//AA* p1 = new AA;//调构造函数和析构函数,自定义类型new创建就被初始化,delete销毁前每个对象被清理
//delete p1;
//free ( p1 );//不崩溃 会存在内存泄漏,掉了构造,没有调用析构,如果析构涉及对象资源清理,便会发生内存泄漏
//delete[] p1;//崩了 越界访问
//delete[] p1;//屏蔽析构,程序不崩溃 会发生内存泄漏
AA* p3 = new AA[10];
//free ( p3);//崩了 屏蔽掉析构不会崩溃 存在内存泄漏
//delete p3;//崩了 屏蔽掉析构不会崩溃 存在内存泄漏
//int* p4 = new int;
//delete[] p4;//没崩
//int* p5 = new int[10];
//delete p5;
//delete[]p5;
//free ( p5 );
/*AA* p2 = (AA*)malloc ( sizeof(AA) );
free ( p2);*/
system ( "pause" );
return 0;
}
//class AA
//{
//public:
// AA ( )
// {
// cout << "AA()" << endl;
// }
// ~AA ( )
// {
// cout <<" ~AA ( )" << endl;
// }
//
//
//};
//int main ( )
//{
// AA* p1 = new AA;
// delete p1;
// AA * p2= (AA*)operator new(4);//没调构造,析构 几乎相当于malloc
// operator delete (p2);
// try{
// char* p3 = (char*)operator new(0x7fffffff);
// printf ( "%p", p3 );//抛异常
// }
// catch ( exception e )
// {
// cout << e.what ( ) << endl;
// }
// char* p4 = (char*) malloc(0x7fffffff);
// printf ( "%p", p4 ); //返回错误码
// system ( "pause" );
// return 0;
//}
//C语言是一个面向过程的语言返回错误码
//面向对象错误抛异常
//class AA
//{
//public:
// AA ( )
// {
// cout << "AA()" << endl;
// }
// /*~AA ( )
// {
// cout << " ~AA ( )" << endl;
// }
//*/
//private:
// int _a;
//};
//int main ( )
//{
// AA*p0 = new AA;
// //free ( p0 );//没有掉析构,不会崩溃 可能会有内存泄漏
// //delete[] p0;//出现越界 new单个对象,没有多开4个字节,delete[]会误以为p0开辟了一个数组,会让当前指针向前偏移4个字节,去取它的值。一往前偏移越界
//
// AA*p1 = new AA[10];
// //free ( p1 );//崩溃 new[]会多开四个字节,free没有释放前面那四个字节
// //delete p1;//崩溃 掉了一次析构不会导致崩溃,导致崩溃是因为没有释放前面那四个字节 掉operator delete不给多传前面那四个字节,释放错误 operator delete[]会多传前面四个字节
//
//
// //delete p1;//屏蔽掉析构,没有写,编译器会自动生成一个,编译器会识别到它的析构不会清理。编译器就不掉析构了。 当然就不用再多开那四个字节 但是如果你写了,就一定要调
// //free(p);//屏蔽掉析构,和上面一样。
//
// int* p2 = new int[10];
// delete p2;//不崩溃 如果内置类型不会崩溃 不会多开四个字节,多开是为了知道后面要调多少次析构函数 int不需要调析构 因此不会崩溃
//
// //严格来说new[]一定要多开四个字节,记录个数方便知道delete[]调几次析构函数 但是编译器会优化,编译器识别到你的构造函数可调可不掉,那就不掉了,int析构函数不会清理资源,因此就不掉了,也就不会多开四个字节
// system ( "pause" );
// return 0;
//}