我编写了一个可以执行某些矩阵运算的矩阵类,但是我的最新函数transpose()在delete []上触发了一个断点。 以下是相关部分:
class Matrix {
float* M;
int n, k;
void allocM(int _n, int _k) {
n = _n;
k = _k;
M = new float[n * k];
}
void delM() {
delete[] M;
}
public:
inline float operator()(int i, int j) const { return M[i * k + j]; }
inline float& operator()(int i, int j) { return M[i * k + j]; }
Matrix(int _n, int _k) {
allocM(_n, _k);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < k; ++j) {
operator()(i, j) = 0;
}
}
}
Matrix(const Matrix& source) {
allocM(source.n, source.k);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < k; ++j) {
operator()(i, j) = source(i, j);
}
}
}
~Matrix() {
delM();
}
Matrix& operator=(const Matrix& source) {
delM();
allocM(source.n, source.k);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < k; ++j) {
operator()(i, j) = source(i, j);
}
}
return *this;
}
Matrix operator*(const Matrix& other) const {
Matrix matrix = Matrix(n, other.k);
for (int i = 0; i < matrix.n; ++i) {
for (int j = 0; j < matrix.k; ++j) {
for (int l = 0; l < k; ++l) {
matrix(i, j) += operator()(i, l) * other(l, j);
}
}
}
return matrix;
}
Matrix transpose() const {
Matrix matrix = Matrix(k, n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < k; ++j) {
matrix(k, n) = operator()(n, k);
}
}
return matrix;
}
};
然后,我基本上将两个矩阵相乘。类似的东西:
matrix0 = matrix1 * matrix2.transpose();
结果:在delM() { delete[] M; }
我在矩阵乘法中添加了一个断点。这就是:
程序进入transpose()
函数,然后运行Matrix(int, int)
构造函数。然后嵌套循环在transpose()
中运行,然后是return matrix;
,在其中运行Matrix(const Matrix& source)
,然后返回return matrix;
,然后delM()
然后它会中断第delete[] M;
行
我只包含了赋值运算符函数来说明为什么我有delM()
函数。我已经尝试在声明后将*M
归零并在删除后delM()
中归零,但它没有帮助。
我做错了什么?
答案 0 :(得分:2)
在transpose
中,您应该更改:
matrix(k, n) = operator()(n, k);
要:
matrix(j, i) = operator()(i, j);
您正在访问数组M末尾的数据,从而导致堆损坏。在operator()
中进行边界检查可能是一个好主意,只是为了在将来捕捉这些事情。
正如我在评论中提到的,在operator=
中,添加:
if (this == &source) return;
或者最好(正如Puppy指出的那样),设计它以使自我分配是安全的。原油示例:
float *temp = M;
allocM(source.n, source.k);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < k; ++j) {
operator()(i, j) = source(i, j);
}
}
delete []temp;
return *this;
答案 1 :(得分:2)
您的分配操作符已被破坏,并且您的班级中每次使用new
和delete
都会被破坏。使用std::vector<float>
,它已经以更加有效和正确的方式为您解决了所有这些问题。
答案 2 :(得分:1)
您的赋值运算符,假设您的复制构造函数和析构函数运行正常,可以这样写:
Matrix& operator=(Matrix source)
{
std::swap(source.n, n);
std::swap(source.k, k);
std::swap(source.M, M);
return *this;
}
不需要检查自我分配,它是异常安全的。如果new[]
抛出异常,那么您编写的赋值运算符会将原始对象搞砸。上面的代码缓解了它。
上面的代码使用copy/swap
成语。如果您遵循代码,您将看到它是如何工作的,以及为什么它是实现赋值运算符的巧妙方法,因为复制构造函数和析构函数正在工作。
概要是您从传入的参数创建临时Matrix,然后将您创建的内容换成当前(this
)数据。然后你用“旧东西”让临时死亡,而“新东西”给了this
(这就是所有交换的内容)。
std::swap
只是将两个值相互交换。如果您的要求非常严格,以至于您无法调用std::swap
函数,那么请编写自己的函数并调用它,或者将其写为“内联”:
template <typename T>
void mySwap(T& x, T& y)
{
T temp = x;
x = y;
y = temp;
}
Matrix& operator=(Matrix source)
{
mySwap(source.n, n);
mySwap(source.k, k);
mySwap(source.M, M);
return *this;
}
编辑:更改签名以允许编译器制作传入对象的初始副本。