C++初阶 —— C/C++内存管理

目录

一,C/C++内存分布

二,内存管理方式 

三,operator new/operator delete函数(重点)

四,new/delete实现原理

五,定位new表达式(placement-new)(了解)


一,C/C++内存分布

  • 栈(又称堆栈),非静态局部变量、函数参数、返回值等,向下增长;
  • 内存映射段,是高效的I/O映射方式,用于装载一个共享的动态内存库;用户可使用系统接口共享共享内存,做进程通信;
  • 堆,用于程序运行时动态内存分配,向上增长,但不一定有可能使用释放后的低地址;
  • 数据段,存储全局数据和静态数据;
  • 代码段,存储可执行代码和只读常量;

C++初阶 —— C/C++内存管理_第1张图片

二,内存管理方式 

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

  • malloc
  • calloc
  • realloc
  • free

C++内存管理方式

  • 兼容C语言内存管理方式;
  • 通过new/delete操作符,进行动态内存管理;
  • 动态申请内置类型,与malloc无区别;
  • 动态申请自定义类型,不仅仅会开空间/释放空间,还会调用构造函数和析构函数;
//new/delete内置类型
int main()
{
	//动态申请一个int类型的空间
	int* ptr1 = new int; 
	//动态申请一个int类型的空间,并赋值10
	int* ptr2 = new int(10);
	//动态申请10个int类型的空间
	int* ptr3 = new int[10];

	delete ptr1;
	delete ptr2;
	delete[] ptr3;
	
	return 0;
}
//new/delete自定义类型
class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};

int main()
{
	//申请一个A类型的空间
	A* ptr1 = (A*)malloc(sizeof(A));
	free(ptr1);
	//申请10个A类型的空间
	A* ptr2 = (A*)malloc(sizeof(A) * 10);
	free(ptr2);

	//申请一个A类型的空间
	A* ptr3 = new A; //或带初始化值new A(10)
	delete ptr3;
	//申请10个A类型的空间
	A* ptr4 = new A[10];
	delete[] ptr4;
	return 0;
}

三,operator new/operator delete函数(重点)

operator new/operator delete函数

  • new/delete是动态申请和释放的操作符;
  • operator new/operator delete是系统提供的全局函数;
  • new在底层:调用operator new全局函数来申请空间 + 构造函数;
  • delete在底层:析构函数 + 调用operator delete全局函数来释放空间;
  • operator new实际是通过malloc来申请的;
  • operator delete最终是通过free来释放空间;
