delete []在从函数返回时触发断点

时间:2014-10-29 21:15:32

标签: c++ matrix delete-operator

我编写了一个可以执行某些矩阵运算的矩阵类,但是我的最新函数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()中归零,但它没有帮助。

我做错了什么?

3 个答案:

答案 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)

您的分配操作符已被破坏,并且您的班级中每次使用newdelete都会被破坏。使用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;
}

编辑:更改签名以允许编译器制作传入对象的初始副本。