一、Effective C++条款16:成对使用new和delete时采用相同的形式

通常我们使用new和delete有两种情形,第一,动态的为单一对象分配内存,第二,动态的创建数组。new和delete的使用需遵循许多规则,这里着重理解相同形式这一关键词。且看下面的动作有什么错?

std::string *stringArray = new std::string[100];

...
delete stringArray;
乍看起来,同时使用了new和delete,似乎没什么错误。这里使用new在运行阶段动态分配了100string对象,但是仅使用delete只释放掉了单一对象的所占用的内存,其中99个string对象不太可能被适当的删除,因为它们的析构函数没有被调用。

请注意:这里仅仅同时使用了new和delete,但并没有满足相同的形式这一条件。我们所面临的问题可以更简单的叙述为:即将被删除的那个指针,所指的是单一对象还是对象数组?另外,请尽量使用C++11智能指针,以防止写代码时忘记释放掉new分配的内存而导致内存泄漏。

请记住

如果在new表达式中使用了[],必须在相应的delete表达式中使用[]。如果在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。确保“同时使用、相同形式”。

二、使用new动态分配内存

为单一对象(可以是数据结构,基本类型或类)获得并指定分配内存的通用格式如下:

typeName *pointter_name  = new typeName;

例如,在运行阶段为一个int值分配未命名的内存,并使用指针来访问这个值,可以写为:int * pn = new int;

程序员告诉new要为int类型的数据类型分配内存,new根据这种数据类型找到长度(在这里int为4个字节)正确的内存块,并返回内存块的地址,程序员负责将该地址赋给一个指针(pn是被声明为指向int的指针,现在pn是一个地址,而*pn为存储在那里的值)。

请看下面的例子:

int *pt = new int;

*pt = 1001;

double *pd = new double;

*pd = 3.14;

则有:如果这里int占4个字节,double占8个字节,则sizeof(pt)为4,sizeof(*pt)为4。sizeof(pd)为4,sizeof(*pd)为8。这里,指向int的指针的长度与指向double的指针相同,它们都是地址,但由于声明了指针的类型,因此程序知道*pd为8个字节的double值,*pt为4个字节的int值。

三、使用new和delete时要遵循的规则

  • 不要使用delete释放不是new分配的内存。
  • 不要使用delete释放同一个内存两次。
  • 使用new[]为数组分配的内存一定要使用delete[]释放,不能使用delete;new为单个对象分配的内存一定使用delete来释放。
  • 对空指针delete是安全的。

四、使用动态数组

创建动态数组后,如何使用数组中的元素?

int *pt = new int[10]; // get  a block of 10 ints

如果这里int占4个字节,上述语句中sizeof(pt)为4个字节,sizeof(*pt)为4 × 10个字节。那么如何使用数组中的元素呢?其实和普通数组一样。

double *pd = new double[3];

pd[0] = 1.0;

pd[1] = 2.0;

pd[2] = 3.0;

那么,pd[0](或*pd) 的值为1.0。如果有pd = pd + 1;, 那么pd[0]的值为2.0。另外请注意,不能修改数组名(常量),但指针(pd)为变量,因此可以修改它的值。而且pd+1的效果是指指针指向数组第一个元素的下一个位置,即第二个元素,因此pd[0]为2.0。

五、杂项

指针和数组等价的原因在于指针算术和C++内部处理数组的方式。首先我们来看一下算术,将整型变量加1后,其值将增加1;但将指针变量增加1后,增加的量等于它指向的数据类型的字节数,如:将指向double的指针加1后,如果系统对double使用8个字节存储,则其值增加8。另外,C++将数组名解释为数组第一个元素的地址。

double arrD[3] = {1.0, 2.0, 3.0};

short arrSh[3] = {1, 2, 3};

double *pd = arrD; // 等价于&arrD[0]

short *psh = &arrSh[0];

...

假设double占4个字节,short占2个字节,则:arrD[0] ,*arrD和*(arrD + 0)等价,其值为1;*psh为1。

sizeof(arrD)为3 × 8 = 24,sizeof(pd)为4,sizeof(*pd)为8。

sizeof(arrSh)为3 × 2 = 6,sizeof(psh)为4,sizeof(*psh)为2。

这是因为 指针为指向变量的地址,且地址占4个字节;对指针的解引用为指针指向变量的值,所占内存谓该值所属类型所占的字节数。

注意:虽然数组名被解释为指向数组第一个元素的地址,但不能修改数组名,是因为数组名为常量;可以修改指针,是因为指针(指向变量的地址)为变量。

pointerName = pointerName + 1; // valid

arrayName = arrayName + 1; // not alllowed

数组的地址

数组名被解释为其第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址。

short tell[10];     // tell为20字节的数组

