如何在c ++中创建连续的2d数组?

时间:2014-02-21 19:35:50

标签: 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数组。

相关问题:How do I declare a 2d array in C++ using new?

7 个答案:

答案 0 :(得分:22)

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

#include <iostream>

template <typename T>
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
   }
}

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次分配。另请注意如何释放内存。您可以通过将此设置为真正的类而不是将分配/释放作为2个单独的函数来改进设计。

编辑:该课程与RAII不同,正如评论所说。我把它作为读者的练习。上面代码中缺少的一件事是检查nRows和nCols是否>创建这样的数组时为0。

编辑2:添加了try-catch以确保在尝试分配内存时抛出std::bad_alloc异常,从而确保正确回滚内存分配。


编辑:对于与上述see this answer类似的代码的三维数组示例。如果分配失败,则包括用于回滚分配的代码。

答案 1 :(得分:6)

处理原始内存资源通常很糟糕。最好的镜头是一个简单的包装:

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构造函数。资源管理是免费的,因为矢量ctor / dtor将发挥他们的魔力。显然,i + h * j可以改为你想要的存储顺序。

矢量&lt;矢量&lt; int&gt; &GT;是2D但在内存中不会连续。

您的功能随后变为:

array2D create_array(int rows, int cols)
{
  return array2D(cols,rows);
}

编辑:

您还可以使用usign子句检索其他向量界面部分,如begin / end或size,以使私有继承的成员函数再次公开。

答案 2 :(得分:6)

除非在编译时知道两个维度的大小,否则您没有太多选择:分配rows*colsint个数组,并使用整数乘法滚动自己的2D索引和补充。在类中包装它可以产生一个漂亮的语法,用于访问带方括号运算符的数组元素。由于您的数组是2D,因此您需要使用代理(AKA“代理”)对象进行第一级数据访问。

这是一个使用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);
    }
};

您现在可以使用Array2D<int>,就像它是内置的C ++数组一样:

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;
    }
}

Running demo on ideone

答案 3 :(得分:5)

由于您使用的是C ++而不是C,我建议使用一个向量而不是使用new / delete。

您可以像这样定义一个连续的内存块:

std::vector<int> my_matrix(rows*cols);

现在您以类似于二维数组的方式使用公式i * n + j访问此向量,其中i是行索引,j是列索引,n是行的长度:

my_matrix[i*n + j];

这与使用array [i] [j]访问2d数组相同。但是现在你有一个连续的内存块的优势,你不需要打扰新的/删除,你可以轻松地与函数共享和返回这个矢量对象。

答案 4 :(得分:4)

在我看来,在标准C ++中定义2D动态数组的方法都不是完全令人满意的。

您最终必须推出自己的解决方案。幸运的是,Boost已经有了一个解决方案。 boost::multi_array

#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;
}

Live demo

答案 5 :(得分:1)

PaulMcKenzie提供的“ Rudimentary RAll”类是一个很好的解决方案。在使用它的过程中,确实发现了内存泄漏,该漏洞已在下面显示的版本中修复。

内存泄漏是由于存在问题 Array2D& operator=(Array2D&& rhs) noexcept

需要删除语句rhs.m_dataPtr = nullPtr,以允许rhs析构函数删除从lhs交换的原始数据(池和指针)。

这是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();
    }
}

答案 6 :(得分:0)

我认为您应该编写一个简单的类来包装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;
}