//new与malloc区别
int main()
{
	char* p1 = (char*)malloc((size_t)2 * 1024 * 1024 * 1024);
	//检查p1
	if (p1 == NULL)
		perror("p1");
	
	try
	{
		char* p2 = new char[0x7fffffff]; // 抛异常,捕获异常位置
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

注:

  • new与malloc不同,new失败会抛异常;

C++初阶 —— C/C++内存管理_第2张图片

C++初阶 —— C/C++内存管理_第3张图片

operator new/operator delete类专属重载(了解)

struct ListNode
{
	ListNode(int data = 0)
		:_next(nullptr)
		,_data(data)
	{}
	ListNode* _next;
	int _data;

	//类专属重载
	//new ListNode申请空间,就会调用此处类专属重载
	void* operator new(size_t n)
	{
		++_count;
		return ::operator new(n); //::是避免自己调用自己,::前为空则为调用全局
	}
	void operator delete(void* p)
	{
		--_count;
		return ::operator delete(p);
	}
	static int _count;
};

int ListNode::_count = 0;

struct ListNode* removeElements(ListNode* head, int val)
{
	ListNode* prev = head;
	ListNode* cur = head;
	while (cur)
	{
		if (cur->_data == val)
		{
			prev->_next = cur->_next;
			delete cur;
			cur = prev->_next;
		}
		else
		{
			prev = cur;
			cur = cur->_next;
		}
	}
	return head;
}

int main()
{
	//创建时先申请空间,在构造函数
	ListNode* node1 = new ListNode(1);
	ListNode* node2 = new ListNode(2);
	ListNode* node3 = new ListNode(3);
	ListNode* node4 = new ListNode(2);
	ListNode* node5 = new ListNode(5);

	node1->_next = node2;
	node2->_next = node3;
	node3->_next = node4;
	node4->_next = node5;

	removeElements(node1, 2);
	cout << ListNode::_count << endl;
	return 0;
}

四,new/delete实现原理

内置类型

  • new与malloc,delete与free基本类似;
  • new/delete申请和释放的是单个元素空间;
  • new[]/delete[]申请和释放的是连续空间,且new申请失败会抛异常;

自定义类型

new原理

  • 调用operator new函数申请空间;
  • 在申请空间上执行构造函数,完成对象构造;

delete原理

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

new T[N]原理

  • 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请;
  • 在申请的空间上执行N次构造函数;

delete[]原理

  • 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理;
  • 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间;
class Stack
{
public:
	Stack(int capacity = 4)
		:_a(new int[capacity])
		,_size(0)
		,_capacity(capacity)
	{
		cout << "Stack():" << this << endl;
	}
	~Stack()
	{
        delete _a;
        _size = _capacity = 0;
		cout << "~Stack():" << this << endl;
	}
private:
	int* _a;
	int _size;
	int _capacity;
};
//先在栈上main内,开辟Stack大小的空间
//在调用构造函数,在堆上开辟16字节大小空间给_a,在初始化_size/_capacity
//最后main结束时,调用析构函数,销毁堆上开辟16字节空间,_size/_capacity赋零
int main()
{
	Stack st;
	return 0;
}
//先在堆上开辟Stack大小空间给main上的指针p
//在开辟Stack空间时,调用构造函数,再在堆上开辟16字节大小空间给_a,在初始化_size/_capacity
//在delete时,调用析构函数,销毁堆上开辟16字节空间,_size/_capacity赋零,在释放堆上的Stack空间
int main()
{
	Stack* p = new Stack;
	delete p;
	return 0;
}

C++初阶 —— C/C++内存管理_第4张图片

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A():" << this << endl;
	}
	A(const A& a)
	{
		_a = a._a;
		cout << "A(const A& a)" << this << endl;
	}
	A& operator = (const A& a)
	{
		_a = a._a;
		cout << "A& operator = (const A& a)" << this << endl;
		return *this;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	A a[5]; //调用5次构造
	A* p1 = new A[5]; //调用5次构造
	for (int i = 0; i < 5; i++)
	{
		*(p1 + i) = a[i]; //调用5次赋值重载
	}
	cout << endl;
	return 0;
}
int main()
{
	A a[5];  //调用5次构造
	A* p1 = (A*)malloc(sizeof(A) * 5);
	cout << endl;
	for (int i = 0; i < 5; i++)
	{
        //也称replacement-new
		new(p1 + i) A(a[i]); //调用5次拷贝构造
	}
	cout << endl;
	return 0;
}

C++初阶 —— C/C++内存管理_第5张图片

五,定位new表达式(placement-new)(了解)

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

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

格式:

  • new(place_address) type 或 new(place_address) type(initializer-list)
  • place_address必须为一个指针,initializer-list是类型的初始化列表;
class A
{
public:
	A(int a = 0, int val = 0)
		:_a(a)
		,_val(val)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
	int _val;
};

int main()
{
	A* p = (A*)malloc(sizeof(A));
	new(p) A(1,2);
	return 0;
}

malloc/free和new/delete共同点

  • 都是堆上申请空间;
  • 需手动释放;

malloc/free和new/delete不同点

  • malloc/free是函数,new/delete是操作符;
  • malloc申请空间时需传递空间大小,new只需空间类型即可;
  • malloc返回void*需强转,new返回空间类型;
  • malloc申请失败返回NULL使用需判空,new会抛异常;
  • 申请自定义类型时,malloc/free不会调用构造函数/析构函数,new申请空间后会调用构造函数初始化,delete在释放空间前会调用析构函数;

内存泄露

  • 是指因为疏忽或错误导致程序未能释放已经不使用的内存的情况;
  • 并不是内存在物理上的消失,而是失去对该段内存的控制,造成内存的浪费;
  • 长期运行的程序出现内存泄露,如操作系统或后台服务,可能会导致响应越来越慢最终卡死;

内存泄露分类(了解)

  • 堆内存泄露(Heap Leak),是指程序从堆中分配的内存,未被释放;
  • 系统资源泄露,是指程序使用系统分配的资源,如套接字、文件描述、管道等没有使用对应的函数释放掉,导致系统资源浪费;

避免内存泄露

  • 智能指针;
  • 内存泄露检查工具;

注:进程正常结束,都会释放;僵死进程、长期运行的服务器程序、物联网设备等不会释放;

你可能感兴趣的:(c++,编程语言,c++)