结论:用 std::vector 是最安全、最省心的选择;若追求性能或需与 c 接口兼容,才考虑手动管理内存的两种方式——但必须严格配对释放,且注意内存连续性差异。

直接说结论:用 std::vector<:vector>></:vector> 是最安全、最省心的选择;若追求性能或需与 C 接口兼容,才考虑手动管理内存的两种方式——但必须严格配对释放,且注意内存连续性差异。
为什么不能写 new int[m][n]
这是初学者最常踩的坑。C++ 标准不支持运行时确定两个维度的 new T[m][n] 语法(除非 n 是编译期常量)。你写:
int m = 3, n = 4; int** arr = new int[m][n]; // ❌ 编译失败:expected constant expression
原因在于:该语法只接受第一维为变量、第二维为字面常量(如 new int[5][4]),本质是分配一块连续内存并做类型转换,和真正的“动态二维”无关。误用会导致编译错误或静默降级为一维指针,后续访问越界风险极高。
int** 分配 + 逐行 new(锯齿数组)
这是最直观、最易理解的手动方式,支持每行长度不同(即“锯齿数组”),但内存不连续:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 先用
new int*[rows]分配行指针数组 - 再循环
arr[i] = new int[cols]为每行单独分配 - 释放时必须双重循环:
delete[] arr[i]→delete[] arr - 访问快(
arr[i][j]直观),但缓存不友好:每次跳转可能跨页,矩阵计算性能差 - 若某次
new失败,必须手动回滚已分配的行,否则内存泄漏
一维连续内存 + 指针数组索引(伪二维)
兼顾 arr[i][j] 语法和内存连续性,适合数值计算等对 cache 敏感的场景:
- 分配一块大内存:
int* data = new int[rows * cols] - 再建指针数组指向各行起始位置:
int** arr = new int*[rows],然后arr[i] = data + i * cols - 释放只需两步:
delete[] data和delete[] arr - 注意:不能直接传
arr给期望int(*)[N]的函数——类型不兼容,需额外转换 - 对象类型(如
std::string)需手动调用构造/析构,不能只靠new char[...]+ placement new,否则资源泄漏
真正该优先用的:std::vector<:vector>></:vector>
现代 C++ 中,95% 的场景应直接用这个:
- 写法简洁:
std::vector<:vector>> arr(rows, std::vector<int>(cols))</int></:vector> - 自动管理内存:作用域结束即释放,无泄漏风险
- 支持动态增行:
arr.emplace_back(cols),也支持每行不同长 - 缺点只有两个:内存不连续(每行独立分配)、少量额外开销(指针间接跳转)
- 若真需要连续内存,可用单层
std::vector<t></t>+ 手动下标计算:data[i * cols + j],再封装访问接口
容易被忽略的关键点:连续内存 ≠ 自动支持 arr[i][j] 语法;而 std::vector 虽不连续,却天然支持该语法且零成本抽象。别为了“看起来连续”硬上裸指针——调试成本和维护代价远高于那点缓存收益。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










