【问题标题】:C++ Matrix Class - SuggestionsC++ 矩阵类 - 建议
【发布时间】:2011-04-12 10:13:22
【问题描述】:

我正在尝试在 C++ 中构建一个模板化的 Matrix 类。下面是它的实现。 到目前为止,我实现了两个运算符 +,+= 只是为了了解它的外观,我认为最好在继续之前征求反馈意见。

整个实现是公开的,也没有明确的边界/错误检查,这是因为它并不是一个完整的矩阵库,因此避免了不必要的代码。

如果有人可以对此发表评论并提出一些改进或建议,那将非常有帮助。

谢谢。

template<class T>
class Matrix
{
    public:
    int r,c;
    vector< vector< T > >mat;

    Matrix() {}

    // Constructor to set the size of the matrix
    Matrix(int _r,int _c)
    {
        r=_r;c=_c;
        mat.resize(r);
        for(int i=0;i<r;i++)
            mat[i].resize(c);
    }
    // Constructor to build a matrix from a C 2d array
    // Pointer to the first element is passed (&arr[0][0])
    Matrix(T *arr,int _r,int _c)
    {
        r=_r;c=_c;
        mat.resize(r);
        for(int i=0;i<r;i++)
            for(int j=0;j<c;j++)
                mat[i].push_back(arr[i*c+j]);
    }
    template<typename U>
    Matrix<T>& operator +=(const Matrix<U>&M)
    {
        for(int i=0;i<r;i++)
            for(int j=0;j<c;j++)
                mat[i][j]+=static_cast<T>(M.mat[i][j]);
        return *this;
    }
    template<typename U>
    Matrix<T> operator +(const Matrix<U>&M)
    {
        Matrix<T>tmp=*this;
        return tmp+=M;
    }
};

template<typename T>
istream& operator >>(istream &in,Matrix<T>&M)
{
    in>>M.r>>M.c;
    Matrix<T>tmp(M.r,M.c);
    for(int i=0;i<M.r;i++)
        for(int j=0;j<M.c;j++)
            in>>tmp.mat[i][j];
    M=tmp;
    return in;
}
template<typename T>
ostream& operator <<(ostream &out,Matrix<T>M)
{
    for(int i=0;i<M.r;i++)
    {
        for(int j=0;j<M.c;j++)
            cout<<M.mat[i][j]<<" ";
        cout<<endl;
    }
    return out;
}

编辑: 谢谢大家的建议。

