学习->C++篇四:C、C++的内存管理

目录

1. C/C++内存分布

2. C语言中动态内存管理方式

malloc原理:

3. C++中动态内存管理

(1)new/delete操作内置类型

(2)new/delete操作自定义类型

(3) operator new与operator delete函数

(4) 定位new表达式(placement-new)

(5)malloc、free和new、delete的区别


1. C/C++内存分布

C/C++程序内存区域划分为:

栈区:存储局部变量,函数参数,函数返回值,栈是向下增长的(压栈)。

内存映射段:高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口,创建共享共享内存,做进程间通信。

堆区:程序运行时的动态内存分配,堆可以向上增长。

静态区(数据段):存储静态变量,全局变量。

常量区(代码段):存储只读常量(字符串常量),可执行代码(二进制指令)。

2. C语言中动态内存管理方式

C语言用这四个函数来管理动态内存:malloc,calloc,realloc,free
malloc,calloc,realloc的区别:
        malloc是分配堆区上的空间,分配的空间的值不确定,而calloc则是分配num个元素的空间,每个元素size字节,并将每个元素初始化为0,两个函数区别就是是否初始化空间中值为0。
        realloc常用于扩容,在原来的空间上开辟更大的空间,或者找一块更大的空间,将原有数据拷贝,并释放原空间,前者方法是在原来空间后有足够大的空间时使用的。
        三个函数在空间开辟失败时都会返回NULL。

malloc原理:

1)当开辟的空间小于 128K 时,调用 brk()函数,移动指针 _enddata(_enddata 指的是 Linux 地址空间中堆段的末尾地址)

2)当开辟的空间大于 128K 时,系统调用mmap()函数来在虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空间来开辟。

如图:

1)brk 是将数据段(.data)的最高地址指针 _enddata 往高地址推

2)mmap 是在进程的虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空闲的虚拟内存。

        这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

3. C++中动态内存管理

C++引入new和delete操作符来进行动态内存管理,更便捷,适用范围更广。

(1)new/delete操作内置类型

例如:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using std::cout;
using std::cin;
using std::endl;

void test(){
	int* p1 = new int;//动态内存申请一个int类型大小的空间
	int* p2 = new int(10);//动态内存申请一个int类型大小的空间,并初始化为10
	int* p3 = new int[10];//动态内存申请一个10个int类型大小的空间
	int* p4 = new int[10]{20,30}; //动态内存申请一个10个int类型大小的空间,
								//并将前两个元素初始化为20,30
	cout << *p1 << endl;
	cout << *p2 << endl;
	cout << *p3 << endl;
	cout << *p4++ << endl;
	cout << *p4-- << endl;

	delete p1;
	delete p2;
	delete[]p3;//delete[]要和new[]搭配使用
	delete[]p4;//delete[]要和new[]搭配使用
	return;//new[]是申请连续的存储空间,delete[]是释放连续的存储空间
}
int main() 
{
	test();
	return 0;
}

(2)new/delete操作自定义类型

        在用new和delete操作内置类型时,和用malloc和free没什么区别,但在操作自定义类型时就不一样了。因为new不仅会开辟空间,还会调用自定义类型的默认构造函数,delete则会调用其析构函数并释放空间,而malloc和free只有开辟空间和释放空间。

例如:

class A {
public:
	A(int m=200):_m(m) { cout << "A() is called!" << endl; }
	~A() { cout << "~A() is called!" << endl; }
	void print() { cout << "m==" << _m << endl; }
	int _m = 100;
};
void test()
{
	A* a1 = (A*)malloc(sizeof(A));
	a1->print();
	
	cout << "--------------" << endl;
	A* a2 = new A;
	a2->print();
	delete a2;
	cout << "--------------" << endl;

	free(a1);
	return;
}
int main() 
{
	test();
	return 0;
}

输入如下:

(3) operator new与operator delete函数

函数原型:(还有另外4个不抛出bad_alloc异常的版本)

void* operator new(size_t);

void* operator new[](size_t);

void* operator delete(void*);

void* operator delete[](void*);

        operator new 和 operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过调用operator delete全局函数来释放空间。(注意:这两个函数并没有重载new和delete表达式,与其他operator函数不同)
        
         operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。 
        倘若有打印日志信息等需求,就可以重载全局函数operator new和operator delete来检测内存泄漏。
newdelete的实现原理:
         对内置类型由(1)可知:
new和malloc,delete和free是类似的。
但new和delete是申请和释放单个元素的空间,new[]和delete[]是申请和释放连续的一段空间。
new开辟空间失败时抛异常,malloc则返回NULL。                 
        对自定义类型:
new:调用operator new开辟空间,再调用构造函数初始化对象。
delete:调用析构函数清理对象的资源,再调用operator delete释放空间。
new T[N]:调用operator new[]函数开辟空间,operator new[]实际上是调用了operator new申请N个对象的空间,再调用N次构造函数初始化N个对象。
delete[]: 调用N次析构函数,再调用 operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间。

(4) 定位new表达式(placement-new)

        operator new和operator delete是标准库的全局函数,可以直接调用它们来开辟/释放空间,这时operator new开辟的空间,还没调用构造函数初始化对象,应该使用定位new表达式来构造对象。定位new一般配合内存池使用,因为内存池分配的空间没有初始化,需要用定位new表达式,显示调用构造函数来进行初始化。

格式:

new (place_address) type
new (place_address) type(initializer-list)
place_address必须是指针,initializer-list是以逗号分隔的初始化列表。
例如:
class A {
public:
	int m_a;
	A(int a=0):m_a(a) { cout << "call A(int a=0)" << endl; }
	~A() { cout << "call ~A()" << endl; }
};
int main() {
	A* p = (A*)operator new(sizeof(A));
	new (p)A{ 100 };
	cout<<p->m_a<<endl;
	p->~A();//用对象的指针或引用显示调用析构函数
	operator delete (p);

	return 0;
}

 输出:

(5)malloc、free和new、delete的区别

定位上:

malloc和free是函数,new和delete是操作符

使用上:

malloc申请的空间不会初始化,new则可以

malloc返回的指针是void*需要强制类型转换,new则自动根据类型匹配,不用强制类型转换

malloc需要手动计算空间大小去开辟,new则是自动计算类型大小

malloc是开辟空间,开辟失败返回NULL,需要对指针判空操作,new则是开辟空间并调用构造函数,开辟失败时抛异常,需要捕获异常

free是释放空间,delete是调用析构函数并释放空间

原理上:

new本质是调用operator new开辟空间,而operator new本质上也是调用malloc开辟空间,delete也同理,本质上也是调用free函数。

operator new和operator delete的源码:

void* __CRTDECL operator new(size_t const size)
{
    for (;;)
    {
        if (void* const block = malloc(size))
        {
            return block;
        }

        if (_callnewh(size) == 0)
        {
            if (size == SIZE_MAX)
            {
                __scrt_throw_std_bad_array_new_length();
            }
            else
            {
                __scrt_throw_std_bad_alloc();//申请空间失败,抛异常
            }
        }
    }
}
void __CRTDECL operator delete(void* const block) noexcept
{
#ifdef _DEBUG
		_free_dbg(block, _UNKNOWN_BLOCK);
#else
		free(block);
#endif
}

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值