【C++】内存管理1

内存可以整体分为三个部分:栈区、堆区、静态常量区。栈区主要存储的是局部变量和函数参数;C语言当中malloc、calloc、realloc、C++当中的new申请的空间基本是在堆区,堆区也被称为自由存储区;静态常量区有两个主要的标记:static定义的变量和字符串常量。静态常量区细分可以是数据段和代码段,全局变量和static变量存放在数据段,字符串常量这种只读常量放在代码段。

在这里插入图片描述

  • 栈又叫堆栈,存储非静态局部变量、函数参数、返回值等等,栈是向下增长的。
  • 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
  • 堆用于程序运行时动态内存分配,堆是可以向上增长的。
  • 数据段存储全局数据和静态数据。
  • 代码段存储可执行的代码和只读常量。
void main()
{
	int *p1 = (int*)malloc(sizeof(int) * 10);
	if (p1 == NULL)
	{
		cout << "out of memory.\n"<<endl;
		return;
	}
	free(p1);

	int *p2 = new int[10];
	delete []p2;
}

如果用new申请的是数组空间,那么delete的时候要加上[],表示释放的是数组空间,[]内不需要写任何数字。
用new申请空间的时候,不需要判断是否申请成功,同时,相对与C语言申请空间的那一套办法,可以进行更加丰富的初始化。C语言只有calloc函数可以将空间初始化为0,我们来看看用new申请空间可以达到什么样的效果:

void main()
{
	int *p1 = new int(1);
	int *p2 = new int[10]{ 1,2,3,4,5,6,7,8,9,10 };
	delete p1;
	delete[]p2;
}

new可以初始化单个变量,也可以初始化整个数组。

class Test
{
public:
	Test() :m_data(0)
	{
		cout << "Test::Test()" << endl;
	}
	~Test()
	{
		cout << "Test::~Test()" << endl;
	}
private:
	int m_data;
};
void main()
{
	Test *p = (Test*)malloc(sizeof(Test));
	assert(p != NULL);
	free(p);
}

对于自定义类型,我们用C语言的malloc函数去申请空间的时候,仅仅是申请了一份空间,申请的空间大小满足自定义类型的大小,但是在申请空间的过程中,不会自动执行构造函数,也不会执行析构函数,自然也不会初始化,如果我们需要它初始化,需要执行其他操作,就需要再自己调用其他函数。
下面就是用C语言给自定义类型进行一整套流程的代码:

#include<iostream>
#include<assert.h>
using namespace std;
class Test
{
public:
	Test() :m_data(0)
	{
		cout << "Test::Test()" << endl;
	}
	~Test()
	{
		cout << "Test::~Test()" << endl;
	}
	void InitTest()
	{
		m_data = 0;
		ptr = (int*)malloc(sizeof(int) * 10);
		assert(ptr != NULL);
	}
	void DestroyTest()
	{
		free(ptr);
	}
private:
	int m_data;
	int *ptr;
};
void main()
{
	Test *p = (Test*)malloc(sizeof(Test));
	//申请空间
	assert(p != NULL);
	p->InitTest();
	//初始化对象
	p->DestroyTest();
	//摧毁对象
	free(p);
	//释放空间
}
#include<assert.h>
using namespace std; 
class Test
{
public:
	Test() :m_data(0)
	{
		cout << "Test::Test()" << endl;
		ptr = new int[10];
	}
	~Test()
	{
		cout << "Test::~Test()" << endl;
		delete[]ptr;
	}
private:
	int m_data;
	int *ptr;
};
int main()
{
	Test *pt = new Test;
	//1、申请内存空间
	//2、调用构造函数初始化对象
	delete pt;                            
	//1、调用析构函数摧毁对象
	//2、释放对象空间
	return 0;
}

在C++中,new不仅仅具有申请内存空间的作用,而且在开辟空间的过程中,会调用构造函数初始化对象。delete也不仅仅会释放内存空间,它会先调用析构函数摧毁对象,再释放对象空间。

int main()
{
	Test *p1 = new Test[10];
	Test *p2 = new Test(10);
	return 0;
}

p1是申请数组空间,p2是申请单个元素空间。

申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]

	Test *p1 = new Test[10];

同时需要注意的是申请数组空间的时候,如果发生错误,要检查是否有默认构造函数支持。
在我们进行深赋值的时候,会调用赋值语句,同时,还需要注意的是深拷贝会申请空间,但是在申请空间的时候可能会发生异常,也就是说可能会申请空间不成功,如果此时我们再释放原有空间,就会导致程序崩溃。那这个问题要怎么解决呢?
第一种方法就是先用new分配新内容再用delete释放已有的内容:
类Srting:

#include<iostream>
#include<assert.h>
using namespace std; 
class String
{
public:
	String(const char *str = "")
	{
		m_data = new char[strlen(str) + 1];
		strcpy(m_data, str);
	}
	String(const String &s)
	{
		m_data = new char[strlen(s.m_data) + 1];
		strcpy(m_data, s.m_data);
	}
	~String()
	{
		delete[]m_data;
		m_data = nullptr;
	}
private:
	char *m_data;
};
void main()
{
	String s("abcxyz");

	String s1 = s;

	String s2("Hello");
	s2 = s1;
}

拷贝构造函数如下:

	String& operator=(const String &s)
	{
		if (this != &s)
		{
			char *new_data = new char[strlen(s.m_data) + 1];
			delete []m_data;
			m_data = new_data;
			strcpy(m_data, s.m_data);
		}
		return *this;
	}

还有一个更好的办法就是先创建一个临时实例,再交换临时实例和原来的实例。

	String& operator=(const String &s)
	{
		if (this != &s)
		{
			String tmp(s);
			char *ptmp = tmp.m_data;
			tmp.m_data = m_data;
			m_data = ptmp;
		}
		return *this;
	}

在这个过程中,我们将创建一个临时对象tmp,在创建临时对象的时候,我们会调用它的构造函数里用new分配内存,如果内存不足抛出异常,此时我们还没有修改原来的实例的状态,因此,此时实例的状态还是有效的,这样子也就保证了异常安全性,tmp是一个局部对象,出了作用域就会调用析构函数进行释放,也不用担心内存溢出的问题。
也可以直接调用交换函数。

	String& operator=(const String &s)
	{
		if (this != &s)
		{
			String tmp(s);
			swap(m_data,tmp.m_data);
		}
		return *this;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值