我只有一个小问题,说我确实想实现错误检查(例如:检查边界、有效参数等)但是我确实希望为用户提供一个完全禁用错误检查的选项,有没有实现这个的好方法? 我需要的是类似示例:`ios_base::sync_with_stdio(0);。 再次感谢。

【问题讨论】:

  • 您假设 RHS 的尺寸与 LHS 相同 - 这可能不是故意的。

标签: c++ templates matrix


【解决方案1】:

几点:

  • 使用单个std::vector&lt;T&gt; 而不是std::vector&lt;std::vector&lt;T&gt;&gt;。用 y*r+x 对其进行索引 - 使用和运算符重载可以使这更容易(见下一点)。这将更节省内存并且速度稍快(并且您的 init 会容易得多:resize(r*c))。
  • 将您的数据成员设为私有,以保护您的矩阵免受意外的大小更改。例如,用户代码当前可以调整向量的大小,但保留旧的 rc
  • 重载operator() 以访问矩阵(常量和非常量)。如果您确实必须使用 matrix[r][c] 语法而不是 matrix(r,c),请考虑重载 operator[] 并将迭代器返回到正确的行(向量迭代器是随机访问的,因此它们将提供 operator[])。
  • operator+ 实现为非朋友非成员函数 - 改进封装!
  • 按照其他人的建议使用初始化列表。
  • 让当前采用T* 的构造函数采用迭代器。这样你就可以自动获得指针支持以及许多其他很酷的东西,例如调试迭代器的范围检查、兼容值类型的自动类型转换以及对所有其他随机访问迭代器的支持。还可以考虑逐行填充矩阵,这样您也可以使用前向迭代器。

【讨论】:

  • 谢谢。我将矩阵压缩为一维向量,速度显着提高。关于第 3 点,我只有一个问题。任何一种运算符重载都会出现速度问题。更具体地说,这三个中的哪一个在访问元素时会更快? 1. 使用 matrix[i][j] 2. 使用 matrix(i,j) 或仅定义一个 getter/setter 函数,如 matrix.at(i,j) 等。谢谢。
  • @jack_carver:如果你的编译器不是很破旧,它会归结为相同的机器指令,所以不应该有任何速度差异。似乎 [][] 可能更难优化(因为它需要一个临时的) - 但我认为任何最近的编译器都可以优化它。
【解决方案2】:

如果您想知道它是如何正确完成的,请查看 Eigen 项目的实现:

https://bitbucket.org/eigen/eigen/src/49e00a2e570c/Eigen/src/Core/Matrix.h

https://bitbucket.org/eigen/eigen/src/49e00a2e570c/Eigen/src/Core/MatrixBase.h

Eigen 是一个用于 C++ 的重模板快速矩阵库。我敢肯定那里有很多用处。

【讨论】:

    【解决方案3】:

    1 .为构造函数使用初始化列表,例如Matrix (...) : r(_r), c(_c)

    2.operator + 可以简单地为{ return this-&gt;Add(M) }(假设类型名TU 是兼容的,并且您已经实现了一些Add() 方法)。 3. 如果你是从二维数组构造一些东西,那么使用下面的技术:

    template<typename T>
    class Matrix {
    public: // ...
      void Construct (T *arr)
      {
        mat.resize(r);
        for(int i=0;i<r;i++)
          for(int j=0;j<c;j++)
            mat[i].push_back(arr[i*c+j]);
      }
      template<size_t ROW, size_t COL>
      Matrix(T (&arr)[ROW][COL]) : r(ROW), c(COL)
      {
        Construct(arr);
      }
    //...
    };
    

    这样你在调用的时候就不必传递二维数组的大小,这样可以减少出错的概率。

    1. 为了提高可读性,您可以为类成员使用更好的名称:this-&gt;rt_r

    【讨论】:

    • 这个构造函数重载很酷,但它不能处理一维数组或泛型迭代器。 +1 表示可爱 :)
    • 感谢您的建议。但是 (*this+M) 不会导致无限递归吗?
    • @Itjax,一维数组可以用同样的方式处理,像 template&lt;size_t ROW&gt; Matrix (T (&amp;arr)[ROW]); 这样再增加 1 个重载。对于泛型迭代器,我们可以放置更多的重载。 @jack_carver ...我认为(*this + M) 不会是无限的。我建议这样做是因为您从operator + 按值返回,并且无需临时复制。感谢两位的赞赏。
    • 我已经定义了 += 运算符并将其用于 + 运算符。我试过了,它给出了分段错误。
    • @jac_carver,我很抱歉我的错误。你是对的(*this + M) 会导致无限循环。我的观点是,我们应该尝试做一些优化的方法,在operator + 中添加 2 个矩阵,例如 this-&gt;Add(M),这样我们就不必创建副本。
    【解决方案4】:

    在初始化构造函数参数时使用初始化列表:

     Matrix(int _r,int _c)
        {
            r=_r;c=_c;
    

    ..

    应该是:

     Matrix(int _r,int _c) : r(_r), c(_c)
        {
            ....
    

    【讨论】:

      【解决方案5】:
      1. 您可能希望将 rcmat 成员设为私有。
      2. 您的流式运算符不是对称的(operator&lt;&lt; 不会写出 rc)。

      【讨论】:

      • 我同意将 mat 设为私有,但是我希望能够从外部检索矩阵的维度,因此我会将它们公开(但将它们设为 const)或至少添加一些方法来获取它们的值。
      【解决方案6】:

      我不会使用向量的向量来实现这一点。这样一来,您就为所有查找增加了相当多的开销,而没有任何实际需要。只需分配T 的一维或二维数组,然后在您的类中使用该数组。这也允许快速复制到其他对象实例,而无需遍历所有字段以及重置整个矩阵。

      此外,当您以后不应更改成员时,您应该将其设为 const(在您的示例中为 rc)。要初始化它们,只需使用 Tony 帖子中的构造函数列表。

      【讨论】:

      • 使用嵌套向量为查找增加了多少开销?肯定有内存开销,但没有查找开销。
      【解决方案7】:

      您在浪费时间保持_r_c,因为向量保持自己的大小。

      【讨论】:

        【解决方案8】:

        我想知道你为什么在这个 Class 初始化中使用了非常昂贵的 push_back 函数:

        // Pointer to the first element is passed (&arr[0][0])
        Matrix(T *arr,int _r,int _c)
        {
            r=_r;c=_c;
            mat.resize(r);
            for(int i=0;i<r;i++)
                for(int j=0;j<c;j++)
                    mat[i].push_back(arr[i*c+j]);
        }
        

        既然您已经有了行数和列数,请考虑一下:

        // Pointer to the first element is passed (&arr[0][0])
        Matrix(T *arr,int _r,int _c)
        {
            r=_r;c=_c;
            mat.resize(r);
            for(int i=0;i<r;i++)
                mat[i].resize(c);
                for(int j=0;j<c;j++)
                    mat[i][j]= arr[i*c+j];
        }
        

        【讨论】:

          【解决方案9】:

          如果您考虑的是速度,请不要自己实现矩阵库,而是使用 Boost 库,我在一些项目中使用过它们,并且在 32 位和 64 位架构上都做得很好

          【讨论】:

          • 感谢您的回复。虽然有一个快速的实现会很好,但速度并不是这里的主要关注点。我正在实现这一点,因为在不允许使用外部库的编程竞赛中经常需要此类代码。
          • 比赛是基于速度的?
          • 两种,主要是你解决和提交解决方案的速度,当然还有代码的效率。
          • 那么,如果您需要最大性能,我建议您使用具有适当硬件的 CUDA 库。无论如何,我真的不知道这些比赛是如何计划的,所以你可能无法使用它
          猜你喜欢
          • 1970-01-01
          • 2011-01-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多