【问题标题】:Deleting templated C++ 2-dimensional array删除模板化 C++ 二维数组
【发布时间】:2012-11-19 23:38:26
【问题描述】:

对 C++ 要求在二维数组中传递维度的烦恼让我开始研究模板化的 Matrix 类。我已经用 C# 编码了一段时间,所以我确定我在这里有点生疏了。

问题是,当我遇到试图删除二维数组的析构函数时,我得到了一个堆异常。

感激地接受任何帮助!

template <typename T>
class Matrix {
public:
    Matrix(int m, int n) : nRows(m), nCols(n) { 
        pMatrix = new T * [nRows]; 
        for (int i = 0; i < nCols; i++) {
            pMatrix[i] = new T[nCols];
        }
    }
    ~Matrix() { 
        if (pMatrix != NULL) { 
            for (int i = 0; i < nRows; i++) { delete[] pMatrix[i]; }
            delete[] pMatrix;
        }
    }
    T ** GetMatrix() const { return pMatrix; }
    T * Row(int i) const { return pMatrix[i]; }
    inline T Cell(int row, int col) const { return pMatrix[row][col]; }
    inline int GetNRows() const { return nRows; }
    inline int GetNCols() const { return nCols; }
private:
    int nRows, nCols;
    T ** pMatrix;
};

【问题讨论】:

  • 为什么需要返回T**T*?对我来说,这似乎暴露了内部实现细节。您可以通过使用单个 std::vector 并使用索引进行访问来简化代码。
  • 完全同意胡安。通过使用std::vector&lt;&gt; 和简单的i*cols+j 来获取[i][j] 元素,您可以显着地 简化此操作。您唯一会缺少的是执行双括号取消引用的能力,甚至可以使用返回行迭代器的创造性子类来征服。
  • 同意,并查看与这些类似的更优雅解决方案的答案。

标签: c++ templates destructor


【解决方案1】:

这是错误:

for (int i = 0; i < nCols; i++) {
        pMatrix[i] = new T[nCols];
}

循环应该是直到nRows,而不是nCols

除此之外,让我告诉你我厌倦了分配二维数组时所做的一些事情。我必须做一个 3-d 数组。我使用了map,它从一个坐标映射 - 一个将 x、y、z 保存到我想要的类型的结构。

我工作得很快,无需分配或解除分配。分配给一个坐标很简单

mymap[Coord(x, y, z)] = whatever...

当然,我需要定义 Coord 结构并重载 &lt; operator,但我发现这比尝试分配和解除分配 3-d 数组更方便。

当然,您需要检查此方案是否足够快。我用它使用OpenGL在一个大立方体内绘制单元格,完全没有抱怨。

【讨论】:

    【解决方案2】:

    关于这个错误,@CodeChords_man 解释得很对。我有关于实施的说明。我建议浏览this wonderful FAQ post

    除非您 100% 确定,否则不应使用动态内存分配

    1. 真的需要它
    2. 你知道如何实现它

    我不知道第一个,以及性能对您来说有多重要。但至于第二个,你至少违反了rule of three。你的课使用起来很不安全。如果你复制它,内存缓冲区将被双重删除。

    您不应该害怕使用 STL 容器,它们快速且经过优化。至少std::vector,在很多场景下它和原始指针一样快。您可以使用std::vector 重写您的课程,如下所示:

    template <typename T>
    class Matrix {
    public:
      typedef std::vector<T> MatrixRow;
      typedef std::vector<MatrixRow> MatrixBody;
    
      Matrix(int m, int n) : nRows(m), nCols(n), _body(m, MatrixRow(n)) {}
    
      const MatrixBody& GetMatrix() const { return _body; }
      const MatrixRow& GetRow(int i) const { return _body[i]; }
    
      inline T Cell(int row, int col) const { return _body[row][col]; }
      inline int GetNRows() const { return nRows; }
      inline int GetNCols() const { return nCols; }
    private:
      int nRows, nCols;
      MatrixBody _body;
    };
    

    由于这个类没有使用动态内存分配,所以复制和分配是安全的。在这种情况下,您也不需要显式存储nRowsnCols;您可以改用_body.size()_body[0].size()

    关于向量的底层向量,它使用相同的[i][j] 构造来取消引用。它很容易用begin()end() 进行迭代。如果您绝对需要在某些例程中使用原始指针,您可以随时使用&amp;row[0] 访问它。

    唯一可能的困难是您不能轻易地将MatrixBody 转换为T**。但请三思,也许您根本不需要使用T**

    【讨论】:

    • 完全不能同意你的看法!我已经修复了这个错误;我现在会使用 STL。我最初来自无法使用 STL 的深度嵌入式环境(大小)。这是一个很好的提示,我会尝试并告诉你它是如何工作的。
    • 为你高兴!然后考虑支持甚至接受我的回答:)
    • 我现在越来越贪心了——我要弄清楚如何重载 [],这样我就不必使用笨拙的“GetMatrix”语法了 :)
    • 您可以将MatrixBody 用作矩阵本身。在这种情况下,您可以不实现任何额外的类。但是,在这种情况下,您将无法使用 (m, n) 构建它。无论如何,在这种情况下,实现operator[]() 是简单、直接和合适的,所以去吧。请记住,您只需要用它替换GetRow。得到vector后,第二个[]会自动生效。
    • 我只是想知道为什么要打扰 Matrix 类的问题。漂亮的建筑也是我要做的。感谢关于 operator[] 的提示 - 我的搜索刚刚发现需要代理来操作第二个 [],但你是对的,向量满足了这一需求!如果可以的话,我会为你投三票。
    猜你喜欢
    • 2011-08-30
    • 2021-10-24
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多