如何在c++中创建一个连续的2d数组
how to create a contiguous 2d array in c++?
我想创建一个函数,用C++返回一个连续的2D数组。
使用以下命令创建阵列不是问题:
int (*v)[cols] = new (int[rows][cols]);
但是,我不知道如何将此数组作为函数的通用类型返回。功能是:
NOT_SURE_WHAT_TYPE create_array(int rows, int cols)
{
int (*v)[cols] = new (int[rows][cols]);
return v;
}
我试了两次,但都不起作用。我不想使用double*,因为我想从外部访问这个数组作为2D数组。
相关问题:如何使用new在C++中声明2d数组?
如果您想要创建一个数据连续的数组,而不想要一维数组(即,您想要使用[][]
语法),那么以下操作应该有效。它创建一个指针数组,每个指针指向内存池中的一个位置。
#include <iostream>
#include <exception>
template <typename T>
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
if (nrows == 0)
throw std::invalid_argument("number of rows is 0");
if (ncols == 0)
throw std::invalid_argument("number of columns is 0");
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{val}; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols )
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete [] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
template <typename T>
void delete2DArray(T** arr)
{
delete [] arr[0]; // remove the pool
delete [] arr; // remove the pointers
}
int main()
{
try
{
double **dPtr = create2DArray<double>(10,10);
dPtr[0][0] = 10; // for example
delete2DArray(dPtr); // free the memory
}
catch(std::bad_alloc& ex)
{
std::cout << "Could not allocate array";
}
}
请注意,只完成了2次分配。这不仅由于完成的分配量较小而更有效,而且如果内存分配失败,我们现在有更好的机会对分配的内存进行回滚,这与"回滚"不同;传统的";在非连续存储器中分配2D阵列的方法:
// The "traditional" non-contiguous allocation of a 2D array (assume N x M)
T** ptr;
ptr = new T*[N];
for (int i = 0; i < N; ++i)
ptr[i] = new T [M]; // <<-- What happens if new[] throws at some iteration?
如果new[]
在for
循环的操作过程中在某个地方抛出异常,则必须回滚之前对new[]
的所有成功调用——这需要更多的代码并增加复杂性。
请注意如何在连续版本中释放内存——当连续分配时,只调用两次delete[]
,而不是为每行调用delete[]
的循环。
此外,由于数据在连续存储器中,假设数据在连续内存中的算法、函数等,就像一维数组一样,现在可以通过指定M*N矩阵的起始和结束范围来使用:
[&array[0][0], &array[M-1][N])
例如:
std::sort(&myArray[0][0], &myArray[M-1][N]);
将按升序对整个矩阵进行排序,从索引[0][0]
开始直到最后一个索引[M-1][N-1]
。
您可以通过使其成为一个真正的类来改进设计,而不是将分配/解除分配作为两个单独的函数。
Edit:正如评论所说,这个类不像RAII。我把这个留给读者练习。上面的代码缺少的一件事是检查nRow和nColls是否>0。
编辑2:添加了一个try-catch
,以确保在尝试分配内存时抛出std::bad_alloc
异常时,可以正确回滚内存分配。
编辑:关于类似于上面代码的三维数组示例,请参阅此答案。其中包括在分配失败时回滚分配的代码。
编辑:添加初步RAII类:
template <typename T>
class Array2D
{
T** data_ptr;
unsigned m_rows;
unsigned m_cols;
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{ val }; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols)
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete[] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
public:
typedef T value_type;
T** data() {
return data_ptr;
}
unsigned get_rows() const {
return m_rows;
}
unsigned get_cols() const {
return m_cols;
}
Array2D() : data_ptr(nullptr), m_rows(0), m_cols(0) {}
Array2D(unsigned rows, unsigned cols, const T& val = T())
{
if (rows == 0)
throw std::invalid_argument("number of rows is 0");
if (cols == 0)
throw std::invalid_argument("number of columns is 0");
data_ptr = create2DArray(rows, cols, val);
m_rows = rows;
m_cols = cols;
}
~Array2D()
{
if (data_ptr)
{
delete[] data_ptr[0]; // remove the pool
delete[] data_ptr; // remove the pointers
}
}
Array2D(const Array2D& rhs) : m_rows(rhs.m_rows), m_cols(rhs.m_cols)
{
data_ptr = create2DArray(m_rows, m_cols);
std::copy(&rhs.data_ptr[0][0], &rhs.data_ptr[m_rows-1][m_cols], &data_ptr[0][0]);
}
Array2D(Array2D&& rhs) noexcept
{
data_ptr = rhs.data_ptr;
m_rows = rhs.m_rows;
m_cols = rhs.m_cols;
rhs.data_ptr = nullptr;
}
Array2D& operator=(Array2D&& rhs) noexcept
{
if (&rhs != this)
{
swap(rhs, *this);
rhs.data_ptr = nullptr;
}
return *this;
}
void swap(Array2D& left, Array2D& right)
{
std::swap(left.data_ptr, right.data_ptr);
std::swap(left.m_cols, right.m_cols);
std::swap(left.m_rows, right.m_rows);
}
Array2D& operator = (const Array2D& rhs)
{
if (&rhs != this)
{
Array2D temp(rhs);
swap(*this, temp);
}
return *this;
}
T* operator[](unsigned row)
{
return data_ptr[row];
}
const T* operator[](unsigned row) const
{
return data_ptr[row];
}
void create(unsigned rows, unsigned cols, const T& val = T())
{
*this = Array2D(rows, cols, val);
}
};
int main()
{
try
{
Array2D<double> dPtr(10, 10);
std::cout << dPtr[0][0] << " " << dPtr[1][1] << "n";
}
catch (std::exception& ex)
{
std::cout << ex.what();
}
}
除非在编译时知道两个维度的大小,否则您没有太多选择:分配一个int
s的单个rows*cols
数组,并使用整数乘法和加法滚动您自己的2D索引。将其封装在类中可以生成一个好看的语法,用于使用方括号运算符访问数组元素。由于您的数组是2D的,因此需要使用代理(也称为"代理")对象进行第一级数据访问。
下面是一个小样本代码,它使用std::vector<T>
来维护动态内存中的连续内存区域:
template<class T>
class Array2D {
vector<T> data;
size_t cols;
public:
// This is the surrogate object for the second-level indexing
template <class U>
class Array2DIndexer {
size_t offset;
vector<U> &data;
public:
Array2DIndexer(size_t o, vector<U> &dt) : offset(o), data(dt) {}
// Second-level indexing is done in this function
T& operator[](size_t index) {
return data[offset+index];
}
};
Array2D(size_t r, size_t c) : data (r*c), cols(c) {}
// First-level indexing is done in this function.
Array2DIndexer<T> operator[](size_t index) {
return Array2DIndexer<T>(index*cols, data);
}
};
现在,您可以像使用内置的C++数组一样使用Array2D<int>
:
Array2D<int> a2d(10, 20);
for (int r = 0 ; r != 10 ; r++) {
for (int c = 0 ; c != 20 ; c++) {
a2d[r][c] = r+2*c+1;
}
}
在ideone上运行演示。
由于您使用的是C++而不是C,因此我建议您使用一个向量,而不是使用new/delete。
您可以这样定义一个连续的内存块:
std::vector<int> my_matrix(rows*cols);
现在你可以用类似2d数组的方式访问这个向量,公式为i*n+j,其中i是行索引,j是列索引,n是行的长度:
my_matrix[i*n + j];
这与使用array[i][j]访问2d数组相同。但是现在你有了一个连续内存块的优势,你不需要为新建/删除而烦恼,你可以很容易地用函数共享和返回这个向量对象。
处理原始内存资源通常很麻烦。最佳方案是一个简单的包装,如:
struct array2D : private std::vector<int>
{
typedef std::vector<int> base_type;
array2D() : base_type(), height_(0), width_(0) {}
array2D(std::size_t h, std::size_t w) : base_type(h*w), height_(h), width_(w);
int operator()(std::size_t i, std::size_t j) const
{
return base_type::operator[](i+j*height_);
}
int& operator()(std::size_t i, std::size_t j)
{
return base_type::operator[](i+j*height_);
}
std::size_t rows() const { return height_; }
std::size_t cols() const { return width_; }
private:
std::size_t height_, width_;
}
私有继承可以让您从vector中获取所有好处,只需添加2D构造函数即可。资源管理是免费的,因为vector ctor/dtor将发挥其魔力。显然,i+h*j可以更改为您想要的任何存储顺序。
向量<向量<int>>是2D,但在内存中不会是连续的。
然后你的功能变成:
array2D create_array(int rows, int cols)
{
return array2D(cols,rows);
}
编辑:
您还可以使用usingn子句检索其他向量接口部分,如begin/end或size,以使私有继承成员函数再次公开。
在我看来,在标准C++中定义2D动态数组的方法都不完全令人满意。
你最终不得不推出自己的解决方案。幸运的是,Boost中已经有了一个解决方案。增强::多阵列:
#include "boost/multi_array.hpp"
template<typename T>
boost::multi_array<T, 2> create_array(int rows, int cols) {
auto dims = boost::extents[rows][cols];
return boost::multi_array<T, 2>(dims);
}
int main() {
auto array = create_array<int>(4, 3);
array[3][2] = 0;
}
现场演示。
PaulMcKenzie提供的"Rudimentary RAll"类是一个出色的解决方案。在我使用它的过程中,我确实发现了一个内存泄漏,在下面的版本中得到了修复。
内存泄漏是由于Array2D& operator=(Array2D&& rhs) noexcept
。
为了允许rhs析构函数删除从lhs交换的原始数据(池和指针),需要删除语句rhs.m_dataPtr = nullPtr
。
以下是PaulMcKenzie 提供的"Rudimentary RAll"类的更正代码
template <typename T>
class Array2D
{
T** data_ptr;
unsigned m_rows;
unsigned m_cols;
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{ val }; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols)
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete[] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
public:
typedef T value_type;
T** data() {
return data_ptr;
}
unsigned get_rows() const {
return m_rows;
}
unsigned get_cols() const {
return m_cols;
}
Array2D() : data_ptr(nullptr), m_rows(0), m_cols(0) {}
Array2D(unsigned rows, unsigned cols, const T& val = T())
{
if (rows == 0)
throw std::invalid_argument("number of rows is 0");
if (cols == 0)
throw std::invalid_argument("number of columns is 0");
data_ptr = create2DArray(rows, cols, val);
m_rows = rows;
m_cols = cols;
}
~Array2D()
{
if (data_ptr)
{
delete[] data_ptr[0]; // remove the pool
delete[] data_ptr; // remove the pointers
}
}
Array2D(const Array2D& rhs) : m_rows(rhs.m_rows), m_cols(rhs.m_cols)
{
data_ptr = create2DArray(m_rows, m_cols);
std::copy(&rhs.data_ptr[0][0], &rhs.data_ptr[m_rows-1][m_cols], &data_ptr[0][0]);
}
Array2D(Array2D&& rhs) noexcept
{
data_ptr = rhs.data_ptr;
m_rows = rhs.m_rows;
m_cols = rhs.m_cols;
rhs.data_ptr = nullptr;
}
Array2D& operator=(Array2D&& rhs) noexcept
{
if (&rhs != this)
{
swap(rhs, *this);
}
return *this;
}
void swap(Array2D& left, Array2D& right)
{
std::swap(left.data_ptr, right.data_ptr);
std::swap(left.m_cols, right.m_cols);
std::swap(left.m_rows, right.m_rows);
}
Array2D& operator = (const Array2D& rhs)
{
if (&rhs != this)
{
Array2D temp(rhs);
swap(*this, temp);
}
return *this;
}
T* operator[](unsigned row)
{
return data_ptr[row];
}
const T* operator[](unsigned row) const
{
return data_ptr[row];
}
void create(unsigned rows, unsigned cols, const T& val = T())
{
*this = Array2D(rows, cols, val);
}
};
int main()
{
try
{
Array2D<double> dPtr(10, 10);
std::cout << dPtr[0][0] << " " << a2[0][0] << "n";
}
catch (std::exception& ex)
{
std::cout << ex.what();
}
}
我认为应该编写一个简单的类来包装一个1-dim数组。然后,您可以使用operator()重载来实现一个2维数组,以获取值,并解构func来释放内存。代码如下:
#include <assert.h>
template <typename T>
class Array_2D
{
private:
T *data_inside;
public:
int size[2];
Array_2D(int row, int column);
~Array_2D();
//
T operator()(int index1, int index2){
return data_inside[get_index(index1, index2)];
}
int get_index(int index1, int index2){
if(index1>=0 and index1<size[0] and index2>=0 and index2<=size[1]){
return index1*size[0] + index2;
}else{
assert("wrong index for array!" == "True");
}
}
};
template <typename T>
Array_2D<T>::Array_2D(int row, int column)
{
size[0] = row;
size[1] = column;
data_inside = new T[row*column];
}
template <typename T>
Array_2D<T>::~Array_2D()
{
// 使用析构函数,自动释放资源
delete[] data_inside;
}
- 我已经建立了递归关系,它找到了两个字符串之间最长的连续公共字符串,我怎么能跳过其中一个字符串中的一个字符
- 使用连续分隔符和空最后一个字符进行拆分
- 是否存在一个范围::视图::group_by对应项,它将所有元素都考虑在内,而不是只考虑连续的元素
- 在 Cython 中创建一个连续数组并将其传递给C++
- C++ 如何在 std::vector 中插入一个连续的间隔范围?
- 给定n个数字,编写一个例程,以在4个连续数字之间找到最大的数字
- 您可以为连续术语编写一个概念吗?
- 从一个字符串中获取 2-5 个连续的单词短语,我得到了 2 个工作,但在做 3 个时遇到麻烦
- 如何在c++中创建一个连续的2d数组
- 查找最大和连续子数组-另一个版本
- 为什么两个连续的转换会改变一个整数
- 将连续字节作为一个整数读取
- 用C++编写一个程序,模拟反复抛硬币,并持续到连续抛出三个头
- 在一个排序的列表中正好找到N个连续项
- 将多维C++数组作为一个连续块(堆上)访问是否有效
- 如何在 C 系统 / popen 命令中通过另一个C++程序连续输入
- 在 OpenGL 中创建一个围绕其 y 轴连续旋转的三角形
- 如何为快速IPC分配一个连续的物理RAM
- 我可以把一个二维数组当作一个连续的一维数组吗?
- 二维数组输出一个连续的行