C ++矩阵类重载运算符通过引用返回

时间:2012-12-29 14:47:26

标签: c++ matrix return binary-operators

我正在编写模板化矩阵类,当从运算符返回值时我得到堆栈溢出:+, - ,*用于更大的矩阵。我宁愿以某种方式通过引用返回来缓解堆栈并避免额外复制,但是,我将不得不返回使用 new 构造的对象并打破“使用删除”的一般规则适用于每个“。由于复制开销和堆栈限制问题,我无法按值返回,而且由于内存泄漏,我也无法通过引用返回,那么我应该做什么呢?

这是我的产品功能(Matrix包含2D阵列元素):

    template<typename T, unsigned int n, unsigned int m> template<unsigned int m2>
Matrix<T,n,m2> Matrix<T,n,m>::operator*(Matrix<T,m,m2>& M) {
    T prod[n][m2];
    if(n*m < GPUAccelerationThreshold)
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m2; j++) {
                prod[i][j] = elems[i][0] * M(0, j); 
                for(int p = 1; p < m; p++)
                    prod[i][j] += elems[i][p] * M(p, j); 
            }
    else {
        array_view<T, 2> product(n, m2, *prod);
        array_view<T, 2> a(n, m, *elems);
        array_view<T, 2> b(m, m2, M.elems[0]);

        parallel_for_each(
            product.extent, 
             [=](index<2> idx) restrict(amp) {
                int row = idx[0];
                int col = idx[1];
                for (int inner = 0; inner < m; inner++) {
                    product[idx] += a(row, inner) * b(inner, col);
                }
            }
        );
        product.synchronize();
    }


    return Matrix<T,n,m2>(prod);
}

我正在写这个课,因为我想在GPU上增加一些矩阵运算(使用MS放大器)。我已经搜索了一个现有的解决方案,找到了GPU加速线性代数库,但我在其中找不到的是带有+, - ,*运算符的简单矩阵类。也许有人可以推荐我吗?

3 个答案:

答案 0 :(得分:3)

