问题描述
我想在 C++ 中创建一个返回连续二维数组的函数.
I want to create a function that returns a contiguous 2D array in C++.
使用命令创建数组没有问题:
It is not a problem to create the array using the command:
int (*v)[cols] = new (int[rows][cols]);
但是,我不确定如何将此数组作为函数的通用类型返回.功能是:
However, I am not sure how to return this array as a general type for a function. The function is:
NOT_SURE_WHAT_TYPE create_array(int rows, int cols)
{
int (*v)[cols] = new (int[rows][cols]);
return v;
}
我尝试了 double*[] 和 double**,但都不起作用.我不想使用 double*,因为我想从外部以二维数组的形式访问这个数组.
I tried double*[] and double** and both don't work. I wouldn't want to use double*, since I want to access this array from outside as a 2D array.
推荐答案
如果你想创建一个数组,其中数据是连续的,并且你不想要一维数组(即你想使用 [][]
语法),那么以下应该可以工作.它创建了一个指针数组,每个指针指向一个内存池中的位置.
If you want to create an array where the data is contiguous and you don't want a 1-dimensional array (i.e. you want to use the [][]
syntax), then the following should work. It creates an array of pointers, and each pointer points to a position into a pool of memory.
#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 次分配.由于完成的分配量较少,这不仅效率更高,而且如果内存分配失败,我们现在有更好的机会回滚已分配的内存,这与传统"方法不同.在非连续内存中分配二维数组的方法:
Note that only 2 allocations are done. Not only is this more efficient due to the lesser amounts of allocations done, we now have a better chance of doing a rollback of the allocated memory if a memory allocation fails, unlike the "traditional" way of allocating a 2D array in non-contiguous memory:
// 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[]的所有成功调用
之前发生的事情——需要更多的代码并增加了复杂性.
If new[]
throws an exception somewhere during the operation of the for
loop, you have to roll back all of the successful calls to new[]
that happened previously -- that requires more code and adds complexity.
注意如何在连续版本中释放内存——连续分配时只需两次调用 delete[]
而不是为每一行调用 delete[]
的循环.
Note how you deallocate the memory in the contiguous version -- just two calls to delete[]
when allocated contiguously instead of a loop calling delete[]
for each row.
另外,由于数据在连续内存中,算法、函数等假设数据在连续内存中,就像一维数组一样,现在可以通过指定开始和结束范围来使用M*N矩阵:
Also, since the data is in contiguous memory, algorithms, functions, etc. that assume that the data is in contiguous memory, just like a one-dimensional array, can now be used by specifying the start and end range for the M*N matrix:
[&array[0][0], &array[M-1][N])
例如:
std::sort(&myArray[0][0], &myArray[M-1][N]);
将按升序对整个矩阵进行排序,从索引 [0][0]
开始,直到最后一个索引 [M-1][N-1]
.
will sort the entire matrix in ascending order, starting from index [0][0]
up until the last index [M-1][N-1]
.
您可以通过使其成为真正的类而不是将分配/释放作为 2 个单独的函数来改进设计.
You can improve on the design by making this a true class instead of having allocation / deallocation as 2 separate functions.
正如评论所说,该类不是类似 RAII 的.我把它留给读者作为练习.上面代码中缺少的一件事是检查 nRows 和 nCols >创建此类数组时为 0.
The class is not RAII-like, just as the comment says. I leave that as an exercise for the reader. One thing missing from the code above is the check that nRows and nCols are > 0 when creating such an array.
编辑 2:添加了 try-catch
以确保在尝试分配内存时抛出 std::bad_alloc
异常时正确回滚内存分配.
Edit 2: Added a try-catch
to ensure a proper roll back of the memory allocation is done if a std::bad_alloc
exception is thrown attempting to allocate memory.
对于类似于上面的代码的 3 维数组示例 看这个答案.包括在分配失败时回滚分配的代码.
For a 3 dimensional array example of code similar to the above see this answer. Included is code to roll back allocations if the allocation fails.
添加了基本的 RAII 类:
Rudimentary RAII class added:
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] << "
";
}
catch (std::exception& ex)
{
std::cout << ex.what();
}
}
这篇关于如何在 C++ 中创建一个连续的二维数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!