数据结构与算法基础(王卓)(8)附:关于new的使用方法详解

news2025/1/21 18:47:00

part 1:

C++中new的用法(不过就是)如下(几种用法):

1: new<数据类型>

分配:

指定类型的,大小为1的,内存空间;

	int *i = new int;
	//注意!如果写:
	//int i = new int;
	//不行,会报错

2: new<数据类型>(<初值> )

分配:

指定类型的,大小为1的,内存空间;用括号中的值初始化变量

	int *i = new int(2);

3:new<数据类型>[<内存单元个数>]

分配:

指定类型的,大小为(方框内数字)n的,内存空间

用方框中的数字n:初始化空间的大小(数组元素个数)

内存形式为数组

	int* i = new int[5];
	char* i = new char[5];

附:part1

定义多维数组变量或数组对象,产生一个指向数组首个元素的指针

返回的类型:保持 除最左边维数外的 所有维数

int *p1 = new int[10];   
返回一个指向int型的指针int*  

int (*p2)[10] = new int[2][10]; 
返回一个,指向int[10]这样的一维数组的,指针int (*)[10]

int (*p3)[2][10] = new int[5][2][10];     

返回一个指向二维数组int[2][10]这种类型的指针int (*)[2][10]

 但是我们在这里需要特别提醒(注意):

在这里我们无论是上述三种情况中的哪一种情况

在new语句的赋值语句的左侧的变量,无论如何情况:

这个变量都是,都只能是,都必须是一个指针!!!

而并不像是在我们后面在数据结构的

数据结构与算法基础(王卓)(5):关于(单)链表较复杂的操作_宇 -Yu的博客-CSDN博客

当中说的那样,开辟的空间类型可以直接在赋值语句左侧直接有该类型的变量

左侧只能放置指向该类型的指针!!!


附:part2

C++中new动态创建二维数组的一般格式:

TYPE (*p)[N] = new TYPE [][N];

p的类型:TYPE*[N];即:指向一个有N列元素数组的指针

不指定数组的列数(创建二维数组):

 int **p;

例:

	int **p = new int* [10]; 
	//int*[10]表示一个有10个元素的指针数组
	
	for (int i = 0; i != 10; ++i)
		p[i] = new int[5];


不写new和用new的部分区别:

必须需要指针接收,一处初始化,多处使用

前面所说的:

在new语句的赋值语句的左侧的变量,无论如何情况:

这个变量都是,都只能是,都必须是一个指针!!!

指的就是我们这里说的,用来接收的指针

需要delete销毁


new创建对象直接使用堆空间

而局部不用new定义对象则使用栈空间


new对象指针用途广泛,比如作为函数返回值、函数参数等


频繁调用场合并不适合new,就像new申请和释放内存一样

例:

用new创建对象:(pTest:用来接收对象指针)

	int* pTest = new int();
	delete pTest;

不用new创建对象:(直接用声明定义)

	int mTest;

不用new创建对象,使用完后不需要手动释放(写delete函数)

类的析构函数会自动执行(释放操作)

而new申请的对象,只有调用到delete时才会执行析构函数

如果程序退出而没有执行delete则会造成内存泄漏

用new创建对象时释放类指针:

	int* pTest = false;

类指针如果未经过对象初始化,则不需要delete释放


part 2:

中,需要建一个新表来返回两表合并后的结果(最终合并后的表):


1.1:

最开始我们想得很简单,就是利用实参把新表传回去:

int Merge(Sqlist A, Sqlist B, Sqlist& C)
{
    Sqlist* C = new Sqlist;

    //验证我们开辟空间出来以后的C
    //是一个线性表还是一个指针
    Sqlist D;
    D = C;

    return true;
}

但是此时的这个验证开辟空间的C是线性表还是指针的设计还存在一个问题(纰漏):

我们哪怕删了这里开辟空间的语句

    Sqlist* C = new Sqlist;

依然不影响最后程序运行的结果(成功)


1.2:

取消形参线性表C,验证我们单独开辟创建的C是指针还是线性表:

int Merge(Sqlist A, Sqlist B)//, Sqlist& C)
{
    Sqlist* C = new Sqlist;

    Sqlist D;
    D = C;
}

结果:

 这无疑说明了,我们new的C是一个指针,依然符合我们前面所介绍的new语句的基本格式规范

所以,想要程序运行成功(修改这个错误),只需要将其中的赋值语句改为:

    D = *C;

即可正确运行;注意(切记):

这里在C前面加的符号必须是“ * ”,而不能是“&”

我们总是习惯性地在地址前面去加“&”,那是我们忘了:

&是取址运算符

*才是取值运算符


2:

我们也可以直接设置返回合并以后的表:(这样就不用再设置一个实参来返回新表了)

Sqlist Merge(Sqlist A, Sqlist B)
{
    Sqlist* C = new Sqlist;
    return *C;
}

