动态内存管理

一、内存分布

先看C++中程序内存区域划分。

1.栈又称为堆栈,非静态局部变量/函数参数/返回值等;

2.内存映射是高效I/O映射方式,用作进程间通信;

3.堆用于动态内存分配;

4.数据段存储全局数据和静态数据;

5.代码段一般就是可执行代码;

6.内核是用户不能操作的那部分。

二、C++和C语言的动态内存管理

在C语言中我们都知道,C语言的动态内存管理一般用malloc/realloc/calloc 三种增容方式,free一种释放方式,C++中动态内存开辟方式只有一种方式,new和delete这一组,但是他有两种格式。

malloc、realloc和calloc的区别

(1)malloc不能初始化所申请的内存空间,而calloc能够。

(2)realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变.当然,新申请的空间的大小如果小于原来的大小,就会导致数据丢失。

(3)realloc申请的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址。当给realloc设置的参数指针若为NULL或者0时就相当于malloc的作用
(4)calloc所申请的空间内容初始化,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零.

class Test
{	
public:
	class Sum
	{
	public:
		Sum()
		{
			_count++;
			_sum += _count;
		}
		static int GetSum()
		{
			return _sum;
		}
		static void ReSetSum()
		{
			_count = 0;
			_sum = 0;
		}
	private:
		static int _count;
		static int _sum;
	};

	int Sum_solution(int n)
	{
		Sum::ReSetSum();
		Sum* ps = new Sum[n];
		delete[] ps;
		return Sum::GetSum();
	}
};

	int Test::Sum::_count = 0;
	int Test::Sum::_sum = 0;
int main()
{
	Test t;
	cout << t.Sum_solution(10) << endl;
	system("pause");
	return 0;
}
class Test
{
public:
	static Test* CreateInstrance(int data)
	{
		return new Test(data);
	}
	~Test()
	{
		cout << "~Test()" << endl;
	}
private:
	Test(int date)
	{
		cout << "Test():" << this << endl;
	}
	Test(const Test& d);
private:
	int _date;
};
int main()
{
	Test *t = Test::CreateInstrance(10);
	delete t;
	system("pause");
	return 0;
}

一共有这样两种动态内存增容方式,new[]/delete[]内部结构和new/delete是一样的,但是他们的不同在与,new[]是开辟一段连续空间,而new开辟的是单个元素的空间。这两种不能混合使用,必须对应着使用。

三、operator new/operator delete全局函数

注意一点,new/delete只是C++中给出的操作符,并不是函数,new在底层调用operator+new全局函数来申请空间,delete在底层调用operator+delete全局函数来释放空间。


 
void Test2() {    
    // 申请单个Test类型的对象    
    Test* p1 = new Test;
    delete p1;        
    // 申请10个Test类型的对象
    Test* p2 = new Test[10];
    delete[] p2;
 }
    /* 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;

}

通过上述两个全局函数就可以知道,new也是用malloc来申请空间,不过new是升级过的malloc。

四、定位new表达式

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。

使用格式:

new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景:

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

class T
{
public:
	T()
		: _data(0)
	{
		cout << "Test():" << this << endl;
	}
	~T()
	{
		cout << "~Test():" << this << endl;
	}
private:
	int _data;
};
void Test()
{
	//pt现在指向的只不过是T对象相同大小的一段空间,
	//还不能算是一个对象,因为构造函数没有执行
	T* pt = (T*)malloc(sizeof(T));
	new(pt) T;//如果T类的构造函数没有参数时,此处需要传参。
}
int main()
{
	Test();
	getchar();
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值