cout << tell ;    // &tell[0] 即数组第一个元素的地址,占2个字节的内存块的地址

cout << &tell;  //  整个数组的地址,从数字上说,该地址和tell的地址相同,但是&tell为占20个字节的内存块的地址

总之,使用new来创建数组以及使用指针来访问不同的元素时,只需要把指针当成数组名对待即可。

new和delete与内存分配相关推荐

  1. linux delete内存不下降_linux内存分配管理

    linux内存分配管理 一.前言 作为从事与C/C++程序开发人员,我们一直需要很好的管理内存,申请和释放:可能很多只知道使用malloc.new去申请,使用free.delete去释放,但是,去根究 ...

  2. 【C++】动态内存分配详解(new/new[]和delete/delete[])

    原文链接:https://blog.csdn.net/qq_40416052/article/details/82493916 代码还是原文看着方便,在此不调整格式了 一.为什么需要动态内存分配? 在 ...

  3. C/C++ 内存分配方式,堆区,栈区,new/delete/malloc/free

    内存分配方式有三种: [1] 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量, static 变量. [2] 在栈上创建.在执行函数时,函数内局 ...

  4. C语言的变量的内存分配

    今晚看了人家写的一个关于C语言内存分配的帖子,发现真是自己想找的,于是乎就收藏了... 先看一下两段代码: char* toStr() {char *s = "abcdefghijkl&qu ...

  5. 释放变量所指向的内存_C++动态内存分配(学习笔记:第6章 15)

    动态内存分配[1] 动态申请内存操作符 new new 类型名T(初始化参数列表) 功能: 在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值. 结果值: 成功:T类型的指针,指向 ...

  6. Eigen向量化内存对齐/Eigen的SSE兼容,内存分配/EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    1.总结 对于基本数据类型和自定义类型,我们需要用预编译指令来保证栈内存的对齐,用重写operator new的方式保证堆内存对齐.对于嵌套的自定义类型,申请栈内存时会自动保证其内部数据类型的对齐,而 ...

  7. 栈区和堆区内存分配区别

    一直以来总是对这个问题的认识比较朦胧,我相信很多朋友也是这样的,总是听到内存一会在栈上分配,一会又在堆上分配,那么它们之间到底是怎么的区别呢?为了说明这个问题,我们先来看一下内存内部的组织情况. 从上 ...

  8. 可变分区存储管理实验报告总结_操作系统实验报告-可变分区存储管理方式的内存分配回收...

    一.实验目的 ( 1 )深入了解可变分区存储管理方式的内存分配回收的实现. 二.实验内容 编写程序完成可变分区存储管理方式的内存分配回收,要求有内存空间分配表, 并采用最优适应算法完成内存的分配与回收 ...

  9. 内存分配管理 自定义

    在内存管理中,经常需要自定义内存分配释放,也就是需要定义new 和 delete. 通常为了有针对性的对某些对象的内存分配进行管理,定义一个内存管理基类 1.定义 1 struct Memory 2 ...

最新文章

  1. 电脑键盘功能介绍_远程操控软件介绍
  2. python制作网页的步骤_使用httplib模块来制作Python下HTTP客户端的方法
  3. slf4j 如何返回堆栈_重学JS系列 - JS 调用堆栈
  4. Android学习笔记-----一个很好用的搜索网站,你懂的
  5. boost::shared_from_raw相关的测试程序
  6. Struts2 控制台不打印异常的解决方案
  7. linux中常用的shell脚本,Linux常用shell脚本
  8. TQ210——S5PV210启动过程
  9. 如何用c语言从txt文件中读取数据
  10. 基于JAVA+SpringMVC+Mybatis+MYSQL的办公用品销售平台进销存系统
  11. linux线程同步 eventfd,用 eventfd 在线程之间通信
  12. Codeforces-Div312
  13. java 判断是否base64_判断一个字符串是否进行了base64加密
  14. Opencv绘制HSV颜色直方图
  15. hive 复合结构Map、Struct详解
  16. java http远程调用接口下载文件
  17. React Native 参考资料 (转自简书)
  18. 流媒体后视镜方案关键技术--调节后视图像显示范围
  19. 批处理的加密,批处理乱码的查看
  20. CTSC1999补丁VS错误题解

热门文章

  1. 彻底删除MySQL57服务
  2. c#实现循环输入商品编号和购买数量,结账时应付金额并找零
  3. 领域驱动设计(DDD)部分核心概念的个人理解
  4. 51.【Java String方法的小结】
  5. 读书笔记--高效能人士的七个习惯+执行4原则
  6. html元素背景与雪碧图
  7. 模型预处理均值、方差的理解与使用
  8. python小组项目总结报告_项目总结报告范文78922
  9. 软件质量保证与测试技术实验报告(三)静态测试
  10. 数据库MySQL之如何添加、删除列?