3:

前面我们一直都在自己写开辟空间的函数语句,但是到这里我们才突然发现:

我们前面已经写了初始化的InitList( )函数了,所以直接写初始化函数就行:

int Merge(Sqlist A, Sqlist B, Sqlist& C)
{
    InitList(C);
    return true;
}


当然,我们也可以根据(参考)前面我们写的初始化的函数的定义,手写初始化语句:

4.1:

原InitList函数中指针传值的写法:(指针传值)

int Merge(Sqlist A, Sqlist B, Sqlist *C)
{
    C.elem = (Poly*)malloc(MAXlength * sizeof(Poly*));
    if (!C.elem)
        exit(OVERFLOW);
    C.length = 0;
    return OK;
}

4.2:

原InitList函数中引用传值的写法:(引用传值)

int Merge(Sqlist A, Sqlist B, Sqlist& C)
{
    C.elem = new Poly[100]; //在堆区开辟动态内存
    if (!C.elem)//分配失败        
        exit(OVERFLOW);
    C.length = 0;
    return OK;
}

关于线性表的初始化的每一步操作的详细解释和分析,详见:

数据结构与算法基础(王卓)(2):线性表的初始化_宇 -Yu的博客-CSDN博客(的结尾)


part 3:

new:

详见:C语言日记 26 指针与函数,动态存储分配_宇 -Yu的博客-CSDN博客

例6-10 开辟空间以存放一个二维数组。

源程序:

#include <iostream>
using namespace std;
int main()
{
	int i, j;
	int** p;
	p = new int* [4];
	//开始分配4行8列的二维数据
	for (i = 0; i < 4; i++)p[i] = new int[8];
	for (i = 0; i < 4; i++)	//给二维数组放入数据
	{
		for (j = 0; j < 8; j++)p[i][j] = j * i;
	}
	//打印数据
	for (i = 0; i < 4; i++)
		for (j = 0; j < 8; j++)
		{
			if (j == 0) cout << endl;
			cout << p[i][j] << "\t";
		}
	for (i = 0; i < 4; i++)
		delete[] p[i];
	delete[] p;
	return 0;
}//1

结果:

  这里的

    p = new int* [4];

他想表示的,应该就是所谓

    int(*p)[8];

的这个意思,但是对于开辟二级(维)指针空间的使用规则和规范,书上没有明确说明,比如:

    p = new int(*p) [4];

就不行:

所以,开辟这种多维指针类型的二级(维)指针空间的使用规则和规范到底是什么?!

当使用new运算符定义一个多维数组变量或数组对象时,它产生一个指向数组第一个元素的指针

返回的类型保持了除最左维数外的所有维数。例如:  

int* p1 = new int[10];

开辟了一个一维数组,返回一个指向该int型存储空间的地址(即指针)(int*  )

int(*p2)[10] = new int[2][10];

开辟了一个二维数组, (去掉最左边那一维[2], 剩下int[10], 所以)

返回的是一个指向int[10]这种一维数组型存储空间的地址(即指针)(int (*)[10] ) 


下面,我们再延展(拓展)至三维:

int(*p3)[2][10] = new int[5][2][10];

开辟了一个三维数组, (去掉最左边那一维[5],剩下int[2][10], 所以)返回的是一个指向int[2][10]这种二维数组型存储空间的地址(即指针)(int (*)[2][10]) 

C++中用new动态创建二维数组的一般格式为:

TYPE (*p)[N] = new TYPE [][N];

TYPE:类型,N:二维数组的列数。

采用这种格式,列数必须指出,而行数无需指定。在这里,p的类型是TYPE*[N],即是指向一个有N列元素数组的指针。


还有一种方法,可以不指定数组的列数:(本例我们这里用的就是这种方法)
 

    int** p; 
    p = new int*[10];  
    //注意,int*[10]表示一个有10个元素的指针数组
    for (int i = 1; i <= 10; i++)p[i] = new int[5];

{

指针数组:

数组元素为指针的数组,其本质为数组。(例如 int *p[3],定义了p[0],p[1],p[2]三个指针)

}

这里,二级指针**p的指针名p为"int **" 类型(也就是说是一个指向int[10]这种一维数组型存储空间(like:int (*)[10]) 的地址(即指针))

具体赋给二级指针**p指针名p的(一个指向int[10]这种)一维数组(一个(10个元素的)指针数组)型存储空间的地址(即指针))。

我们可以很容易就注意到:

这里的“**p”和我们在C语言日记 25(2) 补充:二维数组与指针理解具体过程详解_宇 -Yu的博客-CSDN博客(里面的:A[3][3],int**p=A;执行p++时,编译器因无法知道长度(列宽:一行有多少列)而用不了)中提到的“(*p)[5]”他们的指针名代表的意义截然不同:

