植物大战 动态内存——C++

前言

总结复习前面的知识。

注意:学了C++在做oj题目时,不一定非要用类。
可以多项选择最简单的就行。怎么简单怎么写。

可以用类名定义一个匿名对象。
匿名对象的声明周期只在那一行。

weight();//匿名对象,匿名周期就是当前行。

假如构造+拷贝构造在一起运行,则编译器会优化步骤。

f(weight());//先构造匿名对象weight(),然后再传参拷贝构造。

结论:一个表达式中,连续步骤的构造+拷贝构造
或者拷贝构造+拷贝构造。胆大的编译器都会优化,合二为一。

动态内存分布

最上面是栈。然后是堆,静态区(系统叫做数据段),常量区(代码段)。这里的代码不是我们写的代码,而是那些指令。

栈和堆是程序运行起来才开辟空间的。

为什么要区分这些区域?
因为不同数据需要存在不同的位置。

int glob = 1;
static int staticglob = 1;
void test()
{
    
    
	static int staticvar = 1;
	int loacalvar = 1;
	int num1[10] = {
    
    1,2,3,4};
	char char2[] = "abcd";
	char* pchar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int)*4);
	int* ptr2 =(int*)calloc(4,sizeof(int));
	int* ptr3 = (int*)realloc(ptr2,sizeof(int)*4);
}

globalvar是全局变量,全局变量从语言的角度看在静态区。从系统看在数据段。

staticgob是静态变量,也在静态区。

staticvar是静态变量,在静态区

总结:全局变量和静态变量的声明周期都是全局的。所以都在静态区。

localvar是局部变量,局部变量是在栈上开辟的。

num1代表的是这个数组,数组是在栈上的。

char2在栈上。char2是一个数组,这个数组在栈上。他是用常量字符串abcd ** 拷贝 过去初始化他的。注意这个拷贝**。

*char2在也是在栈上。因为它代表的是这个数组指向的内容,内容也在栈上。

pchar3也在栈上

*pchar3解引用得到是常量区的字符串abcd所以在常量区。

ptr1在栈上。

*ptr1在堆上。因为他是malloc开辟的。

const修饰的叫做常变量,常变量是在栈上。

如何理解C++语法的增加

比如函数重载,引用,new/delete,流插入和流提取运算符。类和对象。

都是为了弥补C语言的不足。可能C++的发明者当时用C语言写

new

malloc和calloc的区别是什么呢?
malloc是开空间。
calloc是开空间+初始化。

realloc是扩容,有空间原地扩,无空间,异地扩。

C语言已经有了开空间的方法。C++为什么搞了一个new?

new和delete配合使用。
new是为了解决C语言中不好用的地方。

new用法

new是先开空间,再调用构造函数进行初始化。,
对于内置类型而言,用malloc和new除了用法不同,没有什么区别,他们的区别在于自定义类型。

//new 一个对象
int* p1 = new int;
//new 10个对象
int* p2 = new int[10];
//new一个对象初始化为10
int* p3 = new int(10);
//C++11的数组初始化
int* p4= new int[10]{
    
    10,1,2,3};

//初始化时一定要匹配释放。
delete p1;
delete[] p2;
delete p3;
delete[] p4;

对于自定义类型链表创建结点来说。

struct ListNode
{
    
    
	ListNode* next;
	int val;
	//构造函数
	ListNode(int val = 0)
		:_next(nullptr)
		,_val(val)
	{
    
    }
};


以下是C语言的用法,特别麻烦。
在这里插入图片描述
但是对于C++来说。C++直接用new就行,因为new会自动调用构造函数。假如构造函数写了就会自动初始化。

ListNode* n = new ListNode;

关于struct和class的使用

struct里面默认都是共有的,一般都要访问的设置为公有,比如list的节点ListNode。

class里面成员默认都是私有的。

关于free和delete的区别。

free直接把指向的当前指针释放。而不管指针的指针,容易导致内存泄漏。

而delete会进行以下两个步骤。

1.先调用析构函数,可以在释放析构函数里面的指针等资源,让空间进行释放。交还给操作系统。
2.再释放当前指针所指向的空间。

背会这句话

创建对象的时候会调用构造函数,假如没写,就是有默认构造函数。对于内置类型来说不做处理,对于自定义类型调用他的默认构造函数。

抛异常

1.对于C语言我们开辟空间失败需要检查,加上assert,malloc失败会返回空指针。

2.对于C++的new来说开辟空间失败会抛异常

怎么捕获异常?
异常的原理是根据继承和多态实现的。

例如以下代码就是抛异常
try和catch会二选一执行。抛异常就执行catch。

try
{
    
    	
	void* p2 = new char[124*1024*1024];
}
catch(const exception& e)
{
    
    
	cout << e.what() << endl;
}

那我们平常使用new都需要抛异常吗?
答案是不需要的。异常一般是在函数外面检查异常的。

operator new和operator delete

重点:new的底层原理是调用operator new和构造函数。

千万要注意。这个虽然带了operator这个词,但不是重载new

实际这个operator new封装了malloc,malloc失败抛异常。
operator封装了free。

这两个本质和malloc有什么区别吗?实际上没有区别。功能上一样,失败以后不抛异常。

开空间例子。

Stack* st = (Stack*)operator new(sizeof(Stack));
operator delete(st);

那operator new有什么用呢?
是为了new而设计的。
new会调用operator new和构造函数。!!
operator new的作用是调用malloc,假如malloc然后抛异常。假如成功则继续进行构造函数。

构造函数的作用是初始化。

内存池

假如我们频繁的向堆申请和释放空间,这时候可以用一个内存池,因为内存池更快,可以提高效率。

因为堆他中间需要调用operator new,operator new还需要再调用mallo成本很高。

而内存池里面的内存就可以直接用,效率很快。

C++的重载new就可以解决直接调用new而导致效率低下的问题。

就是把new进行重载。来调用内存池。
把delete进行重载,直接调用内存池。

new和delete原理

new

  1. 调用operator new函数申请空间

  2. 在申请的空间上执行构造函数,完成对象的构造

delete的原理

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

new T[N]的原理

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

delete[]的原理

  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

定位new

针对一个空间,显示调用构造函数进行初始化
例子

Stack* obj  = (Stack*)operator new(sizeof(Stack));
new(obj)Stack(4);

//相当于
Stack* obj = new Stack(4);

malloc和new的区别是什么?

怎么考虑这个问题呢?
1.从用法上的区别
2.从底层的区别
malloc和free是函数,new和delete是操作符。

底层上的区别就是new会调用构造函数。
free会调用析构函数。

内存泄漏

什么是内存泄漏?

内存泄漏的意思是指针丢了。 是将内存还给了操作系统。

内存泄漏真正的危害是什么?

普通的内存泄漏我们不用怕,就算我们不释放。正常的程序结束,内存就会还给操作系统。

怕的是什么?
怕的是我们的电脑内存很小,或者长期运行的程序,这时候用的时候需要delete。

猜你喜欢

转载自blog.csdn.net/qq2466200050/article/details/128539452