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

目录

part 1:

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

1: new<数据类型>

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

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

附:part1

附:part2

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

part 2:

part 3:


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博客

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

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


原文:

Points:

关于

L= new Lnode;

实际上我觉得真的是多此一举:

系统开始带有参数LinkList &L的时候不是实际上已经提前给你默认提前分配好了L的内存空间吗,你他*还在这边费劲干啥呢

另外,   L= new Lnode;这种开辟空间的格式是哪弄出来的???

原来学过关于new的用法,只有通过定义指针分配新空间,like:

   Lnode* p = new Lnode;

但是既然这里可行可用,那我们也需要记住:

L= new Lnode;

即:

<开辟空间首地址> = new  <开辟空间存放数据类型>

格式(前面没有定义过记得加auto)

最后经过我们后面深入专门去学习了关于new的使用方法和格式,发现并不是这么一回事:

并不是因为代表L是表,而像我们猜测的那样有了什么新的格式

而是由于表头前面的Linklist &L,表明了他本来就是一个指针,依然属于符合原来的使用规范

也就是说,如果前面(形参传递时)我们已经定义过了该变量,那么在后面开辟空间时

我们就不用(也不能)再(重新)申明变量的类型


附: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:

数据结构与算法基础(王卓)(8):线性表的应用

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


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()函数的括号部分打成了中文输入法..

麻了

猜你喜欢

转载自blog.csdn.net/Zz_zzzzzzz__/article/details/128589494