一、new & delete
回顾malloc/free
, malloc
申请而来的只是内存,严格地说并不是“对象”
// 4个int , 16个字节
int* p = (int*) malloc(16);
for(int i=0; i<4; i++){
p[i] = i;
}
free(p);
在C++中,用new/delete
操作符取代malloc/free
// 申请一个对象 :
int* p = new int;
delete p; // 释放
// 申请多个对象 :例如,申请1024个int对象
int* p = new int [1024] ;
delete [] p;
// 申请一个int, 一个double
int* p1 = new int;
*p1 = 12;
delete p1; // 用完后释放
double p2 = new double(12.34);
printf("%f \n", *p2);
delete p2; // 用完后释放
// 申请一个Student对象
Student* s = new Student;
s->id = 12;
strcpy(s->name, "shaofa");
// 用完之后,释放
delete s;
//申请一个长度为10000的char数组,填充为1,2,3,...
char* arr = new char [10000];
for(int i=0; i<10000; i++)
{
arr[i] = i + 1;
}
delete [] arr;
注意:
- new的返回值直接就是对象指针,不用再转换
- new的时候可以设初始值 new int(12);
- 用new申请的内存,必须用delete释放
- 如果new的时候用了[],则释放的时候使用delete []操作符
和free一样,delete之后指针所指向的内存不再可用
delete p;
p = nullptr; // 最好置为空指针
动态创建对象
对于class
类型,必须用new/delete
来创建、销毁。
malloc/free
是无法胜任的
new:动态创建一个对象:
- 申请一块内存
- 内部调用了构造函数
delete:释放这个对象:
- 调用了析构函数
- 释放内存
// 动态创建一个对象
// new的时候构造函数被调用,delete的时候析构函数被调用
Circle* c = new Circle(); // 默认构造函数
Circle* c = new Circle; // 不加括号也可以
Circle* c = new Circle(1,1,4); // 传构造参数
delete c;
// 动态创建多个对象
// ⚠️ 含有“默认构造函数”的类,才可能使用new []一次性地创建多个对象
Circle* c = new Circle[4];
delete [] c;
new与malloc的最大区别
new和malloc巨大区别:
- malloc返回的只是一片内存、不是对象(malloc不负责构造)
- new返回的是一个可以正常工作的对象(因为它调用了构造函数)
所以,new/delete
不仅仅时申请内存这么简单,它是申请了内存,并且构造了对象
Circle* p = (Circle*)malloc(sizeof(Circle));
// 强转为Circle* ??? 没用,因为没有调用构造函数
double area = p->GetArea(); // !!! 对象不可用
小结
-
new得到的是一个对象,malloc得到的不是对象 。因此,C++的类必须用new,不能用malloc
-
new一个对象的时候,可以传参,内部会调用构造函数。
-
new多个对象(数组)的时候,不能传参数,要求该类必须有默认构造函数
二、用new创建类对象与不用new的区别
- new创建类对象需要指针接收
- new创建类对象使用完需delete销毁
- new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
- new对象指针用途广泛,比如作为函数返回值、函数参数等
- 频繁调用场合并不适合new,就像new申请和释放内存一样
// 1. 使用new创建对象
// new申请的对象,则只有调用到delete时再会执行析构函数,如果程序退出而没有执行delete则会造成内存泄漏
CTest* pTest = new CTest();
delete pTest;
// 2. 不使用new,直接使用类定义声明
// 此种创建方式,使用完后不需要手动释放,该类析构函数会自动执行
CTest mTest;
// 例如:
#include <iostream>
using namespace std;
class A
{
private:
int n;
public:
A(int m):n(m)
{
}
~A(){}
};
int main()
{
A a(1); //栈中分配
A b = A(1); //栈中分配
A* c = new A(1); //堆中分配
delete c;
return 0;
}
第一个隐式调用,第二个显式调用,两者都是在进程虚拟地址空间中的栈中分配内存,而第三种使用了new,在堆中分配了内存,栈中内存的分配和释放是由系统管理,堆中内存的分配和释放必须由程序员手动释放
总结
一般来说对于一个进程栈的大小远远小于堆的大小。
在Window下,栈的大小是2MB,Linux下,默认栈空间大小为8MB。对于堆,比如32位系统最大不超过2G,而64位系统最大不超过4G,当需要分配一个非常大的内存时,请用new
。
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在 堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就需要申请足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
由上可知,一般情况不使用new
,直接使用类定义声明,申请内存特别大
的情况下,或者作为函数返回值
时,使用new
创建对象
网友评论