C++ 动态内存

         C++ 程序中的内存分为栈和堆两个部分:

  • 栈:在函数内部声明的所有变量都将占用栈内存;
  • 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。

         堆与栈的详细请参考:一文读懂堆与栈的区别_堆和栈的区别_恋喵大鲤鱼的博客-CSDN博客

         由于很多时候无法提前预知存储某个定义变量中的特定信息需要多少内存,所需内存的大小需要在运行时才能确定。因此,在 C++ 中可以使用 new 运算符为给定类型的变量在运行时分配堆内的内存,并返回所分配空间的地址。当不再需要动态分配的内存空间时,使用 delete 运算符释放之前由 new 运算符分配的内存。

new 和 delete 运算符

         使用 new 运算符动态分配任意数据类型内存的通用语法:

new Data_Type;

         Data_Type 是包括数组在内的任意内置的数据类型,也可以是包括类或结构在内的自定义的任何数据类型。例如,定义一个指向 double 类型的指针,然后请求内存,该内存在执行时被分配:

double* pvalue = NULL;   // 初始化为 null 的指针
pvalue  = new double;    // 为变量请求内存
//也可以直接分配并赋值给指针:double* pvalue = new double;

         当不再需要已经动态分配内存的变量时,使用 delete 操作符释放它所占用的内存(注意:pvalue指针变量本身并没有被销毁,销毁的是它指向的变量),如下所示: 

delete pvalue;  // 释放 pvalue 所指向的内存

         如果自由存储区已被用完,可能无法成功分配内存。所以建议检查 new 运算符是否返回 NULL 指针,并采取以下适当的操作:

double* pvalue  = NULL;
if(!(pvalue  = new double))  // 返回 NULL 指针
{cout << "Error: out of memory." <<endl;exit(1);
}

         演示使用 new 和 delete 运算符:

#include <iostream>
using namespace std;int main ()
{double* pvalue  = NULL;     // 初始化为 NULL 的指针pvalue  = new double;       // 为变量请求内存*pvalue = 10086.11;         // 在分配的地址存储值cout << "Value of pvalue : " << *pvalue << endl;delete pvalue;              // 释放内存return 0;
}

         执行结果如下:

Value of pvalue :10086.1

         malloc() 函数在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。

数组的动态内存分配

一维数组

         假设为一个能存储 20 个字符的一维数组动态分配内存,如下所示:

char* pvalue = NULL;      // 初始化为 null 的指针
pvalue = new char [20];   // 为变量请求内存

         删除创建的数组,语句如下:

delete [] pvalue;   // 删除 pvalue 所指向的数组

二维数组

         例如,下面是一个二维数组a[3][4],包含 3 行和 4 列:

         二维数组array[m][n]:

/* 假定数组array[m][n]第一维长度(行)为 m,第二维长度(列)为 n */
int** array;               // 声明一个指向int类型指针的指针
array = new int *[m];      // 分配m个存储int*类型的空间,array指向第一个存储int*的地址/* 为每一行分配空间 */
for( int i=0; i<m; i++ )   
{array[i] = new int [n];
}/* 释放每一行 */
for( int i=0; i<m; i++ )
{delete [] array[i];
}delete [] array;       

         实例:

#include<iostream>
using namespace std;int main()
{int **a;int i,j;a = new int *[4];for(i=0;i<4;i++){a[i]=new int [8];}for(i=0;i<4;i++)   // 初始化数组{for(j=0;j<8;j++){a[i][j]=((i+1)*(j+1));}}for(i=0;i<4;i++)   // 打印数组数据{for(j=0;j<8;j++){			cout<<a[i][j]<<"\t";if((j==7)&&(i!=3))   // 每行8个,再换行cout<<endl; }}for(i=0;i<4;i++)   // 释放分配的内存{delete [] a[i];}delete [] a;return 0;
}

         执行结果如下:

1       2       3       4       5       6       7       8
2       4       6       8       10      12      14      16
3       6       9       12      15      18      21      24
4       8       12      16      20      24      28      32

三维数组

/* 假定数组第一维为 m, 第二维为 n, 第三维为 h */ 
int ***array;
array = new int **[m];for( int i=0; i<m; i++ )
{array[i] = new int *[n];for( int j=0; j<n; j++ ){array[i][j] = new int [h];}
}for( int i=0; i<m; i++ )   //释放内存
{for( int j=0; j<n; j++ ){delete[] array[i][j];}delete [] array[i];
}delete[] array;

         实例:

#include<iostream>
using namespace std;int main()
{int i,j,k;        // a[2][3][4]int ***a;a = new int **[2];for(i=0;i<2;i++) {a[i]=new int *[3];for(j=0;j<3;j++){a[i][j]=new int [4];}}for(i=0;i<2;i++)  //初始化,并输出{for(j=0;j<3;j++){for(k=0;k<4;k++){a[i][j][k] = i+j+k;cout<<a[i][j][k]<<" ";}cout<<endl;						}cout<<endl;}for(i=0;i<2;i++)  //释放内存{for(j=0;j<3;j++){delete [] a[i][j];}			}for(i=0;i<2;i++){delete [] a[i];}delete [] a;return 0;
}

         执行结果如下:

