C语言前章回顾

int globalVar = 1;
 static int staticGlobalVar = 1;
 void Test()
 {
 static int staticVar = 1;
 int localVar = 1;
 int num1[10] = { 1, 2, 3, 4 };
 char char2[] = "abcd";
 const char* pChar3 = "abcd";
 int* ptr1 = (int*)malloc(sizeof(int) * 4);
 int* ptr2 = (int*)calloc(4, sizeof(int));
 int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
 free(ptr1);
 free(ptr3);
 }
 - 选择题:
选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)
 globalVar在哪里?____C   已经赋值的全局变量/static修饰的变量位于静态区  
staticGlobalVar在哪里?____C
 staticVar在哪里?____   C
localVar在哪里?____A   //其他main函数中的变量存在于栈
 num1 在哪里?____A
 char2在哪里?____   A    
*char2在哪里?___A  *char2 指向的是数组 数组存在于栈上
 pChar3在哪里?____A      
*pChar3在哪里?____D  *pchar3指向的是 abcd常量  常量存在于 常量区
 ptr1在哪里?____  A      
*ptr1在哪里?___B

在这里插入图片描述
栈存放的是 函数内局部变量、函数参数、和返回值 例如:int a = 10;、char arr[5];
堆存放的是 动态分配的内存(需手动管理)
静态取存放的是 全局变量 静态变量
常量区 存放的是字符串常量 和 const修饰的常量

C++内存管理方式

C语言中的malloc……等函数在某些地方用起来比较麻烦,就比如类中我们需要调用构造调用析构。这时候malloc、relloc、calloc、free就显得很鸡肋。因此C++提出了 new和delete操作符进行动态内存管理

  • new与delete特点

1.new和delete本身是一个操作符,可以通过指定的创建类型和要进行初始化的数值进行对象创建
2.new和delete在与类结合时会自动调用构造函数和析构函数
3.当new创建的是一个数组时,delete需要变成delete[] 操作符进行释放

不同类型创建方式

void Test()
{
	//动态申请一个int类型的空间
	int* ptr4 = new int;
	//动态申请一个int类型的空间并且初始化为10
	int* ptr5 = new int(10);
	//动态申请10个int类型的空间
	int* ptr6 = new int[10];
	//动态申请10个int类型的空间并且初始化
	int* ptr7 = new int[10] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

	delete ptr4;
	delete ptr5;
	delete[] ptr6;
	delete[] ptr7;
}

在这里插入图片描述

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A()" << this << endl;
	}
private:
	int _a;
};
int main()
{
	//new/delete 和malloc/free最大的区别是new/delete对于 自定义类型除了开空间还会调用构造函数和析构函数
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	free(p1);
	delete p2;

	//内置类习惯几乎是一样的
	int* p3 = (int*)malloc(sizeof(int));
	int* p4 = new int;
	free(p3);
	delete p4;

	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[10];
	free(p5);
	delete[] p6;
	return 0;
}

operator new 和 operator delete

  • operator new 和new区别

operator new
1.本质:全局函数(可重载为类成员函数),仅负责分配原始内存(类似 C 语言的 malloc),不涉及对象构造。
2.功能:根据传入的 size_t 大小,在堆上分配对应字节的内存,失败时默认抛异常(可使用 nothrow 版本返回 nullptr)

void* raw_mem = operator new(sizeof(int)); // 分配4字节内存,返回void*

new
本质:C++ 关键字,用于创建对象,包含两个步骤:
① 调用 operator new 分配内存;
② 在分配的内存上自动调用对象的构造函数。

class A {
public:
    A(int x) : _x(x) {} // 构造函数
private:
    int _x;
};

A* p = new A(10); // 使用new关键字
// 等价于:
// 1. 分配内存:void* mem = operator new(sizeof(A));
// 2. 构造对象:A* p = new(mem) A(10); // placement new(显式调用构造函数)
  • operator delete和delete的区别

operator delete
本质:全局函数(可重载为类成员函数),仅负责释放原始内存(类似 C 语言的 free),不涉及对象析构。
功能:接收 void* 指针,释放该指针指向的堆内存(需确保内存是 operator new 分配的)。

operator delete(raw_mem); // 释放之前分配的内存

delete
本质:C++ 关键字,用于销毁对象,包含两个步骤:
① 先调用对象的析构函数(清理对象资源);
② 调用 operator delete 释放内存。

delete p; // 使用delete关键字
// 等价于:
// 1. 析构对象:p->~A();
// 2. 释放内存:operator delete(p);

在这里插入图片描述

/*
 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;
}

总结——new和delete是精简版本,即使我们没有写显式的operator new函数和operator delete函数,但是当我们使用new创建对象和使用delete释放空间时,编译器也会自动调用operator new函数 和operator delete函数

new和delete实现原理

  • 如果申请的是内置类型的空间,那么new和malloc、delete和free基本类似,不同的地方是 new/delete申请和释放的是单个元素空间。new[]\delete[]申请的是连续的空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
  • new的原理

1.调用operator new函数申请空间
2.在申请空间上执行构造函数,完成对象的构造

  • delete的原理

1.在空间上执行析构函数,完成对象中资源的清理工作
2.调用operator delete函数释放对象空间

  • new T[N]的原理

1.调用operator new[]函数,在operator new[] 中调用operator new 函数,完成N个对象空间的申请
2.调用构造函数N次,完成初始化

  • delete T [N]的原理

1.调用析构函数N次,完成对资源的全部释放
2.调用operator delete N次,完成对空间的释放

定位new表达式(palacement-new)

定位new表达式 是已分配的原始内存空间中调用构造函数初始化一个对象
使用格式:
new(place_address)type,或者new(place_address) type (initializer-list) 注意place_address必须是一个指针,initializer-list 必须是初始化列表

定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

int main()
{
	//p1现在指向的是与A对象相同大小的一段空间,不能算是一个对象,因为构造函数没有执行
	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A;
	p1->~A();
	free(p1);
	A* p2 = (A*)operator new(sizeof(A));
	new(p2)A(10);
	p2->~A();
	operator delete(p2);
	return 0;
}

malloc/free和new/delete的区别

malloc/free 和new/delete共同特点就是都是在堆上申请空间,并且需要用户手动输入

1.malloc和free是函数,new和delete是操作符
2.malloc不会初始化,new会初始化
3.malloc申请空间失败返回NULL,new申请空间失败需要捕获异常
4.malloc申请空间需要手动出入大小,new不需要,只需要加上类型即可
5.malloc返回值是void* 需要强转,new不需要,因为new后面跟的就是空间类型
6.malloc/free智慧开辟空间,而new/delete会自动调用构造函数和析构函数

更多推荐