(*p)[5]中的数组名p,其实表示的是一个行地址

和这里完全不一样的地方:p也代表整个二维数组的首(个)元素(整个二维数组的第0行)的内存的首(元素的)地址;

当然和这里很像的是,它本身代表着一个一维数组,“[5]"表示一维数组包含5个元素

而这里,在开辟存储空间时,**p中的数组名p,表示的是一个一维数组型存储空间的地址


另外,这里这个程序却(是)不能改成

    int(*p)[8];

的形式:

#include <iostream>
using namespace std;
int main()
{
	int(*p)[8];
	int i, j;
	p = new int* [4];
	//开始分配4行8列的二维数据
	for (i = 0; i < 4; i++)p[i] = new int[8];
	for (i = 0; i < 4; i++)	//给二维数组放入数据
		for (j = 0; j < 8; j++)p[i][j] = j * i;
	//打印数据
	for (i = 0; i < 4; i++)
		for (j = 0; j < 8; j++)
		{
			if (j == 0) cout << endl;
			cout << p[i][j] << "\t";
		}
	for (i = 0; i < 4; i++)
		delete[] p[i];
	delete[] p;
	return 0;
}//1

结果:

 这又是为什么?

其实原因很简单:

因为我们这里用的p是一个指针数组,而不是一个数组指针;

{

数组指针:

指向数组地址的指针,本质上就是个指针;

指针数组:

数组元素为指针的数组,其本质为数组。(例如 int *p[3],定义了p[0],p[1],p[2]三个指针)

}


不知道他搞什么鸡毛,我感觉明明这俩程序一模一样,结果下面这个程序输入以后全是一堆报错:

#include <iostream>
using namespace std;
int main()
{
	int i,j;
	p = new int* [4];
	int** p;
	//开始分配4行8列的二维数据
	for (i = 0; i < 4; i++)p[i] = new int[8];
	for (i = 0; i < 4; i++)	//给二维数组放入数据
	{
		for (j = 0; j < 8; j++)p[i][j] = j * i;
	}
	//打印数据
	for (i = 0; i < 4; i++)
		for (j = 0; j < 8; j++)
		{
			if (j == 0) cout << endl;
			cout << p[i][j] << "\t";
		}
	//开始释放申请的堆
for (i = 0; i < 4; i++)
	delete[] p[i];
delete[] p;
return 0;
}

谁能搞清楚是怎么回事???

main()函数的括号部分打成了中文输入法..

麻了,我nm裂开


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

13_3、Java的IO流之节点流的使用

一、FileReader和FileWriter的使用1、数据读入操作说明&#xff1a;①read():返回读入的第一个字符&#xff0c;当读到文档末尾&#xff0c;返回-1②异常的处理&#xff1a;为了保证流资源一定会执行关闭操作&#xff0c;要对异常进行try-catch-finally处理③对于读入操作&…

【PWA学习】1. 初识 PWA