三个快速评论:

  • 传统上,Matrix类使用了动态 分配。您没有显示Matrix课程,但是如果您的课程 数据是:
    T myData[n][m];
    
    您可能希望将其更改为:
        std::vector myData;
    
    ,将其初始化为构造函数中的n * m大小,并且 计算operator[]中的单个索引(应该是。{ 如果要进行任何边界检查,则返回代理)。 或者,您可以使用operator()( int i, int j ) 访问元素:是myMatrix( i, j )还是。{ myMatrix[i][j]优先考虑访问取决于您的要求。 虽然这个解决方案稍微增加了总内存使用量(但是 非常轻微),它将堆栈占用空间减少到几个 十几个字节,无论矩阵的大小。
  • 传统上,矩阵类的维度也没有 他们的模板参数的一部分。这是否是一件好事 或不是有争议的。你会得到更好的类型检查(和 您的解决方案在编译时错误,而不是运行时) 但是如果维度是构造函数的参数,那么 与模板参数相比,您可以从命令行中读取它们 或配置文件或其他。这是经典的安全 与灵活性权衡。 关于你的问题,没有尺寸 模板参数意味着T类型的所有矩阵都具有 相同的类型。因此,您可以访问矩阵的内部 从你的会员功能返回,你不再需要了 中间T prod[n][m2]。当然,你可以做所有 Matrix朋友的实例化,或者只是使用访问权限 用于设置值的函数。无论如何,你想要一个 中间T prod[n][m2];这不仅需要很多 堆栈内存,这意味着你必须复制结果。
  • 最后,这有点更高级:在最佳矩阵中 类,operator*不返回矩阵,而是返回帮助器 类,顺序如下:     模板     classMaxMultiply     {         L const * myLhs;         R const * myRhs;     上市:         typedef T value_type;         MatrixMultiply(L const&amp; lhs,R const&amp; rhs)             :myLhs(&amp; lhs)             ,myRhs(&amp; rhs)         {         }         int getX()const         {             return myLhs-&gt; getX();         }         int getY()const         {             return myRhs-&gt; getY();         }         T get(int i,int j)const         {             return calculateIJ(myLhs,myRhs);         }     }; 然后,您提供模板化构造函数和赋值运算符 它使用getX()getY()get( i, j )。您的 operator*也是一个模板,它返回一个 MatrixMultiply:     模板     MatrixMultiply函数     operator *(L const&amp; lhs,R const&amp; rhs)     {         返回MatrixMultiply(lhs,rhs);     } (请注意,L::value_typeR::value_type不是。{ 相同,这将无法编译。这是你想要的,除了 错误信息将远非明确。) 结果是你从未真正构建中间体, 临时矩阵。 可以想象,上述解决方案大大简化了。 你需要额外的代码来处理错误,但我没有 认为并行化是微不足道的。但它避免了 构建所有中间矩阵,即使是复杂的 表达式。 (使用抽象基类可以使用相同的技术, 说MatrixAccessor,使用纯虚拟吸气剂,并导出 Matrix以及MatrixMultiply等所有帮助者 它。恕我直言,这是更可读,和错误消息 从编译器肯定会更容易理解。结果将是 只要编译器实际内联所有成员就一样 功能。但那是一个很大的 if ,因为可以 重要功能嵌套。)

答案 1 :(得分:1)

没有简单的方法可以解决这个问题。您不能将堆栈局部变量作为参考返回,因为返回时变量后面的内存将消失。所以你必须在某处拥有一些专用存储。它不一定来自new / delete,但在复制数据时你需要有某种存储空间。

一种解决方案当然要有三操作数操作,所以代替:

a = b + c;

你使用一个函数:

添加(a,b,c);

其中a,b和c是引用。

它确实使代码变得更加混乱,但我想不出任何更明显的方法来解决问题 - 如果你不想编写自己的分配器/删除函数(或垃圾收集器)。

答案 2 :(得分:1)

实际上我无法完全理解你的想法......二元运算符需要两个参数并创建结果。实际上,您可以看到您正在返回一个新创建的对象。所以这是编写程序的唯一方法:分配内存,使用它,然后删除它。实际上我甚至不明白你的构造函数是做什么的。如果它只是将指针复制到“prod”,那么当你从函数返回时结果矩阵会被破坏,因为一旦函数返回(因为它是在堆栈上创建的),“prod”内存将被删除。所以你也不能通过引用返回它。

我看到解决方案在矩阵构造函数中分配内存的方式。如果您根据矩阵大小将其作为模板,则可以从模板参数中获知矩阵的大小(我发现制作以矩阵大小为参数的模板非常奇怪。这有什么意义?)。因此,您在构造函数中使用“new”分配内存,并在析构函数中使用“delete”将其删除。所以这个内存将根据在OOP中运行良好的RAII方法进行分配。然后实现setElement(i,j,value)等方法,并在二元运算符中设置新创建矩阵中的元素并将其返回。

但是我想要你照顾一些问题。复制构造函数必须真正复制矩阵而不仅仅是一个指针(或者几个析构函数会尝试销毁相同的内存),或者你可以编写实际上在更改时复制矩阵的“lazy copy”模型(参见wiki)。或者您可以将复制构造函数设置为私有而不实现(以防止复制)。 如果您不允许创建诸如“setElement”之类的方法,因为您不希望库的用户更改矩阵值,则可以访问私有数据和方法(即使在我们作为参数或新创建的对象中) )在这样的运算符中因为你在类方法中。

如果您需要将原始指针传递给其他计算函数,就像在“else”部分中完成一样,您可以从只复制指针的指针创建构造函数(但这是危险的方法。如果您传递指针,不能无处访问它,因为矩阵类现在是老板)或完全复制数据(虽然速度较慢但你可以从堆栈或指针传递指针,你需要控制之后),这取决于你的愿望,析构函数将清除它矩阵破坏。或者您甚至可以创建私有方法,例如“getRawMatrix()”,它将返回来自矩阵的数据的原始指针,并将此指针传递给您的计算函数,甚至只需获取原始数据指针,因为您位于矩阵类的方法中

通常你在构造函数中分配内存并制作“lazy copy”模型,因为矩阵可能很大。允许在类内部访问私有数据和成员,这就是所创建的类。我希望它会有用..