今天我们来学习有关C++的内存管理。
一。首先让我们先回忆一下C语言中的内存管理
1.在C语言中,我们经常用malloc、calloc以及realloc。
举例:
void Test ()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = {1, 2, 3, 4};
char char2 [] = "abcd";
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 );
}
(1)malloc只是开辟空间没有初始化、calloc在开辟空间的同时也进行了初始化为0。realloc可以扩大空间,修改已经开辟好的空间大小。
(2)在开辟空间的同时,必须记得要释放空间,否则会产生内存泄漏。内存泄漏简单来说就是用户向系统借来了空间,最后结束时就必须还给系统,若不还,会导致程序崩溃。
二。C++内存管理
1.在C++中,会用new和delect来进行动态内存管理。
(1)new/delete动态管理对象。
(2)new[]/delete[]动态管理对象数组。
(3)举例:
void Test ()
{
// 动态分配4个字节(1个 int)的空间单个数据
int* p4 = new int;
// 动态分配4个字节(1个 int)的空间并初始化为3
int* p5 = new int(3);
// 动态分配12个字节(3个 int)的空间
int* p6 = new int[3];
delete p4 ; //只要用new开辟空间,就一定要用delete释放空间。
delete p5 ;
delete[] p6 ;
}
2。new/delete操作符/操作符new/delete。
(1) 操作符new与delete 操作符new/delete是一个函数:
void * operator new (size_t size);
void operator delete (void * );
void * operator new [](size_t size);
void operator delete[] (void*);
(2)他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象> ,实际operator new 和operator delete只是malloc和free的一层封装。
(3)
new做了两件事 1. 调用operator new分配空间。 2. 调用构造函数初始化对象。
delete也做了两件事 1. 调用析构函数清理对象 2. 调用operator delete释放空间
new[N] 1. 调用operator new分配空间。 2. 调用N次构造函数分别初始化每个对象。
delete[] 1. 调用N次析构函数清理对象。(思考这里怎么N是怎么来的?) 其实是编译器在new出的数组对象头指 针位置向前4个字节中记录了对象个数(4个字节对应一个int值),结合内存变化观察下这种情况。 2. 调用operator delete 比释放空间。
3.定位new表达式
(1)已分配的原始内存空间中调用构造函数初始化一个对象。
(2)函数。
new (place_address) type
new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表。
(3)调用:
allocator alloc;
T * ptr1 = alloc.allocate(1);//从池对象中申请对象对应的空间, T为对象类型 。
new(ptr1)T;//显式调用T类型的构造函数进行初始化
ptr1->~T();
alloc.deallocate(ptr1,1)
(4)举例:
class stu
{
public:
stu()
{
cout<<"stu()"<<a<<endl;
}
~stu()
{
cout<<"~stu()"<<endl;
}
private:
int* a;
};
int main()
{
allocator<stu> alloc;
stu * ptr1 = alloc.allocate(1);//从池对象中申请对象对应的空间, stu为对象类型
new(ptr1)stu;//显式调用stu类型的构造函数进行初始化
ptr1->~stu();
alloc.deallocate(ptr1,1);
system("pause");
return 0;
}
(5)应用场景:多用于对象池化处理。最常见的就是内存池技术。 对象池化:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频 繁创建对象所造成的开销。用于充当保存对象的“容器”的对象,被称为“对象池”。
4.new/delete与malloc/free的区别与联系:
(1)它们都是动态管理内存的入口。
(2)malloc/free是C/C++标准库的函数,new/delete是C++操作符。
(3)malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进 行初始化与清理(清理成员)。
(4)malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回对应类型的指针。
三。特殊要求的类的设计:
在设计之前我们要先知道一件事:
在C++中,类的对象建立分为两种,一种是静态建立,如A a;另一种是动态建立,如A* ptr=new A;这两种方式是有区别的。
(1)静态建立一个类对象,是由编译器为对象在栈空间中分配内存,是通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数。
(2)动态建立类对象,是使用new运算符将对象建立在堆空间中。这个过程分为两步,第一步是执行operator new()函数,在堆空间中搜索合适的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间。这种方法,间接调用类的构造函数。
1.一个类只能在堆上实现创建。
(1)分析:因为只能在堆上实现,所以当我们把析构函数设为私有成员后,编译器就不会再使用构造函数创建对象,所以此时只能用new在堆上创建。
class stu
{
public:
stu()
{
cout<<"stu()"<<endl;
}
private:
~stu()
{
cout<<"~stu()"<<endl;
}
private:
int a;
};
int main()
{
//stu a; //无法直接初始化,因为析构函数为私有的。
stu* b=new stu;
system("pause");
return 0;
}
这样就可以在堆上创建了,但是这也有一个问题,那就是没有释放,所以还可以优化为:
class stu
{
public:
static stu* creat()
{
stu* b=new stu;
b->a=10;
cout<<"stu()"<<endl;
return b;
}
static void destory(stu* b)
{
if(b)
{
delete(b);
b=NULL;
}
cout<<"~stu()"<<endl;
}
int a;
private:
stu(){};
~stu(){};
};
2.只能在栈上实现创建类。
(1)分析:要想实现只在栈上创建,那就要让new函数不能只用,所以将new函数设为私有函数即可。
(2)
class A
{
public:
A()
{
cout<<"Creat OK!"<<endl;
}
~A()
{
cout<<"delete ok!"<<endl;
}
private:
void* operator new(size_t size);
void operator delete(void *p);
};
3.请设计一个类,该类只能创建一个对象。
(1)弄一个判别函数和一个静态成员变量,判别函数判别那个变量是否重复,若重复直接返回,否则直接返回那个变量。
(2)
class AA
{
public:
static AA* get()
{
if(a==NULL)
{
a=new AA();
}
return a;
}
static void destroy()
{
if(a!=NULL)
{
delete(a);
}
a=NULL;
}
private:
AA()
{
cout<<"++++++++++"<<endl;
}
static AA* a;
};
AA* AA::a=NULL;
以上就是C++的内存管理。