C++中new和delete的底层实现原理

一、operator new和operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层调用operator delete全局函数来释放空间。

operator new是对malloc的封装,operator delete是对free的封装。我们通常不会使用这两个全局函数,它们是给new和delete使用的。

malloc申请空间失败后返回空,operator new则会抛异常从而实现new。

int* ptr = (int*)operator new(40);//申请40个字节空间
operator delete(ptr);//释放ptr指针变量指向的空间
ptr = nullptr;

对于一次申请多个空间的,new [ ]在底层调用的是operator new[ ]全局函数,operator new [ ]函数其实封装的是operator new函数

对于一次释放多个空间的,delete[ ]在底层调用的是operator delete[ ]全局函数,operator delete[ ]函数其实封装的是operator delete函数

二、new、delete底层实现原理

内置类型

对于内置类型,new和malloc,delete和free基本类似。不同的地方在于new/delete申请/释放的是单个空间,new[ ]/delete[ ]申请/释放的是连续的空间;并且new申请空间失败会抛异常,而malloc申请空间失败会返回NULL

自定义类型

new动态申请自定义类型空间时,会先调用operator new函数申请空间,再调用该自定义类型的构造函数,完成对象的构造

new[ ]动态申请自定义类型空间时,会先调用operator new[ ]函数申请空间,实际就是调用operator new完成N个对象的空间申请,再调用N次构造函数,完成对象的构造

delete动态销毁自定义类型空间时,会先调用该自定义类型的析构函数清理对象中的资源,再调用operator delete函数释放对象的空间

delete[ ]动态销毁自定义类型空间时,会先调用N次析构函数完成N个对象中资源的清理,再调用operator delete[ ]函数释放N个对象的空间,实际上就是调用operator delete函数释放N个对象的空间

以Stack类为例,delete s时会先调用析构函数释放对象s中的资源(_a指向的空间),再调用operator delete函数销毁对象s的空间

class Stack {
private:
	int* _a;
	int _top;
	int _capacity;
public:
	Stack()//构造函数
		:_a((int*)malloc(sizeof(int)*4))
		,_top(0)
		,_capacity(4)
	{}
	~Stack()//析构函数
    {
	cout << "~Stack()//析构函数" << endl;
	if (_a)
	{
		free(_a);
		_a = nullptr;
	}
	_top = 0;
	_capacity = 0;
    }
};
int main()
{
	Stack* s = new Stack;
	delete s;
	return 0;
}

三、delete[ ]如何知道调用几次析构函数?

对于new[N]申请自定义类型空间时,根据N即可知道调用几次构造函数来初始化对象;但是对于delete[ ]没有N的指示,如何知道调用几次析构函数呢?

事实上,当类中有显式定义的析构函数时,new[N]申请空间时会多申请一个地址空间(32位平台下多申请4字节,64位平台下多申请8字节),用于存放申请的对象个数,根据这个数据即可知道调用多少次构造函数。如下类中,new[10]申请10个对象空间,一个对象4字节,但实际申请的44字节,多出来的4字节地址空间用于存放10这个数据。

class A {
private:
    int _a;
public:
    A(int a = 0)//构造函数
        :_a(a)
    {
        cout << "A(int a = 0)构造函数" << endl;
    }
    ~A()//析构函数
    {
        cout << "~A()析构函数" << endl;
    }
};
int main()
{
    A* ptr1 = new A[10];
    delete[] ptr1;
    return 0;
}

 

当类中没有显示定义的析构函数时,编译器会自动优化,认为没有必要调用析构函数,因此也就无需多开4/8个字节来存放申请对象的个数。

相关推荐

  1. C++new/delete

    2024-03-14 20:36:01       39 阅读
  2. [C++] newdelete

    2024-03-14 20:36:01       34 阅读
  3. c++newdelete

    2024-03-14 20:36:01       45 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-14 20:36:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-14 20:36:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-14 20:36:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-14 20:36:01       18 阅读

热门阅读

  1. RabbitMQ详解

    2024-03-14 20:36:01       17 阅读
  2. Python 面试问题:递归

    2024-03-14 20:36:01       23 阅读
  3. LeetCode每日一题[C++]-找出数组的第K大和

    2024-03-14 20:36:01       18 阅读
  4. ChatGPT模型api的python调用

    2024-03-14 20:36:01       17 阅读
  5. vue父子组件生命周期

    2024-03-14 20:36:01       18 阅读
  6. C语言(循环)单元练习

    2024-03-14 20:36:01       17 阅读
  7. TCP网络通信-在C#/Unity中的知识点

    2024-03-14 20:36:01       21 阅读
  8. Nmap常用的一些参数

    2024-03-14 20:36:01       18 阅读
  9. linux Shell 命令行-09-redirect 重定向

    2024-03-14 20:36:01       17 阅读