当前位置: 首页 > article >正文

C++ 内存管理 对比C语言动态内存管理;operator new和delete

先做做下列题目捏,复习一下C语言的内存管理!!!

最后再看答案和解析哈

1.C/C++内存分布

----------------------------------------------------------------------------------------------------

每个对象的存储地方

2.C语言中动态内存管理方式:malloc/calloc/realloc/free

  1. malloc 申请空间;
  2. calloc 申请空间,但会进行初始化;
  3. realloc 对空间的扩容 ;
  4. free空间回收
int main()
{
	//calloc 会对开辟空间默认初始化为0
	int* p1 = (int*)calloc(4, sizeof(int));
	//realloc 分为原地扩容和异地扩容;原地扩在结尾增加空间返回指针;异地扩容释放原空间拷贝数据到新空间返回新地址
	int* p2 = (int*)realloc(p1, sizeof(int) * 10);

	//不管异地扩容还是原地扩;都会返回一个地址给p2
	free(p2);
	return 0;
}

面试题:

1. malloc/calloc/realloc的区别?

2. malloc的实现原理? glibc中malloc实现原理    如果对malloc的底层实现原理感兴趣可以去看看

3.C++内存管理方式

C语言的内存管理虽然在C++中可以继续使用,但是已经不能满足现在的需求了;所以C++有自己的内存管理方式:通过new和delete 操作符进行动态内存管理

new: 申请空间

delete:释放空间

3.1.new/delete操作内置类型

int main()
{
    int* p1 = new int;//申请一个int大小
    int* p2 = new int[10];//申请10个int大小空间

    delete p1;
    delete[] p2;//如果是数组的形式需要带个括号

    int* p3 = new int(1);//一个int 并初始化为1
    int* p4 = new int[10] {0};//10个全部初始化为0
    int* p5 = new int[10] {1, 2, 3, 4, 5};//初始化前5个

    delete p3;
    delete[] p4;
    delete[] p5;
    return 0;
}

注意:申请和释放空间,假如释放单个 就需要 new 和 delete操作符,搭配使用才行;同理对于申请和释放连续的空间,就需要 new[] 和 delete[] 搭配使用了

3.2.new和delete操作自定义类型

  1. new 和delete 对比C语言的内存管理(malloc 和 free);好处就是对于(自定义类型)会自动调用构造和析构函数
  2. 假设申请一个类的空间,但是类里面也有动态开辟的空间,这时调用构造函数就很方便,很大程度上可以避免忘记释放对象内的动态空间申请;
struct ListNode
{
	int val;
	ListNode* next;

	ListNode(int xx)
		:val(0)  //可以给固定值或变量
		,next(nullptr)
	{

	}
	void Print()
	{
		cout << val << endl;
	}
};

int main()
{
	//使用new 和 delete的好处是会自动调用构造和析构函数
	A* p1 = new A;
	A* p2 = new A(1);

	delete p1;
	delete p2;

	//可以快速搭建一个简易链表
	ListNode* n1 = new ListNode(10);
	ListNode* n2 = new ListNode(10);
	ListNode* n3 = new ListNode(10);
	ListNode* n4 = new ListNode(10);

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	return 0;
}
  1. 三种初始化方式
class AA
{
public:
	AA(int a = 0,int b = 0)
		:_a(a)
		,_b(b)
	{
		cout << "AA(int a = 0)" << endl;
	}

	void Print()
	{
		cout << _a << endl;
	}

private:
	int _a;
	int _b;
};
int main()
{
	AA* x1 = new AA(1);
	AA* x2 = new AA(1,2);

	//假如要初始化呢?
	AA tmp1(1, 1);
	AA tmp2(2, 2);
	AA tmp3(3, 3);
	AA* x3 = new AA[3]{ tmp1,tmp2,tmp3 };//数组的类;拿3个同类型初始化

	//匿名对象
	AA* x4 = new AA[3]{ AA(10,10),AA(20,20),AA(30,30) };

	//隐式类型转换
	AA* x5 = new AA[3]{ {1,1},{2,2},{3,3} };
	return 0;
}

3.3. new空间 申请失败

  1. 在32位下,堆可以申请的空间大概是2g;64位下,堆可申请的空间大概是22g
void fun1()
{
	int n = 1;
	while (1)
	{
		void* ps = new char[1024 * 1024];
		cout << &ps << "->" << n << endl;
		n++;
	}
}

