1. operator new 、placement new 和 operator delete
我们首先来看一下下面的简单示例
#include <iostream>
class MTestClass
{
public:
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
~MTestClass() {
std::cout << "执行析构函数" << std::endl;
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
};
int main(int argc, char** argv)
{
MTestClass *p = new MTestClass(50);
p->print();
delete p;
system("pause");
return 0;
}
上面的代码很简单,
在代码中我们 new 了一个对象 p,
然后调用函数 p->print() ,
最后将对象删除, delete p;
上面的程序运行结果是这样的:
执行构造函数 MTestClass(int num)
Number is : 50
执行析构函数
我们可以对这个过程做出如下猜测的操作步骤:
- 在堆中使用 malloc 函数创建 sizeof(MTestClass) 大小的内存,并将内存转为 MTestClass* 类型;(使用 operator new)
- 调用构造函数 MTestClass(50) 并将已分配的内存设置值。(使用placement new)
- 调用函数 p->print();
- 调用析构函数 ~MTestClass()
- 释放申请的堆空间内存。(operator delete)
- operator new : 是指形如以下形式 void* operator new (size_t size) 形式的函数,分配 size 大小的内存空间。
- placement new : 调用构造函数,并为已将分配的内存空间赋值,这块内存可以是栈内存也可以是堆内存。
形如 ::new(obj) MTestClass(50),为指针 obj 指向的已存在的内存空间赋值- operator delete : 释放堆内存空间。
我们改写上面的示例:
#include <iostream>
class MTestClass
{
public:
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
~MTestClass() {
std::cout << "执行析构函数" << std::endl;
}
void* operator new(size_t size) {
std::cout << "执行 operator new !" << std::endl;
return ::operator new(size);
}
void operator delete(void* pWhere) {
std::cout << "执行 operator delete !" << std::endl;
return ::operator delete(pWhere);
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
};
int main(int argc, char** argv)
{
// operator new
MTestClass* p = (MTestClass*)MTestClass::operator new(sizeof(MTestClass));
p->print();
// placement new
::new(p) MTestClass(50);
p->print();
// 执行析构函数
p->~MTestClass();
MTestClass::operator delete(p);
system("pause");
return 0;
}
程序的运行结果如下:
执行 operator new !
Number is : -842150451
执行构造函数 MTestClass(int num)
Number is : 50
执行析构函数
执行 operator delete !
根据运行结果可以看出,
- 首先执行 operator new ,operator new 虽然分配了内存空间,但是并没有为这块内存空间赋值;
- 执行 placement new,调用构造函数并为这块空间赋值。
::new(p) MTestClass(50);
- 执行析构函数
- 执行 operator delete 释放内存。
2. new[] 和delete[]
我们经常说 new[] 和 delete[] 一定要成对出现,那么为什么呢?
其实是这样的,对于普通数据类型使用 new[] 创建的数组,使用 delete[] 和直接使用 delete 释放内存效果是一样的,对于自定义的类型,如果显示的声明了析构函数,则需要使用 delete[], 如果不是显示的声明析构函数,那么 delete 和 delete[] 的效果是相同的。
如果显示声明了析构函数,delete[] 需要调用n次析构函数,那么 delete[] 是怎么知道申请了多少个内存空间呢?我们可以看一下下面的伪代码
MTestClass *p = new MTestClass[10];
delete[] p;
分析如下:
首先 new MTestClass[10] 会申请 10 * sizeof(MTestClass) + sizeof(size_t) 个内存空间,为什么是 10 * sizeof(MTestClass) + sizeof(size_t) 个而不是 10 * sizeof(MTestClass) 个,是因为他要用 sizeof(size_t) 个字节存储数组的元素个数。
在调用 delete[] 的时候会根据第一个字节的个数去调用n次析构函数,然后释放掉整个内存空间。
为了验证上面的说法,我们将代码改写如下:
#include <iostream>
class MTestClass
{
public:
MTestClass(){
std::cout << "执行构造函数 MTestClass()" << std::endl;
}
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
~MTestClass() {
std::cout << "执行析构函数" << std::endl;
}
void* operator new(size_t size) {
std::cout << "执行 operator new !" << std::endl;
return ::operator new(size);
}
void operator delete(void* pWhere) {
std::cout << "执行 operator delete !" << std::endl;
return ::operator delete(pWhere);
}
void* operator new[](size_t size) {
std::cout << "执行 operator new[] !" << "\tsize is " << size << std::endl;
return ::operator new[](size);
}
void operator delete[](void* pWhere){
std::cout << "执行 operator delete[] !" << std::endl;
return ::operator delete[](pWhere);
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
int m_Number2 = 20;
};
int main(int argc, char** argv)
{
std::cout << sizeof(MTestClass) << ", " << sizeof(size_t) << std::endl;
MTestClass* p = new MTestClass[5];
delete[] p;
system("pause");
return 0;
}
运行结果为:
8, 4
执行 operator new[] ! size is 44
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行析构函数
执行析构函数
执行析构函数
执行析构函数
执行析构函数
执行 operator delete[] !
可以看出,operator new[] 申请的内存空间大小为44个字节,sizeof(MTestClass) 为8字节,而 sizeof(size_t) 为4字节,正好为 44 = 5 * 8 + 4,验证了申请空间大小为 n * sizeof(MTestClass) + sizeof(size_t)
如果我们不显示声明析构函数,又会怎么样呢??
去掉析构函数的完整代码如下:
#include <iostream>
class MTestClass
{
public:
MTestClass(){
std::cout << "执行构造函数 MTestClass()" << std::endl;
}
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
void* operator new(size_t size) {
std::cout << "执行 operator new !" << std::endl;
return ::operator new(size);
}
void operator delete(void* pWhere) {
std::cout << "执行 operator delete !" << std::endl;
return ::operator delete(pWhere);
}
void* operator new[](size_t size) {
std::cout << "执行 operator new[] !" << "\tsize is " << size << std::endl;
return ::operator new[](size);
}
void operator delete[](void* pWhere){
std::cout << "执行 operator delete[] !" << std::endl;
return ::operator delete[](pWhere);
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
int m_Number2 = 20;
};
int main(int argc, char** argv)
{
std::cout << sizeof(MTestClass) << ", " << sizeof(size_t) << std::endl;
MTestClass* p = new MTestClass[5];
delete[] p;
system("pause");
return 0;
}
程序运行结果:
8, 4
执行 operator new[] ! size is 40
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行 operator delete[] !
从结果可以看出,申请的大小为40,验证了如果没有显示的声明析构函数,则会申请 sizeof(MTestClass) * n 个字节的内存空间,这时使用delete 和使用 delete[] 效果是相同的。
通过上面这两个例子也可以看出
operator new 和 operator new[] 的效果是相同的,都是申请 size 大小的空间;
operator delete 和 operator delete[] 的效果也是相同的,都是释放堆内存空间。
作者:douzhq
个人主页:https://www.douzhq.cn
文章同步页:https://douzhq.cn/newanddelete/