一、new

1.new类对象是加不加括号的区别

看下边代码示例

class A{
public:
  int num_;
}
int main(){
A *pa = new A();//此时成员变量num_被初始化为0;
A *pb = new A;//num_是随机值;
}

这说明使用带括号的初始化方式会把一些和成员变量有关的内存设置为0。
如果类中有构造函数,结果又会不同。增加如下构造函数:

public:
	A(){}

此时发现:main中两种初始化方式的结果又变得相同了,“A *pa = new A()”这种初始化方式,成员变量num_n内存也不为0了,这大概是成员变量的初始化工作交给了构造函数,但此时构造函数为空,什么也没做。

2.new到底干了啥

new到底是啥?
new可以成为关键字,也可称为操作符。我们可以在“A *pa = new A();”代码行打上断点,进入它的反汇编页面查看它到底干了啥。
我们在构造函数中给num_赋值,观察一下汇编代码。

public:
	A(){num_=7;}

部分汇编代码如图:
在这里插入图片描述上图可以看出我们首先调用了一个operator new函数,然后出现了 如下汇编代码:

mov dword ptr [rax],7;

我大概知道这是个赋值语句,而7正是我们将要给num_赋的值。这是不是说明调用了构造函数?
于是我修改了构造函数的代码,重新运行了一下,结果果然如此。
与此同时,operator new中是否也进行了什么操作?
我们单步运行下去,进入operator new一探究竟:
在这里插入图片描述此时发现了malloc函数。
原来如此!

当我们new一个对象的时候,new关键字主要做了这两件事:

  1. 调用operator new(),opeartor new() 又调用了malloc。
  2. 调用构造函数。

出院!

3.delete又干了啥

new 与delete 必须成对出现,只new不delete势必会导致内存泄露。
给类A添加一个析构函数如下:

~A(){}

重复上述步骤,最终可以得到释放内存的大概调用关系:
1.如果有析构函数,先调用析构函数。
2. 调用operate delete()函数,operate delete()函数又调用free()函数。

2、new 内存分配与重载类内operator new/delete

1.new 内存分配详解

且看以下代码:

char * ptr = new char[15];
memset(ptr,9,15);
delete []ptr;

程序运行到“memset(ptr,9,15);”时,此时刚生成对象,我们查看ptr的地址为0x0000020f5567ffd0:此时内存被分配但还没有赋值。
在这里插入图片描述
继续运行,程序运行到“delete []ptr;”后,再次查看内存:
在这里插入图片描述
此时内存已被写入。再继续往下运行:
在这里插入图片描述
运行完“delete []ptr;”之后我们发现,不止释放了15字节的内存,而是释放了一片内存。这是为啥?
虽然在分配内存时,我们指明需要15字节的内存,但是只是我们一厢情愿,我们并没有告诉编译器要释放多少字节的内存,所以势必编译器在某个地方记录了内存的大小。我们在内存中寻找一下:
在这里插入图片描述
在ptr地址前16个字节处有一个0f正好对应15字节,看来是编译器在此处记录分配内存的大小。
将分配内存数增大为20,再次验证一下:
在这里插入图片描述
果然如此。看来内存分配这件事情并不是单纯的分配你所需的字节数量,而是包含周围的一些内存来记录相关信息,而编译器最终只是把这一段内存中的中间某块位置的地址返回给ptr,作为我们能够使用的内存起始地址。

2.重载类内operator new/delete

既然new和delete会调用operator new/delete ,那么我们同样可以重载这两个函数,自己去做内存的分配与释放。
且看如下代码:

class A {
public:
	static void* operator new(size_t size) {
		std::cout << "调用operator new\n";
		A* ptr = (A*)malloc(size);
		return ptr;
	}
	static void operator delete(void* p) {
		std::cout << "调用operator delete\n";
		free(p);

	}
	A() {
		std::cout << "调用构造函数\n";
	}
	~A() {
		std::cout << "调用析构函数\n";
	}
};
int main() {
	A* pa = new A();
	delete pa;
}
执行结果:
调用operator new
调用构造函数
调用析构函数
调用operator delete

如果不想用自己写的,想用系统自带的只需要做一下修改即可:

	A* pa = ::new A();
	::delete pa;
结果:
调用构造函数
调用析构函数

总结

你学废了吗,后续会补充operator new[] /delete[]。
在这里插入图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