int main()
{
    //new 空间申请失败,会有一个抛异常 此时需要 try/catch 捕获抛异常
    //只讲讲用法,这个和继承多态相关 了解一下即可
	try
	{
		fun1();
	}
	catch (const std::exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

4.operator new与operator delete函数

  1. 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) //当malloc返回值等于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. 通过上述的底层代码发现,operator new实际上还是通过 malloc 来申请空间的;operator delete函数最核心也是使用free

5.new和delete的实现原理

5.1. 内置类型

  1. 如果是内置类型,malloc 和 free 与 new 、delete的功能是类似的;区别在于new是申请一个空间,delete是释放一个空间;要连续申请和释放空间就要用到new[]和delete[]
  2. new 失败会抛异常 ,malloc 失败会返回NULL

5.2. 自定义类型

  1. new的原理:先调用operator new申请空间,然后再申请的空间上调用构造函数完成初始化
  2. delete的原理:先调用析构函数释放资源,然后调用operator delete完成空间的释放
  3. new C[N]的原理:
    1. 调用operator new[]申请空间,但是实际上operator new[]内部也是operator new,只不过通过改进,实际上会调用N次的operator new函数完成申请资源
    2. 在申请的空间上调用N次构造函数
  4. delete[]的原理
    1. 在释放空间前,调用N次析构函数
    2. 然后通过operator delete[]调用N次operator delete
int main()
{
	A* p1 = new A(1);
	delete p1;

	A* p2 = new A[5];
	delete[] p2;

	return 0;
}

5.3. 错配问题

//错配的问题
int main()
{
	int* p1 = new int(1);

	//free(p1);//从底层来讲,delete的底层是free,所以使用free不会报错
	delete(p1);

	tmp1* p2 = new tmp1;
	
	//free(p2);//这里使用free也不会报错,因为不调用析构函数也不会报错;但是会导致对象内的资源泄露
	delete(p2);
	return 0;
}
class tmp1
{
public:
	tmp1(int a = 0, int b = 0)
		:_a(a)
		, _b(b)
	{
		cout << "tmp1(int a = 0, int b = 0)" << endl;
	}
	~tmp1()
	{
		cout << "~tmp1()" << endl;
	}
private:
	int _a;
	int _b;
};
class tmp2
{
private:
	int _a;
	int _b;
};

int main()
{
	//为什么执行第一个就不会报错呢? 因为编译器优化了,编译器认为竟然你没有写析构函数,那也就不用析构函数了反正也没有资源释放
	tmp2* pw1 = new tmp2[5];
	delete pw1;

	tmp1* pw2 = new tmp1[5];
	//delete pw2;
	delete[] pw2;//如果写了括号([]),他自己计算析构个数,也就是保存的那个值
	return 0;
}

  1. 为什么执行pw1就不会报错呢? 因为编译器优化了,编译器认为竟然你没有写析构函数,那也就不用析构函数了反正也没有资源释放
  2. pw2崩溃的原因是什么? 是因为如果你写了析构函数,那么他会在最前面申请一个4字节的空间保存你实例化对象的个数,这个数决定着总共调用析构函数几次

再看看底层是不是的,显示写析构,确实多申请了4个字节的空间大小

所以最后想和大家说的是,new和delete 要搭配使用,不能错配;而这个问题也是一个面试题

6.定位new表达式(placement-new)了解即可

  1. 定位new表达式在已分配的原始空间中调用构造函数的对象
  2. 使用格式:
    1. new (place_address) type或者new (place_address) type(initializer-list)
    2. place_address必须是一个指针,initializer-list是类型的初始化列表
  3. 使用的场景
    1. 定位new表达式,一般都是要配合内存池使用,在堆中已经分配出去一块空间给内存池;如果是自定义类型使用内存池空间,此时就需要用new定义的表达式显示调用构造函数进行初始化
#include <iostream>
using namespace std;
class A
{
public:
	A()
		//:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	void Print()
	{
		cout << _a << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a = 10;
};
int main()
{
	A* aa = new A;
	aa->Print();
	delete aa;

	A* type = (A*)operator new(sizeof(A));
	new(type)A; // 定位new 申请空间后的初始化 ;type 表示要初始化的对象,A 调用构造,如果有参带A(1)
	//这种场景适用于内存池调用构造

	type->~A();
	free(type);
	return 0;
}

7.malloc/free和new/delete的区别

  1. malloc/free和new/delete 的共同点:都是向堆申请空间,都需要手动释放空间
  2. malloc 和 free 是函数;new 和 delete是关键字
  3. 如果要申请连续的空间,malloc需要手动计算,而new只需要在类型后面的方括号写上数字 A[];
  4. malloc 申请空间会返回viod* 需要强制类型转换,而new不需要;new 后面跟着的是类型
  5. malloc 申请空间会返回NULL,需要判断;而new 不要,new 申请空间失败 会有一个抛异常,用try catch 捕获异常
  6. 最主要的区别是:
    1. new 对比 malloc;new 自定义类型在申请空间完后会调用构造函数,delete在释放空间前会调用析构函数;mallco不会调用

以上就是C++ 内存管理的内容;可能不太详细;欢迎指出

继续加油!! 兄妹们


http://www.kler.cn/news/354774.html

相关文章:

  • 洛谷 P1803:凌乱的yyy / 线段覆盖 ← 贪心算法
  • (C/C++)文件
  • 鼠标市场洞察:数据分析揭示消费趋势!
  • 如何解决MQ的重复消费问题?Kafka、ActiveMQ、RabbitMQ有什么区别?
  • 低功耗 ARMxy工业计算机:工业场景的绿色新选择
  • Linux 简述基于 TCP 连接状态分析网络排障
  • 【C语言】函数的声明与定义
  • Windows 和 Ubuntu通讯的网络设置
  • 无技能,学历不高?想要找一份高薪工作,通信网优肯定适合你
  • 影楼即将倒闭!!!!stable diffusion comfyui制作:AI人像摄影专业工作流
  • python string中提取中文字符处理之后插入回原string
  • 【python爬虫基础】年轻人的第一个爬虫程序
  • C++简单多状态dp:按摩师、打家劫舍II、删除并获得点数、粉刷房子
  • 2024全新UI网址发布页源码带黑夜模式
  • WebSocket介绍和入门案例
  • xavier 在tensorflow pytorch中的应用,正太分布和均匀分布的计算公式不一样
  • 串的模式匹配算法_BF算法
  • 【实战案例】SpringBoot项目中异常处理通用解决方案
  • 单片机原理与应用——嵌入式系统中的核心控制器
  • MySQL从入门到跑路