什么是PWA PWA(Progressive Web Apps&#xff0c;渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富&#xff0c;使其具有与原生应用相同的用户体验优势 我们需要理解的是&#xff0c;PWA 不是某一项技术&am…

MAC(m1)-VMWare Fusion CentOS8设置静态IP、SSH连接

在使用虚拟机的时候&#xff0c;默认情况下使用的DHCP协议&#xff08;根据网段自动分配ip&#xff09;分配的动态IP地址&#xff0c; 使得每次打开虚拟机后当前的IP地址都会发生变化&#xff0c;这样不方便管理。为了能够给当前虚拟机设置 一个静态IP地址&#xff0c;方便后…

Linux的开发工具——软件包管理器 yum

目录 1 查看 2 安装 3 卸载 4 常用软件 5 扩展细节 5.1 yum源 什么是软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成window…

【自学Python】Python标识符和保留字

Python标识符 Python标识符教程 Python 对各种 变量、方法、函数等命名时使用的字符序列称为标识符。 也可以说凡是自己可以起名字的地方都叫标识符&#xff0c;简单地理解&#xff0c;标识符就是一个名字&#xff0c;它的主要作用就是作为变量、函数、类、模块以及其他对象…

柱承重式钢模块建筑结构体系适用高度研究

作者&#xff1a;陈志华 冯云鹏 刘佳迪 刘洋 钟旭 模块建筑网 导语 摘要&#xff1a;模块建筑作为一种新兴的建筑体系&#xff0c;具有较高的预制化和装配化程度&#xff0c;符合建筑工业化以及绿色建筑的发展要求&#xff0c;但国内的模块建筑大多只应用于低多层&#xff0c;…

[付源码+数据集]Github星标上万,23 个机器学习项目汇总

在本文中分享了涵盖面向初学者&#xff0c;中级专家和专家的23种机器学习项目创意&#xff0c;以获取有关该增长技术的真实经验。这些机器学习项目构想将帮助你了解在职业生涯中取得成功、和当下就业所需的所有实践。 通过项目学习是你短期内能做的最好投资&#xff0c;这些项…

.NET 6结合SkiaSharp实现拼接验证码功能

从最初的滑动验证码&#xff0c;到实现旋转验证码&#xff01;不光实践了SkiaSharp的使用&#xff0c;也学到了很多东西。在网上看到一个拼接验证码功能&#xff0c;手痒了起来&#xff0c;结合前面实现的两种验证码&#xff0c;我们来学习一下如何实现拼接验证码功能&#xff…

流量路由技术解析

作者&#xff1a;十眠 流量路由&#xff0c;顾名思义就是将具有某些属性特征的流量&#xff0c;路由到指定的目标。流量路由是流量治理中重要的一环&#xff0c;本节内容将会介绍流量路由常见的场景、流量路由技术的原理以及实现。 流量路由的业务场景 我们可以基于流量路由…

aws sam 本地测试部署 lambda 和 apigateway

使用sam框架可以在部署serverless应用之前&#xff0c;在本地调试application是否符合预期 sam框架安装 serverless应用是lambda函数&#xff0c;事件源和其他资源的组合 使用sam能够基于docker容器在本地测试lambda函数 安装sam wget https://github.com/aws/aws-sam-cli…

ArcGIS基础实验操作100例--实验77按要素分区统计路网

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验77 按要素分区统计路网 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

ART-SLAM: Accurate Real-Time 6DoF LiDAR SLAM

IEEE Robotics and Automation Letters 意大利米兰理工学院 Abstract 地面车辆实时六自由度姿态估计&#xff0c;由于自动驾驶和三维建图等诸多应用&#xff0c;是机器人技术中一个相关和被研究广泛的课题。虽然有些系统已经存在&#xff0c;但它们要么不够准确&#xff0c;要…

Qt之标准对话框(QMessageBox、QFileDialog)

文章目录前言如何学习标准对话框QMessageBox消息对话框应用属性实操QFileDialog文件对话框应用属性实操前言 Qt为开发者提供了一些可复用的对话框&#xff0c;他对我们的开发是很重要的。下面我们就来学习 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考…

无监控,不运维!深入浅出介绍ChengYing监控设计和使用

监控系统俗称「第三只眼」&#xff0c;几乎是我们每天都会打交道的系统&#xff0c;它也一直是IT系统中的核心组成部分&#xff0c;负责问题的发现以及辅助性的定位。 ChengYing作为一站式全自动化全生命周期大数据平台运维管家&#xff0c;自然也提供大数据产品的监控服务。这…

力扣sql基础篇(二)

力扣sql基础篇(二) 1 每月交易I 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # sum函数如果需要筛选,可以考虑在里面嵌套if函数 SELECT DATE_FORMAT(trans_date,"%Y-%m") month,country,count(*) trans_count,COUNT(IF(state"appr…

matlab利用逻辑数组将保密率负数部分转换为零

通信中计算保密率的公式为 r[Rd−Re]r[R_d-R_e]^ r[Rd​−Re​] 其中RdR_dRd​代表合法目的地的数据速率&#xff0c;ReR_eRe​代表窃听节点的数据速率 当窃听节点的速率大于目的节点的速率时候&#xff0c;计算出来的保密率是负值&#xff0c;这在设计的时候可以将这时候的保…

referer、prototype、array、json笔记整理

目录referer、prototype、array、json笔记整理refererReferrer-policy如何设置referer绕过图片防盗链1、利用https网站盗链http资源网站&#xff0c;refer不会发送2、设置meta3、设置referrerpolicy"no-referrer"4、利用iframe伪造请求referer5、客户端在请求时修改h…

【LeetCode每日一题】——233.数字 1 的个数

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 数学 二【题目难度】 困难 三【题目编号】 233.数字 1 的个数 四【题目描述】 给定一个整数 …

为你的Typecho使用Redis缓存,优化访问速度-织音博客

前言Typecho虽然轻量&#xff0c;但终究仍是PHP动态脚本&#xff0c;访问时需要频繁调取数据库的信息&#xff0c;导致并发值一高&#xff0c;CPU就100%占用&#xff0c;无法处理新的请求信息。这时&#xff0c;我们可以用Redis来设置缓存&#xff0c;从而不用频繁调动数据库&a…

【Meetup预告】SeaTunnel + OpenMLDB:共筑数据集成生态,加速实时场景落地

2023年1月12日&#xff08;周四&#xff09;20&#xff1a;00-21:30&#xff0c;云原生数据集成平台 SeaTunnel 联合开源机器学习数据库 OpenMLDB 合作带来本期 Meetup。 活动背景 Al 应用的繁荣发展&#xff0c;既得益于数据的爆发式增长&#xff0c;也受困于数据治理的种种…