前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++:26---动态内存管理new、delete

C++:26---动态内存管理new、delete

作者头像
用户3479834
发布2021-02-03 12:29:08
6460
发布2021-02-03 12:29:08
举报
文章被收录于专栏:游戏开发司机游戏开发司机

实在不好意思,到这里才给大家分享new和delete。

对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。

由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

new和delete是运算符不是函数

因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。new/delete不是库函数,而是运算符。

一、new关键字

  • new用来在内存中分配一块内存,new分配的对象是没有名称的,而是返回一个指向该对象的指针
代码语言:javascript
复制
int *p1=new int(1);  //pi指向一个动态分配的、初始化值为1的无名对象int *p2(new int(1)); //同上

二、new的值初始化规则

通用规则

  • 如果类型名后无括号:内置类型或组合类型的对象的值是未定义的,而类类型对象将用默认构造函数进行初始化
  • 如果类型名后有括号:则内置类型/组合类型/类类型都进行默认的初始化
代码语言:javascript
复制
br
代码语言:javascript
复制

C++11标准下的初始化规则

  • 允许使用花括号来指定初始化列表
  • 注意:花括号{}本质上是用来当一个列表的
代码语言:javascript
复制
vector<int> *vec = new vector<int> { 1, 2, 3, 4, 5 };int *arr = new int[3] {1, 2, 3};int *p = new int { 1 };

auto初始化

  • 如果使用圆括号初始化器,就可以使用auto来推断我们想要分配的对象类型
代码语言:javascript
复制
br
代码语言:javascript
复制

const初始化

  • 因为const对象为常量,初始化之后就不可以修改值了
  • 类类型初始化时可以不给出值,此时使用默认的构造函数值
  • 其它类型必须显示地给出初始化值(注意:编译器本质允许不给出值,但是定义之后就不能改变值了)
代码语言:javascript
复制
br

三、bad_alloc异常处理

  • bad_alloc异常出现的情景:如果一个程序可用的内存消耗完,那么new表达式就会失败。默认情况下,此时会抛出bad_alloc异常
  • bad_alloc异常的处理:如果不处理此异常那么程序就会中断。但是我们可以使用定位new的nothrow关键字来处理此异常
  • nothrow关键字:如果在new后面加一个圆括号并且加上“nothrow”,那么捕获到bad_alloc异常时,new返回空指针,而不抛出异常。我们称这种形式的new为“定位new”
  • bad_alloc和nothrow都定义在头文件new中
代码语言:javascript
复制
br

四、delete关键字

  • 用来释放一块动态申请的内存,解除指针与该指针所指向的内存之间的关系
  • 如果new的动态内存没有被释放(销毁),那么该动态内存就一直存在,会造成浪费

五、delete的使用规则

规则如下

  • 不能用来释放一块静态内存(栈区)
  • 用来释放动态申请的内存(new申请的堆区)
  • 允许释放一个空指针,不会出错
  • 释放一块已经释放的内存是错误的
  • 虽然const对象的值不能被改变,但是可以使用一个const动态对象
代码语言:javascript
复制
br
代码语言:javascript
复制

六、内存泄漏问题

  • 当我们使用new申请一块动态内存后,如果没有delete掉内存,那么就会造成内存泄漏

案例:

  • 定义一个factory函数,返回一个指向与Foo类型的动态内存指针
代码语言:javascript
复制
Foo* factory(T arg){...return new Foo(arg);}
  • 错误使用:下面的函数,调用了factory函数申请了一块动态内存,但是函数结束之后,没有释放p所指向的内存,于是就造成了内存的浪费
代码语言:javascript
复制
void use_factory(T arg){Foo *p=factory(arg);}
  • 正确使用:下面对use_factory函数进行了改造,在函数的最后delete掉了p所指向的动态内存,这样就不会导致内存的泄漏了
代码语言:javascript
复制
void use_factory(Foo arg){Foo *p=factoyr(arg);...delete p;}

七、delete指针之后的置空问题

  • 规则:当我们释放一个指针之后,该指针指向的是一个不确定的内存空间。因此,当释放指针之后,建议将指针值为空,来指示该指针不指向任何对象了
代码语言:javascript
复制
int *p=new int(30);  //申请......delete p;  //释放p=nullptr; //置位空
实际上,作为初级程序员容易犯这样的错误,认为只要delete之后,自己就不负责任了(实际上你这种行为和渣男有什么区别,明明已经和你的前女友分手了,你还留着人家的各种信息和人家藕断丝连,偶尔还用保存着前女友的某一个把柄来威胁前女友。)我曾经犯过这样错误,导致游戏服务器的一个全球跨服战的宕机,原因就是我在delete之后,没有将指针指向的内容没有置为NULL,导致我后面又对指针指向的成员进行非法访问,宕机,我半夜两天起来远程连接公司电脑修BUG,到现在记忆犹新,心脏瞬间不好了。

八、多个指针同指一块内存的使用

特点

  • ①多个指针指向同一内存时,释放其中一个指针,其他指针均变为无效
  • ②将一个指针值为空,只与该指针有关,与其他指针无关
代码语言:javascript
复制
br

九、shared_ptr与new的使用

使用规则

  • ①我们可以使用将shared_ptr类对象指向一个new所申请的动态内存
  • ②new申请的动态内存的使用、释放等规则仍然符合shared_ptr类的使用规则

