c&c++内存管理
1.了解一些基本的内存段(图演示)
验证栈是向下生长的
#include<iostream>
using namespace std;
int main()
{
int a = 3;
int b = 4;
int c = 5;
int d = 6;
cout <<"a:"<< &a << endl;
cout << "b:"<<&b << endl;
cout << "c:"<<&c << endl;
cout << "d:"<<&d << endl;
return 0;
}
验证堆一般是向上生长的(不一定)
#include<iostream>
using namespace std;
int main()
{
int num = 10;
while (num--)
{
int *p1 = (int*)malloc(sizeof(int));
int *p2 = (int*)malloc(sizeof(int));
cout <<"p1"<< p1 << endl;
cout <<"p2"<<p2 << endl;
cout << endl;
free(p1);
}
return 0;
}
一般情况下,p1 的地址是比p2 的地址高的(因为堆一般是向上生长的),但是有时候是不一定的。
习题检测,巩固内存管理知识点
答案
温馨提示:题目中的指针是局部指针变量,是在栈上的,但是它指向的内容(解引用)可能是堆区或者常量区的。,可以画画图理解理解
2.c++申请动态内存的新玩儿法new,delete
回顾c语言动态内存管理的方式
malloc 和calloc 和realloc
-
malloc 堆上动态开空间 -
calloc 堆上动态开空间+初始化成0等价于malloc +memset -
realloc 指针已有的空间扩容 原题增容–后面又足够的空间 异地增容–后面没有足够的空间
开辟内置类型的空间
#include<iostream>
using namespace std;
int main()
{
int* p1 = (int*)malloc(sizeof(int));
*p1 = 1;
int* p2 = new int(2);
free(p1);
delete p2;
int *p3 = (int*)malloc(sizeof(int)* 10);
for (int i = 0; i < 10; i++)
{
p3[i] = i + 1;
}
int *p4 = new int[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
free(p3);
delete[]p4;
return 0;
}
跟c语言的版本相比,c++的版本,初始化时显得比较方便,而且语法比较简洁。当然了,更大的优越性还在后面。
开辟自定义类型的空间(请用vs2013以上版本测试代码)
#include<iostream>
using namespace std;
struct ListNode
{
int _val;
ListNode* _next;
ListNode* _prev;
ListNode(int val = 0)
:_val(val)
, _next(nullptr)
, _prev(nullptr)
{
cout<<"ListNode(int val = 0)"<<endl;
}
~ListNode()
{
cout << "ListNode()" << endl;
}
};
int main()
{
ListNode* pa = (ListNode*)malloc(sizeof(ListNode)*4);
ListNode* pb = new ListNode(1);
free(pa);
delete pb;
ListNode* pc = (ListNode*)malloc(sizeof(ListNode)* 4);
ListNode* pd = new ListNode[4]{1, 2, 3, 4};
free(pc);
delete[]pd;
return 0;
}
? 学过c语言版本的数据结构的小伙伴都知道,我们push_back一个结点时,需要先实现一个buynewnode 的函数(创建一个结点,并且对其进行初始化)。而new这个操作符,在创建结点的同时,已经帮我们实现了结点的初始化。调用了构造函数,而且delete 还调用了析构函数。
总结一下
- c++中,如果是申请内置类型对象或者数组,
malloc 和new 没有太大区别 - 如果时自定义类型,区别很大,new和delete时开空间+初始化,析构清理+释放空间,
malloc 和free 仅仅时开空间+释放空间 - 建议在c++中,无论时内置类型还是自定义类型的申请释放,尽量使用new和delete。
3. 32位平台下可以开辟多大的内存
我:cpu过来遭罪 cpu:你不要过来啊
上述程序,我们开了1.8 G左右的内存,加上需要堆上的小内存,最后的综合有2 G的空间
如何开辟4 G的内存
将项目属性修改一下,改成64位平台即可,64位平台有2^34 G的空间(虚拟内存)在这我们不做细说,因为我也不太了解Linux。
4.了解operator new 和operator delete
new 其实是operator new + 构造函数
delete 其实是operator delete +构造函数
new 和delete 是用户进行动态内存申请和释放的操作符,operator new 和operator delete 是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间
大家可以将operator new和operator delete理解成和malloc 和free一样用法的函数。
唯一不同的地方是,operator new和malloc 开辟空间失败的处理方式不一样,malloc 是返回NULL空指针,而operator是抛异常,下面代码让大家看看效果,不必深究,以后会有介绍。
struct ListNode
{
int _val;
ListNode* _next;
ListNode* _prev;
ListNode(int val = 0)
:_val(val)
, _next(nullptr)
, _prev(nullptr)
{
cout << "ListNode(int val = 0)" << endl;
}
~ListNode()
{
cout << "ListNode()" << endl;
}
};
void f()
{
ListNode* p1 = (ListNode*)malloc(sizeof(ListNode));
free(p1);
ListNode* p2 = (ListNode*)operator new(sizeof(ListNode));
operator delete(p2);
void* p3 = malloc(0x7fffffff);
if (p3 == NULL)
{
cout << "malloc fail" << endl;
}
void* p4 = operator new(0x7fffffff);
ListNode* p5 = new ListNode(2);
cout << "继续" << endl;
}
int main()
{
try
{
f();
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
malloc 失败返回NULL,程序还会继续向下执行,但是operator new失败,就会报异常,f函数后面的没有在继续执行,然后走进catch语句中。
5.operator new 和operator delete 的类函数重载–了解即可
struct ListNode
{
ListNode* _next;
ListNode* _prev;
int _val;
void* operator new(size_t n)
{
void* p = nullptr;
p = allocator<ListNode>().allocate(1);
cout << "memory pool allocate" << endl;
return p;
}
void operator delete(void* p)
{
allocator<ListNode>().deallocate((ListNode*)p, 1);
cout << "memory pool deallocate" << endl;
}
ListNode(int val)
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
int main()
{
ListNode* p = new ListNode(1);
delete p;
return 0;
}
如果你自己在类里面写了operator new 和operator delete ,那么编译器就不会去调用系统提供的了,这是一种提高效率的方式。
我们是可以通过反汇编来看效果的
6.定位new–placement-new
通过上述的学习我们知道,malloc ,和operator 是不会去调用构造函数的,new 会去调用构造函数,而且构造函数是只允许构造出对象时调用,而你的对象创建出来之后是无法调用构造的。
但是如果我们operator new 了一块空间(未初始化),但是又想要调用其构造函数,该怎们办呢?
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A* pa = (A*)operator new(sizeof(A));
new(pa)A;
A* pb = (A*)operator new(sizeof(A));
new(pb)A(3);
A* pc = new A;
new(pc)A(3);
pa->~A();
operator delete(pa);
pb->~A();
operator delete(pb);
delete pc;
return 0;
}
大家要知道定位new哦,他是一种对已经创建出来的对象,还能继续调用其构造函数的方式。
7.malloc /free 和new /delete 的区别–常考面试题
8.再次理解内存泄漏
首先大家先想一想这个问题:内存泄漏是指针丢了还是内存丢了?
内存管理中,内存是用指针去维护的,当你的动态内存空间还没有释放时,你已经把指针弄丢了,那么你将无法控制这段空间,进而导致内存泄漏。
什么是内存泄漏,内存泄漏的危害
什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而 造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会 导致响应越来越慢,最终卡死。
感谢大家的观看!
|