• C++:new 和 delete


    在这里插入图片描述

    个人主页个人主页
    个人专栏 : 《数据结构》 《C语言》《C++》


    前言

    本篇博客作为C++:new 和 detele操作符的知识总结


    一、C++内存管理

    1.内置类型

    int main()
    {
    	// 动态申请一个int类型的空间
    	int* p1 = new int; // 未初始化
    	int* p2 = new int(1); // 初始化
    	// 动态申请10个连续的int类型的空间
    	int* p3 = new int[10]; // 未初始化
    	int* p4 = new int[10] {1, 2, 3, 4, 5}; // 初始化
    
    	delete p1;
    	delete p2;
    	delete[] p3;
    	delete[] p4;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    在这里插入图片描述

    注意:申请连续空间初始化时与数组初始化类似
    注意:申请和释放单个元素空间,使用new 和 delete操作符,申请和释放连续的空间,使用new[] 和 delete[]。

    2.自定义类型

    class A
    {
    public:
    	A(int a = 0)
    		:_a(a)
    	{
    		cout << "A(int a = 0)" << endl;
    	}
    
    	~A()
    	{
    		cout << "~A()" << endl;
    	}
    private:
    	int _a;
    };
    
    int main()
    {
    	A* a = new A;
    	
    	delete a;
    	cout << "-----------------------------" << endl;
    
    	A* aa = new A[10];
    	
    	delete[] aa;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述

    注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数

    3.delete 与 new不匹配使用问题(VS平台下)

    • 对于内置类型使用不匹配问题
    int main()
    {
    	int* p1 = new int;
    	free(p1);
    
    	int* p2 = new int[10];
    	free(p2);
    
    	int* p3 = new int;
    	delete[] p3;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    对于内置类型,此时delete 就相当于 free,因此程序不会崩溃,不会造成内存泄漏。但不建议这样使用

    在这里插入图片描述


    • 对于自定义类型使用不匹配问题
    class A1
    {
    public:
    	A1(int a = 0)
    		:_a1(a)
    	{
    		cout << "A1(int a = 0)" << endl;
    	}
    
    	// 未写析构函数
    	/*~A()
    	{
    		cout << "~A()" << endl;
    	}*/
    private:
    	int _a1;
    };
    
    class A2
    {
    public:
    	A2(int a = 0)
    		:_a2(a)
    	{
    		cout << "A(int a = 0)" << endl;
    	}
    
    	// 写L了析构函数
    	~A2()
    	{
    		cout << "~A()" << endl;
    	}
    private:
    	int _a2;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    对于上面A1类而言:
    在这里插入图片描述
    在这里插入图片描述
    不会导致程序崩溃,但使用free来释放空间,如果该类型会申请空间,那么会造成内存泄漏。

    对于A2类而言:

    在这里插入图片描述
    在这里插入图片描述
    不匹配使用会造成程序崩溃,但为什么?A1类与A2类明明只有显示写出析构函数的差别。这是因为delete 释放空间的指针地址出了问题。

    在这里插入图片描述
    对于A2类使用new来开辟连续的一片空间,VS会在起始地址前多开辟一块空间来记录要调用析构函数的次数,此时delete p1指向的空间起始地址,delete[] p1指向空间起始地址前一块空间的地址。
    对于A1类未显示写出析构函数,编译器会自动生成析构函数,但编译器会检查类的成员变量的类型,如果是内置类型就不做处理,是自定义类型会调用对应的析构函数,对于A1类而言其成员变量是内置类型,那么编译器就不会多开辟一个空间来记录要调用析构函数的次数,那么delete p1和delete[] p1指向同一个地址。
    所以对于显示写出析构函数的自定义类型,不匹配使用会造成程序崩溃。

    我们可以对A2类再做一次测试。
    在这里插入图片描述
    在这里插入图片描述
    程序没有崩溃,但析构函数只调用了一次。

    所以总的来说,对于new 和 delete的使用要匹配使用

    二、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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    通过上述两个全局函数的实现知道,operator new实际也是通过malloc来申请空间,如果malloc申请空间成功就会直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就异常。operator delete最终是通过free来释放空间的。

    三、 new 和delete的实现原理

    内置类型

    如果申请的内置类型的空间,new 和 malloc,delete 和 free基本类似,不同的是:new / delete申请和释放的是但个元素的空间,new[] 和 delete[]申请的是连续的空间,而且new在申请空间失败时会抛异常,malloc会返回NULL

    自定义类型

    • new的原理
      1.调用operator new函数申请空间
      2.在申请的空间上执行构造函数,完成对象的构造
    • delete的原理
      1.在空间上执行析构函数,完成对象中资源的清理工作
      2.调用operator delete函数释放对象的空间
    class A
    {
    public:
    	A(int a = 0)
    		:_a(a)
    	{
    		cout << "A(int a = 0)" << endl;
    	}
    
    	~A()
    	{
    		cout << "~A()" << endl;
    	}
    private:
    	int _a;
    };
    
    int main()
    {
    	A* a = new A;
    
    	delete a;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述
    在这里插入图片描述


    • new T[n]的原理
      1.调用operator new[]函数,在operator new[]中实际调用operator new函数完成n个对象空间的申请
      2.在申请的空间上执行N次构造函数

    • delete[]的原理
      1.在释放的对象空间上执行n次析构函数,完成n个对象中资源的清理
      2.调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

    class A
    {
    public:
    	A(int a = 0)
    		:_a(a)
    	{
    		cout << "A(int a = 0)" << endl;
    	}
    
    	~A()
    	{
    		cout << "~A()" << endl;
    	}
    private:
    	int _a;
    };
    
    int main()
    {
    	A* a = new A[10];
    
    	delete[] a;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    四、malloc/free 和 new/delete的区别

    • 共同点

    malloc / free 与 new / delete都是从堆上申请空间,并且需要用户手动释放空间

    • 不同点

    用法(malloc使用繁琐,new使用简便):
    malloc 和 free是函数,new 和 delete是操作符
    malloc申请的空间不会初始化,new可以初始化
    malloc申请空间时,需要手动计算空间大小并传递,new只需要在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
    malloc的返回值为void*,在使用时必须强转,new不需要,因为new后跟的是空间类型
    malloc申请空间失败时,返回的是NULL,因此使用时必须检查,new不需要,但new需要捕获异常

    底层原理(对于自定义类型)
    申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理


    总结

    以上就是我对于new 和 delete操作符的知识总结。感谢支持!!!
    在这里插入图片描述

  • 相关阅读:
    硬件学习 PAD9.5 day01 原理图布局开始设置, 元器件的调用和绘制, 新建库, 库添加元器件,
    智能车菜单编写(1)
    java抽象类和接口(Comparator和Conparable的使用)
    【服务器数据恢复】raid5崩溃导致lvm信息和VXFS文件系统损坏的数据恢复案例
    如何合理估算 Java 线程池大小
    Stream根据多个字段去重
    正则表达式的修饰符
    Ajax复习(62nd)
    【Vue】使用 Vue 实现基础的 CSS 过渡与动画效果
    DBCO-C3-Maleimide,CAS号:1629057-08-4,DBCO-C3-马来酰亚胺,二苯并环辛炔-碳3-马来酰亚胺
  • 原文地址:https://blog.csdn.net/li209779/article/details/132977259