c++矩阵类重载操作符按引用返回
C++ matrix class overloaded operators returning by reference
我正在编写模板化的矩阵类,当从操作符返回值时,我得到堆栈溢出:对于较大的矩阵:+,-,*。我更愿意以某种方式通过引用返回来缓解堆栈并避免额外的复制,但是,我将不得不返回一个用new构造的对象,并打破"对每个new使用delete"的一般规则。由于复制开销和堆栈限制问题,我不能按值返回,而且由于内存泄漏,我也不能按引用返回,那么我应该怎么做呢?
这是我的乘积函数(矩阵包含二维数组元素):
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加速线性代数库,但我找不到的是一个简单的矩阵类+,-,*操作符。也许有人能给我推荐一些?
三个快速评论:
- 传统上,
Matrix
类使用动态分配。你没有展示你的Matrix
课程,但是如果你数据是:<>之前T myData [n] [m];之前您可能需要将其更改为:<>之前std::向量myData;之前,在构造函数中将其初始化为n * m
大小计算operator[]
中的单项指标(应返回一个代理(如果你想做任何边界检查)。或者,您可以使用operator()( int i, int j )
访问一个元素:无论是myMatrix( i, j )
还是myMatrix[i][j]
是访问的首选,这取决于您询问的对象。虽然这个解决方案稍微增加了总内存使用量(但是)(非常轻微),它将堆栈占用减少到几个12个字节,与矩阵大小无关。 - 传统上,矩阵类也没有维数模板参数的一部分。这是否是一件好事或者没有是有争议的。您将获得更好的类型检查(和解决方案在编译时(而不是运行时)出现的错误,但是,如果维度是构造函数的参数,那么除了模板参数之外,您还可以从命令行读取它们或者配置文件之类的。这是经典的安全方法Vs.灵活性权衡。关于你的问题,没有维度作为模板参数意味着所有类型为
T
的矩阵都具有相同的类型。因此你可以进入矩阵的内部从成员函数返回,并且不再需要中间T prod[n][m2]
。当然,你可以做所有的实例化Matrix
友元,或者直接使用访问设置值的函数。无论如何,您确实不想要一个中间T prod[n][m2]
;这不仅需要很多的on堆栈内存,这意味着你必须复制结果。 - 最后,这是更高级的:在最佳矩阵中类,
operator*
不返回一个矩阵,而是一个助手类,沿着下面的线:模板类MatrixMultiply{L const* myls;R const* myRhs;公众:typedef T value_type;MatrixMultiply(L constent & lhs, R constent & rhs)@ @ @ @h & h;{}int getX()常量{返回myLhs -> getX ();}int getY() const{返回myRhs -> getY ();}得到(int i, int j) const{返回calculateIJ(myLhs, myLhs);}};然后提供一个模板化的构造函数和赋值操作符它使用getX()
,getY()
和get( i, j )
。你的operator*
也是一个模板,它返回一个MatrixMultiply
:模板MatrixMultiply算子*(L构造& lhs, R构造& rhs){返回MatrixMultiply(lhs, rhs);}(注意,如果L::value_type
和R::value_type
不是同样的,这不会编译。这就是你想要的,除了错误信息将远不清楚。)结果是你从来没有真正构建中间产物,临时的矩阵。可以想象,上面的解决方案被大大简化了。您需要额外的代码来处理错误,而我不需要认为并行化是微不足道的。但它避免了构造所有中间矩阵,即使是复杂的表达式。(同样的技术可以使用抽象基类,例如MatrixAccessor
,带有纯虚getter和派生Matrix
和所有像MatrixMultiply
这样的助手它。恕我直言,这是可读性强得多,错误信息从编译器肯定会更容易理解。结果将是只要编译器实际上内联了所有的成员功能。但这是一个很大的if,因为可能存在重要的函数嵌套。)
没有简单的方法来解决这个问题。您不能返回堆栈局部变量作为引用,因为当您返回时,变量"后面"的内存将消失。所以你必须在某个地方有一些专用的存储空间。它不一定来自new/delete,但在复制数据时确实需要某种存储。
一个解决方案当然是有三个操作数的操作,所以不是:
a = b + c;
你使用了一个函数:
add(a, b, c);
,其中a, b和c是引用。
它确实使代码更加混乱,但我想不出任何更明显的方法来解决这个问题-如果你不想编写自己的分配器/删除函数(或垃圾收集器)。
我看到的解决方案是在矩阵构造函数中分配内存。如果您根据矩阵大小将其作为模板,则可以从模板参数中了解矩阵的大小(我发现将矩阵大小作为参数制作模板非常奇怪…这有什么意义呢?)所以你用new在constructor中分配内存,用delete在destructor中删除内存。因此,这些内存将根据RAII方法进行分配,这种方法在OOP中工作得非常好。然后实现setElement(i, j, value)等方法,并在二进制操作符中设置新创建矩阵中的元素并返回它。
但是有一些问题我想让你处理。复制构造函数必须真正地复制矩阵,而不仅仅是一个指针(或者几个析构函数将试图破坏相同的内存),或者您可以编程"延迟复制"模型,该模型在更改时实际复制矩阵(参见wiki)。或者您可以在没有实现的情况下将复制构造函数设为私有(以完全防止复制)。如果你不允许创建像"setElement"这样的方法,因为你不希望你的库的用户改变矩阵值,你可以访问这些操作符中的私有数据和方法(甚至在我们作为参数或新创建的对象中),因为你在类方法中。如果你需要将原始指针传递给其他计算函数,就像在"else"部分所做的那样,你可以从一个指针创建一个构造函数,它只会复制一个指针(但这是一种危险的方式)。如果你传递一个指针,你不能访问它无处,因为矩阵类现在是老板)或复制数据完全(这是较慢的,但你可以传递那里的指针从堆栈或指针,你需要控制后),根据你的愿望,和析析函数将清理它时,矩阵破坏。或者你甚至可以创建私有方法,如"getRawMatrix()",它将从矩阵返回原始数据指针,并将该指针传递给你的计算函数,或者甚至简单地获得原始数据指针,因为你在矩阵类的方法中。
通常你在构造函数中分配内存并创建"延迟复制"模型,因为矩阵可能很大。类内部允许访问私有数据和成员,这就是类的构成。
- 如何通过引用返回对象
- 函数如何使用引用返回所需的数字?
- 如何防止引用返回的私有结构的突变
- 如何在不使用临时变量的情况下取消引用返回指针的函数的返回值?
- 通过引用返回的变量的范围
- 对于具有引用返回类型的搜索算法,默认返回值应该是什么?
- 运算符重载C++类中的引用返回
- C++对象引用返回不同的值
- 解释通过从函数引用返回数组的语法
- 具有引用返回类型的重写方法上的协变返回类型无效
- 为什么在通过引用返回运算符分配时取消引用'this'指针?
- 为什么我在函数中使用引用并通过引用返回它仍然有效?
- 直接在 C++ 中将值分配给引用返回类型
- C++当您取消引用指向类对象的指针,然后将其作为引用返回时,是否可以对此引用调用方法
- 可以通过常量引用返回默认参数的值吗?
- 按值与右值引用返回
- 非常量引用返回函数在常量值返回函数上用作 r 值
- 当我使用按引用返回时,我不知道这些代码之间的区别
- 为什么通过引用返回向量比通过移动返回要快得多?
- 通过引用返回包含对象的向量