1.定义
- 在c语言中,关于动态内存分配的方式有malloc,calloc,realloc,释放内存的话有free等方式。而在c++中,提供了一种新的更加完善和方便的动态内存的分配和释放的方式,即new和delete。
- new的格式:
- 指针类型 指针变量名 = new 类型
- 指针类型 指针变量名 = new 类型(初始值)
- 指针类型 指针变量名 = new 类型[元素个数]
- delete的格式:
- delete 指针变量名
- delete[] 指针变量名
2.new和delete对内置类型的操作
//申请空间
int* ptr = new int;
//申请空间并初始化
int* ptr2 = new int(1);
//申请连续的空间,空间大小为4*10=40
int* arr = new int[10];//c++98不允许连续空间初始化
//释放单个空间
delete ptr;
delete ptr2;
//释放连续的多个空间
delete[] arr;
3.new和delete对自定义类型的操作
class A
{
public:
A(int a=10)
:_a(a)
{
cout<<"A()的初始化:"<<_a<<endl;
}
~A()
{
cout<<"~A()的析构"<<endl;
}
private:
int _a;
};
int main()
{
A* pa1=new A;
delete pa1;
return 0;
}
(我们可以看到,new和delete会分别调用构造函数和析构函数,而malloc和free则不会)
- 如果没有默认构造函数,我们必须在new一个对象时后面要加小括号给予初始值进行初始化。没有默认构造函数,我们也不能申请连续的多个空间。
class A
{
public:
A(int a)//不是默认构造函数
:_a(a)
{
cout<<"A()的初始化:"<<_a<<endl;
}
~A()
{
cout<<"~A()的析构"<<endl;
}
private:
int _a;
};
int main()
{
//因为没有默认构造函数,所以报错
A* pa1=new A;
delete pa1;
//可以运行
A* pa2=new A(10);
delete pa2;
return 0;
}
- 如果类中有多个成员变量,也只要把我们想要赋给对象的值放在小括号里,用逗号隔开。
class A
{
public:
A(int a=10,int b=20,int c=30)//不是默认构造函数
:_a(a)
,_b(b)
,_c(c)
{}
private:
int _a;
int _b;
int _c;
};
int main()
{
//使用传入的值
A* pa1 = new A(10, 20, 30);
delete pa1;
//使用缺省参数
A* pa2 = new A;
delete pa2;
//全部使用缺省参数
A* arr = new A[10];
delete[] arr;
return 0;
}
- 注意:new,delete,new[],delete[],malloc和free建议成对使用。
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
A* p2 = new A[10]; //会报错,size的大小会是40或者44
free(p2);
return 0;
}
(当类型是内置类型的时候,相互使用并不会有影响,但是是用户自定义类型的时候,就会报错。如上图所示,2ch是44,为什么是44字节呢?因为多出来的4个字节是用来存总字节数目的,如果你用free来释放,就会报错)
4.operator new和operator delete函数
- new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是 系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。
/*operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
{
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/*operator delete: 该函数最终是通过free来释放空间的*/
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
(通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的)
- new,operator new,malloc和delete,operator delete,free三者的关系:
- operator new = malloc + 失败抛异常
- new = operator new + 调用构造函数
- new = malloc + 失败抛异常 + 调用构造函数
- operator delete = free
- delete = operator + 调用析构函数
- delete = free + 调用析构函数
4.1new和delete的原理实现
- new的原理
- 调用operator new函数申请空间
- 在申请的空间上执行构造函数,完成对象的构造
- delete的原理
- 在空间上执行析构函数,完成对象中资源的清理工作
- 调用operator delete函数释放对象的空间
- new T[N]的原理
- 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对 象空间的申请
- 在申请的空间上执行N次构造函数
- delete[]的原理
- 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
- 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释 放空间
5.定位new(placement-new)
- 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
- 使用格式:
- new (place_address) type
- new (place_address) type(initializer-list) (place_address必须是一个指针,initializer-list是类型的初始化列表)
5.1使用场景
- 在工作中,我们会有多个类,也会有多个对象,平凡的去申请空间会增加系统的开销,C++引入了内存池去减小系统开销,且可以提高效率。内存池的工作原理是先向系统一次性申请比较大的空间,当我们每次去申请空间时就直接使用内存池里的空间,而省略了申请和释放的两个开销动作,也减少了系统内存碎片,从而提高了开发效率。
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
A* p1 = new A;//开辟空间
//或者
A* p1 = (A*)operator new(sizeof(A));//用operator new的方法拟实现new来开辟空间
new(p1)A; // 对已有空间,显示调用构造
new(p1)A(10); // 对已有空间,显示调用构造
A* p1 = pool.alloc(sizeof(A));
new(p1)A(10); // 对已有空间,显示调用构造
// delete p1
p1->~A();
operator delete(p1);
// new []用定位new的拟实现
A* p2 = (A*)operator new[](sizeof(A)*10);//用operator new[]创造新空间
new(p2)A[10]{1,2,3,4}; // 对已有空间,显示调用构造
for (int i = 0; i < 10; ++i)
new(p2 + i)A(i);
// delete[]的拟实现
for (int i = 0; i < 10; i++)
{
(p2 + i)->~A();
}
operator delete[](p2);
return 0;
}
6.malloc/free和new/delete的区别
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时需要手动计算空间大小并传递,new不需要
- malloc的返回值为void*,使用时必须强转来接收,new不需要
- malloc申请失败时返回NULL,new申请失败会抛异常
- 申请自定义类型的对象时,malloc/free不会调用构造函数和析构函数,而new会申请空间后调用构造函数,delete会调用析构函数后再释放空间