本文介绍了如何在 C++ 中创建一个连续的二维数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 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.

相关问题:如何申报在 C++ 中使用 new 的二维数组?

推荐答案

如果你想创建一个数组,其中数据是连续的,并且你不想要一维数组(即你想使用 [][] 语法),那么以下应该可以工作.它创建了一个指针数组,每个指针指向一个内存池中的位置.

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++ 中创建一个连续的二维数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 01:25