首页 > 编程语言 >C++ 使用 new 创建二维数组

C++ 使用 new 创建二维数组

时间:2023-01-11 23:55:15浏览次数:40  
标签:std int C++ 二维 数组 new 指针 delete

1. 直接创建

C++ 使用 new 创建二维数组最直接的方法就是 new T[M][N]。返回的指针类型是 T (*)[N],它是指向数组的指针,可以直接使用数组下标形式访问元素。释放内存直接使用delete[]。示例代码:

#include <iostream>

class A
{
public:
    A()
    {
        std::cout << "A::A" << std::endl;
    }
    ~A()
    {
        std::cout << "A::~A" << std::endl;
    }

    int x;
};

int main()
{
    A (*p)[3] = new A[2][3];
    delete[] p;
}

执行结果:

A::A
A::A
A::A
A::A
A::A
A::A
A::~A
A::~A
A::~A
A::~A
A::~A
A::~A

可以看到 A 的构造函数和析构函数正常执行。如果觉得 T (*)[N] 繁琐,可以直接使用 auto p = new T[M][N]。三维数组甚至更高维数组都可以使用这种方法。例如,三维数组使用 new T[M][N][O] 进行创建,依旧使用 delete[] p 进行释放。

为什么可以这样写?因为这种多维数组和普通的多维数组都是通过一维数组实现的。例如,int a[6][8],实际上编译器会转化为 int b[6 * 8] 一维数组。然后每次访问二维数组 a[i][j] 相当于访问 b[i * 8 + j]。从二维、三维数组的转化过程中可以发现一些规律。

T a[M][N] 	 --> T b[M * N],  	 a[i][j]    --> b[i * N + j]
T a[M][N][O] --> T b[M * N * O], b[i][j][k] --> b[i * N * O + j * O + k]

编译器进行下标转换时,并没有用到第 0 维的大小,而其它维的大小都是必须的。这也就是为什么下面代码能正确执行。

int a[2][3];
int (*p)[3] = a;

由于多维数组本质上是一维数组,所以释放内存都是 delete[] p,而没有奇怪的 delete[][] 语法。

2. 借助指针数组

还有一种方法就是先 new T*[M] 创建一个指针数组,其每个元素保存每一行的首个元素的地址,再使用 new T[N] 创建每一行。示例代码如下:

A** p = new A*[2];
for (int i = 0; i < 2; ++i) {
    p[i] = new A[3];
}

for (int i = 0; i < 2; ++i) {
    delete[] p[i];
}
delete[] p;

这种方法非常繁琐,首先 new T*[M] 不能写成 new (T(*)[M]),因为它是指针数组而不是数组指针。其次,需要对每一行调用 new T[N]。释放内存时,要先使用 delete[] 释放每一行,再调用 delete[] 释放数组指针。这几个步骤一步都不能错,不然就出现野指针或者内存泄漏。这段代码我也是用 Address Sanitizer 和 Leak Sanitizer 检查一遍才写对。

这种方法唯一的好处就是可以创建交错数组(Jagged Array),也就是每一行的大小不一样。例如:

A **p = new A *[2];
p[0] = new A[3];
p[1] = new A[4];

for (int i = 0; i < 2; ++i)
{
    delete[] p[i];
}
delete[] p;

3. 借助 std::vector

可以用 std::vector 对上面这种方法进行包装,使其更加易用。示例代码如下:

std::vector<std::vector<int>> v{ std::vector<int>(3), std::vector<int>(4) };
std::cout << v[0].size() << " " << v[1].size() << std::endl;

这段代码创建了一个二维数组,第 0 行有 3 个元素,第 1 行有 4 个元素。这种方法既能创建交错数组,也不需要手动释放内存。

标签:std,int,C++,二维,数组,new,指针,delete
From: https://www.cnblogs.com/mkckr0/p/17045233.html

相关文章

  • 【教你学Qt桌面端开发】pt1:浅谈Qt:特色C++主义类库
    还在为头脑简单看不懂代码而发愁吗?还在为思想浅薄只会人云亦云、拾人牙慧、鹦鹉学舌而遭人鄙夷吗?《教你写代码》,从另一维度解读代码,让你成为见解独特的黑马观众。教你学Q......
  • C++ 扩展的显示类型转换
      ......
  • C++实现顺序队列(循环队列)相关操作代码
    #include<iostream>#include<cstdlib>usingnamespacestd;#defineMAXSIZE100#defineOK1#defineERROR0#defineOVERFLOW-1typedefintStatus;typedefintElemtype......
  • Visual Studio Code – C++ 入门
    ——基于VisualStudioCode官方文档的全面的、具体的入门级教程作者:XiXu在本教程中,您将为使用UCRT64中的GCCC++编译器(g++)和GDB调试器配置VisualStudio......
  • c++静态库和动态库的添加
    #声明要求的cmake最低版本cmake_minimum_required(VERSION2.8)#声明一个cmake工程project(helloSLAM)#设置编译模式set(CMAKE_BUILD_TYPE"Debug")#共享库add_l......
  • C++用递归实现求解相关函数
    //递归实现Hanoi塔问题#include<iostream>#include<cstdlib>usingnamespacestd;#defineMAXSIZE100#defineOK1#defineERROR0typedefintStatus;typedefintElemty......
  • C++实现链栈相关操作代码
    #include<iostream>#include<cstdlib>usingnamespacestd;#defineMAXSIZE100#defineOK1#defineERROR0typedefintStatus;typedefintElemtype;typedefstructSta......
  • C++构造函数【cherno课程学习】
    C++构造函数无参构造函数首先创造一个Entity类,在类里面有两个变量x,y以及一个方法#include<iostream>classEntity{public:floatX,Y;voidPrint(){......
  • c++ 利用 chrono 打印时间
    c++利用chrono打印时间自己封装的一个打印时间的类,可以打印秒、毫秒、微秒、纳秒。头文件#ifndefTIMER_H#defineTIMER_H#include<chrono>enumclassType{......
  • 手撕AVL树(C++)
    阅读本文前,请确保您已经了解了二叉搜索树的相关内容(如定义、增删查改的方法以及效率等)。否则,建议您先学习二叉搜索树。本文假定您对二叉搜索树有了足够的了解。效率?众所......