0 1 2 3
1 2 3 4
2 3 4 5

1 2 3 4
2 3 4 5
3 4 5 6
 

         补充:由于对象没有析构函数(destructor),“ delete [] p ”和 “ delete p ” 对于像 int/char/long/int*/struct 等简单数据类型的释放没有太大区别。但对于C++ 对象数组,就不同了:

  • delete a;     仅释放了a指针指向的全部内存空间 ,但是只调用了a[0]对象的析构函数,其他分配的内存空间将不能释放,从而造成内存泄漏;
  • delete [] a;  调用使用类对象的析构函数释放用户自己分配内存空间,并释放了a指针指向的全部内存空间。

对象的动态内存分配

         对象与简单的数据类型没有什么不同,如下所示,为一个包含3个 Box 对象的数组分配内存,构造函数将被调用 4 次。当删除这些对象时,析构函数也将被调用相同的次数:

#include<iostream>
using namespace std;class Box
{
public:Box(){cout<<"调用构造函数!"<<endl;}~Box(){cout<<"调用析构函数!"<<endl;}
};int main()
{Box* BoxArray = new Box[3];delete [] BoxArray;return 0;
}

         执行结果如下:

调用构造函数!
调用构造函数!
调用构造函数!
调用析构函数!
调用析构函数!
调用析构函数!

new 和 malloc 的区别

         new 的功能是在堆区新建一个对象,并返回该对象的指针。 "新建对象" 的意思就是将调用该类的构造函数,因为如果不构造的话,就不能称之为一个对象。

         malloc 只是机械的分配一块内存,用 mallco 在堆区创建一个对象不会调用构造函数。严格说来用 malloc 不能算是新建了一个对象,只能说是分配了一块与该类对象匹配的内存而已,然后强行把它解释为 “这是一个对象”,因此,也不存在调用构造函数的说法。

         同样的,用 delete 去释放一个堆区的对象,会调用该对象的析构函数。

         用 free 去释放一个堆区的对象,不会调用该对象的析构函数。

         实例:

#include <iostream>
#include <malloc.h> // 需要用到malloc()函数using namespace std;class Test
{
private:int num1;int num2;
public:Test(){num1 = 10;num2 = 20;}		void Print(){cout<<num1<<"\t" <<num2<<endl;		}
};int main()
{/****用malloc()函数在堆区分配一块内存空间,然后用强制类型转换将该块内存空间*解释为是一个Test类对象,但不会调用Test的默认构造函数	  */ Test* pObject1 = (Test*)malloc(sizeof(Test));pObject1->Print();/* 用new在堆区创建一个Test类的对象,会调用Test类的默认构造函数 */Test* pObject2 = new Test;pObject2->Print();return 0;
}

         由于pObject1没有调用Test类的默认构造函数,所以在pObject1所指的对象中,num1与num2都是malloc()分配地址上原有的垃圾值,并非是由构造函数定义的初始值(10 or 20)。而pObject2所指的对象中,num1与num2显然是经过了构造后的值,执行结果如下:

1922960 0
10      20

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/118290.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[二分查找] 旋转数组

1. &#xff08;严格递增序列&#xff09;旋转数组的元素查找 简单来说分为三种情况进行分析 1. 整个旋转数组单调递增 根据x和A[mid]的大小关系&#xff0c;更迭范围。 // 1. 整个旋转数组单调递增if (A[left]<A[right]){if (A[mid] x)return mid;else if (x < A[mid]…

【Linux】Libevent相关小知识总结

Libevent是基于事件的&#xff0c;也就是说&#xff0c;相当于去注册一个事件&#xff0c;当这个事件发生的话&#xff0c;那么就会调用回调函数。

25.CSS自定义形状按钮与悬停效果

效果 源码 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>CSS Custom Shape Button</title><link rel="stylesheet" href="style.css"> </head> <body&…

操作系统的发展和分类

注意&#xff1a;每个阶段的主要优点都是解决了上个阶段的缺点 1.手工操作阶段 概括&#xff1a;一个用户在一段时间内独占全机&#xff0c;导致资源利用率极低&#xff0c;用户输入指令给机器&#xff0c;然后机器运行响应给用户。 2.批处理阶段 2.1单道批处理系统 优点&…

docker命令学习

docker vscode插件出现的问题 docker命令 docker images &#xff08;查看所有的镜像&#xff09; docker ps -a &#xff08;查看所有的容器&#xff09; docker ps &#xff08;查看运行的容器&#xff09; docker run imageID docker run --gpus all --shm-size8g -it imag…

A Mathematical Framework for Transformer Circuits—Part (1)

A Mathematical Framework for Transformer Circuits 前言Summary of ResultsREVERSE ENGINEERING RESULTSCONCEPTUAL TAKE-AWAYS Transformer OverviewModel SimplificationsHigh-Level ArchitectureVirtual Weights and the Residual Stream as a Communication ChannelVIRTU…

16.WebSocket聊天室

基于SpringBoot 2.6.11 1.WebSocket WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议&#xff0c;可以在html页面直接使用。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在 WebSocket A…