使用语法

  • 因为智能指针的构造函数是explicit的。因此:我们不能将一个内置指针隐式地转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针
代码语言:javascript
复制
shared_ptr<int> p=new int(1024);   //错误shared_ptr<int> p2(new int(1024)); //正确:使用直接初始化
  • 动态内存作为返回值时的使用手法:限于上面的使用语法:一个返回shared_ptr的函数不能在其返回语句中隐式转换为一个普通指针
代码语言:javascript
复制
shared_ptr<int> clone(int p){return new int(p); //错误}shared_ptr<int> clone(int p){return shared_ptr<int>(new int(p)); //正确}

十、new传参时与shared_ptr的关系

  • 当一个函数的参数是shared_ptr类时,有以下规则:
    • 函数的调用是传值调用
    • 调用函数时,该shared_ptr类所指向的对象引用计数加1
    • 但是函数调用完成之后,shared_ptr类自动释放,对象的引用计数又减1
代码语言:javascript
复制
void process(shared_ptr<int> ptr){ ... }shared_ptr<int> p(new int(42)); //初始化一个智能指针对象pprocess(p);  //p所指的对象引用计数加1//process函数调用之后,p所指的引用计数减1int i=*p; //正确

函数参数使用时与new的关系

  • 因为shared_ptr类会在生存周期结束之后,将引用计数减1,当引用计数为0时,会释放内存空间
  • 下面是一个特殊的应用场景,需要注意
代码语言:javascript
复制
void process(shared_ptr<int> ptr){ ... }int *x(new int(1024));process(x);                  //错误,不能将int*转换为一个shared_ptr<int>process(shared_ptr<int>(x)); //合法的,但是process函数返回之后内存会被释放int j = *x;                  //错误,x所指的内存已经被释放了

十一、异常处理

  • 当程序发生异常时,我们可以捕获异常来将资源被正确的释放
  • 但是如果没有对异常进行处理,则有以下规则:
    • shared_ptr的异常处理:如果程序发生异常,并且过早的结束了,那么智能指针也能确保在内存不再需要时将其释放
    • new的异常处理:如果释放内存在异常终止之后,那么就造成内存浪费
代码语言:javascript
复制
代码语言:javascript
复制
br
代码语言:javascript
复制

十二: 动态数组的初始化

  • 默认情况下,new分配的对象都有默认初始化
代码语言:javascript
复制
代码语言:javascript
复制
br
代码语言:javascript
复制

十三、动态数组的一些注意事项

  • ①因为new申请的数组是动态地,因此不能使用begin()和end()函数对动态数组进行操作
  • ②不能使用for each对数组进行遍历,但是可以使用for循环操作动态数组
代码语言:javascript
复制
int *p1 = new int[3]{1,2,3};  //动态数组int p2[3]= { 1,2,3 };  //普通数组
for (int i = 0; i < 3; i++) //正确cout <<p1[i] << endl;for each (int var in p2)//错误{
}

十四、动态分配一个空数组是合法的

  • 动态分配一个数组是合法的,new返回一个合法的非空指针,此指针保证与new返回的其它任何指针都不相同。但是不能对此指针解引用
  • 普通数组定义一个空数组是不允许的
代码语言:javascript
复制
int arr[0];  //错误int *p = new int[0];  //正确

十五、动态数组的释放

  • 使用delete释放,并且在数组名前需要加上“[]”
  • 释放的顺序:数组中的元素按照逆序销毁,即最后一个元素首先被销毁,然后倒数第二个....以此类推
代码语言:javascript
复制
typedef int arrT[10];int *a=new arrT;delete []a;实际上我要解释下:delete a;         仅释放了a指针指向的全部内存空间 但是只调用了a[0]对象的析构函数剩下的从a[1]到a[9]这9个用户自行分配的m_cBuffer对应内存空间将不能释放 从而造成内存泄漏。delete [] a;    调用使用类对象的析构函数释放用户自己分配内存空间并且释放了a指针指向的全部内存空间所以总结下就是,如果ptr代表一个用new申请的内存返回的内存空间地址,即所谓的指针,那么:delete   ptr   代表用来释放内存,且只用来释放ptr指向的内存。 
delete[]   rg   用来释放rg指向的内存,!!还逐一调用数组中每个对象的destructor 对于像int/char/long/int*/struct等等简单数据类型,由于对象没有destructor,所以用delete 和delete [] 是一样的!但是如果是C++对象数组就不同了!我将会在接下来的第28节说到这块
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实在不好意思,到这里才给大家分享new和delete。
  • 一、new关键字
  • 二、new的值初始化规则
    • 通用规则
      • C++11标准下的初始化规则
        • auto初始化
          • const初始化
          • 三、bad_alloc异常处理
          • 四、delete关键字
          • 五、delete的使用规则
            • 规则如下
            • 六、内存泄漏问题
              • 案例:
              • 七、delete指针之后的置空问题
              • 八、多个指针同指一块内存的使用
              • 九、shared_ptr与new的使用
                • 使用语法
                • 十、new传参时与shared_ptr的关系
                  • 函数参数使用时与new的关系
                  • 十一、异常处理
                  • 十三、动态数组的一些注意事项
                  • 十四、动态分配一个空数组是合法的
                  • 十五、动态数组的释放
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档