c++入门(3) 内存管理

一、内存区域划分

C++中,程序的内存区域从低地址到高地址划分如下:

  • 代码段:存储可执行程序的代码和只读常量
  • 数据段:存储已初始化的全局变量和静态变量
  • 堆:用于程序运行时动态内存分配,从低地址向高地址增长
  • 栈:又叫堆栈,存储非静态局部变量/函数参数和返回值等,从高地址向低地址增长

        声明 char char2[] = "abcd"; 时,实际上是在栈上分配了一个足够大的字符数组,并将字符串字面量 "abcd" 的内容复制到这个数组中。因此,数组 char2 和它的内容都存储在栈区。这是因为 char2 是一个局部变量,而局部变量通常(但不总是)存储在栈上。

        声明 char* pChar3 = "abcd"; 时,并没有在栈上分配一个包含 "abcd" 的数组。相反,只是创建了一个指向字符串字面量 "abcd" 的指针 pChar3这个指针 pChar3 本身存储在栈区,因为它是一个局部变量。但是,字符串字面量 "abcd"通常存储在只读数据段(也称为代码段或文本段,尽管这个术语可能因编译器和操作系统的不同而有所差异)。这是因为字符串字面量在编译时就已知,并且它们在程序的整个生命周期内都不会改变。因此,编译器将它们放在只读数据段中,以确保它们不会被意外修改。

二、c++的内存管理方式

2.1 malloc/free

        C++兼容C语言,所以C语言的内存管理方式在C++中可以正常使用,

        malloc函数和free函数可以处理内置类型,不能处理自定义类型

        malloc不能实现初始化

        malloc申请空间失败返回NULL

2.2 new/delete

        new和delete不是函数,而是用户进行动态内存申请和释放的操作符。


2.2.1 底层函数调用

new:

  •         new操作符底层调用operator new函数申请空间,如果操作对象是自定义类型的变量,还会在申请的空间上调用构造函数,完成对象的构造。
  •         其中operator new是系统提供的全局函数,是通过malloc来申请空间的,如果malloc申请空间成功就直接返回,如果失败则执行用户提供的应对措施,如果用户提供该措施则继续申请空间,否则抛出异常。
  •         由此可见new操作符在处理内置类型变量时和malloc函数没有太大的区别,除了new能够初始化申请的空间,malloc不能。

delete:

  •         在空间上调用析构函数,完成对象中资源的清理工作,如果操作对象是自定义类型的变量,还会调用operator delete函数释放对象的空间。其中系统提供的全局函数operator delete最终是通过free来释放空间的。同样可见delet操作符在处理内置类型变量时和free函数没有太大的区别

(operator new和operator delete函数的实现可见operator new实际上是通过malloc来申请空间,operator delete最终是通过free来释放空间的。)

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);
}
 
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 T[N]:

  •         调用operator new[]函数,而operator new[]函数实际上又会调用operator new函数完成N个T类型对象的空间申请,如果为自定义类型变量,则在申请的空间上执行N次构造函数

delete[]:

  • 如果为自定义类型变量,则在空间上执行N次析构函数,完成N个对象的资源清理调用operator delete[]函数,而operator delete[]函数又会调用operator delete

2.2.2 初始化

2.3定位new

        目的:在C++定位new(也称为placement new)是一new表达式,它允许你在已分配的内存上构造对象,而不是像普通的new表达式那样自动分配内存。定位new主要用于在已经存在的内存块(例如,通过malloccallocrealloc或先前通过new[]、operator new分配的内存块)上构造对象。

        定义:1.定位new的语法如下:new (place_address) Type(initializer_list);这里place_address是你要在其上构造对象的内存地址,而Type是要构造的对象的类,initializer_list是传递给对象构造函数的参数列表(如果对象不需要初始化,则可以省略此部分)。

                   2.内置类型用定位new并不常见,因为内置类型不需要调用构造、析构函数。

                   3.使用场景

  • 内存池管理:在需要频繁分配和释放小对象的场景中,使用内存池可以减少内存分配的开销。定位new允许在内存池分配的内存块上直接构造对象。

  • 与C代码交互:当C++代码需要与C代码交互,且C代码已经分配了内存时,可以使用定位new在这些内存上构造C++对象。

  • 优化性能:在某些性能敏感的应用程序中,通过减少内存分配的次数来提高性能。

                  4.定位new一般不能调用delete/delete[]来销毁,需要手动调用析构函数销毁对象,再释放空间。因为delete操作符会尝试释放与对象相关联的内存,但这块内存通常不是通过new操作符分配的,而是由用户(或库)通过其他方式(如mallocnew char[]等)分配的。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class A {
public:
	A(int a = 0, int b = 0)
		:_a(a)
		, _b(b)
	{
		cout << "A()" << endl;
	}

	~A() {
		cout << "~A()" << endl;
	}

	void Print() {
		cout << _a << "," << _b << endl;
	}
private:
	int _a;
	int _b;
};

int main() {

	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A;
	p1->~A();
	free(p1);
	//也可以operator delete(p1);

	A* p2 = (A*)operator new(sizeof(A));
	new(p2)A;
	p2->~A();
	operator delete(p2);
	//也可以free(p2);

	//通常不会这样做
	A* p3 = new A;//申请空间,构造对象
	new(p3)A(1, 2);//在申请的空间上构造对象,第二个对象覆盖了第一个对象
	delete p3;
	//因为p1指向的空间是由new申请
	//所以可以调用delete

	char* p5 = new char[sizeof(A)];
	//申请一个A的对象大小的空间,不会创造对象
	A* p6 = new(p5)A;//在p5指向的空间构造一个对象
	//但是p5始终是char*类型,所以创造A*类型的p6
	p6->~A();
	free(p6);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值