java对时间序列根据阈值进行连续性分片

问题描述&#xff1a;我需要对一个连续的时间戳list进行分片&#xff0c;分片规则是下一个数据比当前数据要大于某一个阈值则进行分片&#xff1b; 解决方式&#xff1a; 1、输入的有顺序的list &#xff0c;和需要进行分片的阈值 2、调用方法&#xff0c;填入该排序的list和阈…

docker安装redis,并挂载配置文件

1&#xff1a;下载镜像&#xff0c;不添加版本 默认下载最新的 docker pull redis下载成功后如图所示 2&#xff1a;下载redis配置文件&#xff0c;我是在docker中下载的&#xff0c;也可以使用文件上传工具将配置文件上传到自己指定的目录。 首先需要安装wget&#xff0c;否…

蓝牙发展现状

目录 一、产品分类1、Bluetooth经典2、Bluetooth低能耗(LE)3、二者差异 二、出货量三、未来需要加强的方向四、技术行业细分五、学习资料1、蓝牙官网2、大神博客——于忠军 一、产品分类 1、Bluetooth经典 Bluetooth Classic无线电&#xff0c;也被称为Bluetooth 基本速率/增强…

QT 界面相关操作

1> 创建自定义类时需要指定父类 2> 第一个界面的相关操作 #include "widget.h" #include<iostream> //printf #include<QDebug> //qDebuf #include<QIcon> //图标的头文件 using namespace std; //coutWidget::Widget(QWidget *…

go gin 自定义验证

我们上一篇已经提到了gin中binding时候可以指定json字段大小等限制&#xff0c;但是那个错误却是英文的&#xff0c;现在想搞成中文的&#xff0c;以便前端可读&#xff0c;demo如下 package mainimport ("net/http""reflect""github.com/gin-gonic/…

基于JAVA SpringBoot互联网就医门诊挂号管理系统

摘要 随着时代的发展,无线互联网技术的应用和普及给人们的生活带来了极大的改变,现在信息技术不仅可以提高我们的工作效率,还能有效的规避一些错误风险,节约人力成本。我国国民一方面对健康的要求越来越重视了&#xff0c;另一方面现代人的健康问题日益严重&#xff0c;所以医院…

Linux系统调试中出现核心转储(core dump)的问题

​ 大家好&#xff0c;我是ST。今天主要分享一下&#xff0c;Linux应用程序发生Segmentation fault段错误时&#xff0c;如何利用core dump文件定位错误。 核心转储 在 Linux 系统中&#xff0c;常将“主内存”称为核心(core)&#xff0c;而核心映像(core image) 就是 “进…

4.1 链式栈StackT

C关键词&#xff1a;内部类/模板类/头插 C自学精简教程 目录(必读) C数据结构与算法实现&#xff08;目录&#xff09; 栈的内存结构 空栈&#xff1a; 有一个元素的栈&#xff1a; 多个元素的栈&#xff1a; 成员函数说明 0 clear 清空栈 clear 函数负责将栈的对内存释放…

arduino仿真 SimulIDE1.0仿真器

SimulIDE 是一个开源的电子电路模拟器&#xff0c;支持模拟各种电子元器件的行为&#xff0c;可以帮助电子工程师和爱好者进行电路设计和测试。以下是 SimulIDE 的安装和使用说明&#xff1a; 安装 SimulIDE SimulIDE 可以在 Windows、Linux 和 Mac OS X 等操作系统上安装。您…

仿京东 项目笔记1

目录 项目代码1. 项目配置2. 前端Vue核心3. 组件的显示与隐藏用v-if和v-show4. 路由传参4.1 路由跳转有几种方式&#xff1f;4.2 路由传参&#xff0c;参数有几种写法&#xff1f;4.3 路由传参相关面试题4.3.1 路由传递参数&#xff08;对象写法&#xff09;path是否可以结合pa…

Qt应用开发(基础篇)——进度对话框 QProgressDialog

一、前言 QProgressDialog类继承于QDialog&#xff0c;是Qt设计用来反馈进度的对话框。 对话框QDialog QProgressDialog提供了一个进度条&#xff0c;表示当前程序的某操作的执行进度&#xff0c;让用户知道操作依旧在激活状态&#xff0c;配合按钮&#xff0c;用户就可以随时终…

【C++入门】string类常用方法(万字详解)

目录 1.STL简介1.1什么是STL1.2STL的版本1.3STL的六大组件1.4STL的缺陷 2.string类的使用2.1C语言中的字符串2.2标准库中的string类2.3string类的常用接口说明 &#xff08;只讲解最常用的接口&#xff09;2.3.1string类对象的常见构造2.3.2 string类对象的容量操作2.3.3string…

nnUNet v2数据准备及格式转换 (二)

如果你曾经使用过nnUNet V1&#xff0c;那你一定明白数据集的命名是有严格要求的&#xff0c;必须按照特定的格式来进行命名才能正常使用。 这一节的学习需要有数据&#xff0c;如果你有自己的数据&#xff0c;可以拿自己的数据来实验&#xff0c;如果没有&#xff0c;可以用十…