代码之家  ›  专栏  ›  技术社区  ›  cibercitizen1

C++中矩阵的算子+

  •  5
  • cibercitizen1  · 技术社区  · 15 年前

    我认为矩阵的a+运算符的天真实现(例如2d) 在C++中:

    class Matrix {
    
      Matrix operator+ (const Matrix & other) const {
          Matrix result;
          // fill result with *this.data plus other.data
          return result;
      }
    }
    

    所以我们可以像

    Matrix a;
    Matrix b;
    Matrix c;
    
    c = a + b;
    

    对吗?

    但是,如果矩阵很大,这就没有效率,因为我们正在做一个不必要的复制(返回结果)。

    因此,如果我们不想提高效率,就必须忘记干净的要求:

    c = a + b;
    

    对吗?

    你建议/喜欢什么? 谢谢。

    8 回复  |  直到 15 年前
        1
  •  12
  •   Jerry Coffin    15 年前

    C++标准允许编译器在这种情况下删除不必要的拷贝(它被称为“命名返回值优化”,通常缩写为NRVO)。当您返回一个临时变量而不是一个命名变量时,会有一个匹配的“rvo”。

    几乎所有最近合理的C++编译器都实现了NRVO和RVO,所以一般来说,你可以忽略这样一个事实,即这个构造不会特别有效。

    编辑:当然,我是在讨论返回新矩阵时所涉及的副本,它保存了加法的结果。你大概 要按常量引用传递输入:

    Matrix operator+(Matrix const &other) const { 
        Matrix result;
        // ...
        return result;
    }
    

    …否则,按值传递,但返回传递的值:

    Matrix operator+(Matrix other) const { 
        other += *this;
        return other;
    }
    

    注意这取决于交换性 b+a 而不是 a+b )所以,虽然可以添加,但对其他操作不起作用。

        2
  •  3
  •   randomThought    15 年前

    您可以在不触发复制构造的情况下返回值。它被称为r值引用 这里解释得很详细 http://www.artima.com/cppsource/rvalue.html

        3
  •  2
  •   paercebal    15 年前

    注意,您的第一个天真实现是非常本机的,因为没有任何东西是通过引用传递的。我假设这是一个非常天真的例子,没有必要提醒读者通过引用而不是通过值传递的好处。

    请注意,我也使用了非成员函数运算符,而不是成员函数,但最后,结果(几乎)是相同的。

    如果要确保不创建必要的副本,应尝试非操作员版本:

    void add(Matrix & result, const Matrix & lhs, const Matrix & rhs) ;
    

    如果您想用operator方式(这是我首选的解决方案),那么您应该假设operator+将创建一个临时的。然后您应该定义operator+和operator+=:

    Matrix & operator += (Matrix & result, const Matrix & rhs) ;
    {
       // add rhs to result, and return result
       return result ;
    }
    
    Matrix operator + (const Matrix & lhs, const Matrix & rhs) ;
    {
       Matrix result(lhs) ;
       result += rhs ;
       return result ;
    }
    

    现在,您可以尝试“利用”编译器优化,并将其编写为:

    Matrix & operator += (Matrix & result, const Matrix & rhs) ;
    {
       // add rhs to result, and return result
       return result ;
    }
    
    Matrix operator + (Matrix lhs, const Matrix & rhs)
    {
       return lhs += rhs ;
    }
    

    由Herb Sutter在 C++编码标准 , 27。偏爱算术和赋值运算符的规范形式 ,P48-49:

    一种变体是让运算符@[@being+,-,whatever]按值接受其第一个参数。这样,您就可以安排编译器本身隐式地为您执行复制,这可以让编译器在应用优化时有更大的回旋余地。

        4
  •  1
  •   Mark B    15 年前

    如果你真的关心性能(你分析了吗?)您可能根本不应该实现operator+,因为您无法控制它是否会导致创建一个非最佳的临时对象。只需实现运算符+=和/或成员函数 add(Matrix& result, const Matrix& in1, const Matrix& in2) 让你的客户创建正确的临时文件。

    如果你需要接线员,杰里·科芬的任何一个都可以。

        5
  •  1
  •   Steve314    15 年前

    正如其他人所指出的,您的实现并不像您想象的那样昂贵。但是,定义其他方法来修改对象以在关键的内部循环中使用可能有一定的意义。

    编辑 -固定在以下段落

    这里的要点是,即使使用了返回值优化,您仍然可以构造一个局部变量,然后在operator+退出后将其分配给结果变量。当然,还要破坏那个额外的物体。仍然有一个额外的对象用于临时保存结果。使用写时拷贝进行引用计数是可能的,但这会给矩阵的每次使用增加解引用开销。

    对于99%的案例来说,这些都不是问题,但是偶尔你会遇到一个关键的案例。如果你处理的是大型矩阵,参考计数的开销将是微不足道的-但对于2d到4d,有时你可能会非常关心这些额外的周期-或者更重要的是,关于 当您希望矩阵位于堆栈上或嵌入到某个struct/class/array中时,将其放在堆上。

    也就是说-在这些情况下,你可能不会编写自己的矩阵代码-你只需要使用来自directx或opengl的矩阵操作或其他什么。

        6
  •  0
  •   Andrey    15 年前

    有两种解决方法。

    1)使用参考文献:

    Matrix& operator+ (Matrix& other) const {
    

    2)在复制构造函数中使用浅复制。是,将创建新的矩阵对象,但不会再次创建实际矩阵

        7
  •  0
  •   Cthutu    15 年前

    我试图在return语句中构造矩阵:

    Matrix Matrix::operator + (const Matrix& M)
    {
        return Matrix(
            // 16 parameters defining the 4x4 matrix here
            // e.g. m00 + M.m00, m01 + M.m01, ...
        );
    }
    

    这样你就不用临时工了。

        8
  •  0
  •   Alexey Malistov    15 年前
    class Matrix { 
    
      Matrix & operator+=(const Matrix & other) {
          // fill result with *this.data plus other.data 
          // elements += other elements 
          return *this;
      }
      Matrix operator+ (const Matrix & other) {
          Matrix result = *this; 
          return result += other; 
      } 
    }