C++中的new和delete


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
执行析构函数

我们可以对这个过程做出如下猜测的操作步骤:

  1. 在堆中使用 malloc 函数创建 sizeof(MTestClass) 大小的内存,并将内存转为 MTestClass* 类型;(使用 operator new
  2. 调用构造函数 MTestClass(50) 并将已分配的内存设置值。(使用placement new)
  3. 调用函数 p->print();
  4. 调用析构函数 ~MTestClass()
  5. 释放申请的堆空间内存。(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 !

根据运行结果可以看出,

  1. 首先执行 operator newoperator new 虽然分配了内存空间,但是并没有为这块内存空间赋值;
  2. 执行 placement new,调用构造函数并为这块空间赋值。
::new(p) MTestClass(50);
  1. 执行析构函数
  2. 执行 operator delete 释放内存。

2. new[] 和delete[]

我们经常说 new[]delete[] 一定要成对出现,那么为什么呢?
其实是这样的,对于普通数据类型使用 new[] 创建的数组,使用 delete[] 和直接使用 delete 释放内存效果是一样的,对于自定义的类型,如果显示的声明了析构函数,则需要使用 delete[], 如果不是显示的声明析构函数,那么 deletedelete[] 的效果是相同的。
如果显示声明了析构函数,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 newoperator new[] 的效果是相同的,都是申请 size 大小的空间;
operator deleteoperator delete[] 的效果也是相同的,都是释放堆内存空间。


作者:douzhq
个人主页:https://www.douzhq.cn
文章同步页:https://douzhq.cn/newanddelete/

发布了88 篇原创文章 · 获赞 79 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/douzhq/article/details/90454467