【问题标题】:Program crashes at: (1) matrix multiplication; and (2) failed matrix addition/subtraction程序在以下情况下崩溃: (1) 矩阵乘法; (2) 矩阵加法/减法失败
【发布时间】:2014-10-05 01:05:32
【问题描述】:

简而言之,我的任务是创建一个动态分配内存以形成 int 值矩阵的类。

类的一部分是执行基本矩阵计算的成员函数——加法、减法和乘法。一切都可以编译(至少在我这边),但是当我使用驱动程序测试乘法部分时,它一直在崩溃。

我将 Codeblocks 用作我的 IDE,但调试器在尝试解决问题时运气不佳。计算似乎完成了(使用正确的值),但随后某处出现了可怕的错误。

为清楚起见,Matrix 类的每个对象都有以下成员数据:

private:
    int rows;
    int cols;
    int **element;

下面是实现文件的 sn-p,其中充实了重载的 operator*。在执行乘法的循环之前将 temp.element[i][x] 设置为 '0' 的部分被注释掉,因为默认构造函数已经将所有值设置为 '0' - 我在放入时忘记了起初。当我也没有注释掉它时它也不起作用。

在测试中,我使用了一个 2x3 数组和一个 3x2 数组。

Matrix Matrix::operator*(const Matrix &aMatrix) const
{
    if(cols == aMatrix.rows)
    {
        Matrix temp(rows, aMatrix.cols);
        for(int i = 0; i < rows; i++)
        {
            for(int x = 0; x < aMatrix.cols; x++)
            {
                //temp.element[i][x] = 0;
                for(int n = 0; n < cols; n++)
                {
                    temp.element[i][x] += (element[i][n]
                                       * aMatrix.element[n][x]);
                }
            }
        }
        return temp;
    }
    else
    {
        cerr << "Matrix multiplication failed -- incompatible matrix sizes."
             << endl;
        return *this;
    }
}

在尝试检查代码并发现错误后,我开始重新检查我拥有的其他功能。看起来加法和减法都有效,但如果矩阵不兼容(即尝试添加 2x3 和 4x4),程序将关闭。

下面是加法的 sn-p(减法几乎相同,只是在最后的循环中使用 '-' 而不是 '+'。

Matrix Matrix::operator+(const Matrix &aMatrix) const
{
    if(rows == aMatrix.rows && cols == aMatrix.cols)
    {
        Matrix temp(rows, cols);
        for(int i = 0; i < rows; i++)
        {
            for(int x = 0; x < cols; x++)
            {
                temp.element[i][x] = element[i][x] + aMatrix.element[i][x];
            }
        }
        return temp;
    }
    else
    {
        cerr << "Undefined matrix addition -- matrices are different sizes."
             << endl;
        return *this;
    }
}

感谢任何帮助或见解。谢谢。

已编辑:添加了重载赋值运算符、复制构造函数和析构函数代码。

下面是重载的赋值运算符:

Matrix Matrix::operator=(Matrix aMatrix)
{
    if(this != &aMatrix)
    {
        for(int i = 0; i < rows; i++)
        {
            delete [] element[i];
            element[i] = NULL;
        }
        delete [] element;
        rows = aMatrix.rows;
        cols = aMatrix.cols;
        element = new int* [rows];
        for(int i = 0; i < rows; i++)
        {
            element[i] = new int [cols];
            for (int x = 0; x < cols; x++)
            {
                element[i][x] = aMatrix.element[i][x];
            }
        }
    }
    return *this;
}

下面是复制构造函数:

Matrix::Matrix(const Matrix &aMatrix)
{
    rows = aMatrix.rows;
    cols = aMatrix.cols;
    element = new int* [rows];
    for(int i = 0; i < rows; i++)
    {
        element[i] = new int [cols];
        for (int x = 0; x < cols; x++)
        {
            element[i][x] = aMatrix.element[i][x];
        }
    }
}

析构函数:

Matrix::~Matrix()
{
    for(int i = 0; i < rows; i++)
    {
        delete [] element[i];
        element[i] = NULL:
    }
    delete [] element;
    element = NULL;
}

【问题讨论】:

  • 如何为element 分配内存?你不应该在做任何算术之前检查尺寸吗?
  • 在默认构造函数中动态分配。我用动态内存分配标记了这个问题,但没有包括>_
  • @uber08 - 您正在按值返回一个矩阵。这意味着我们需要查看您的Matrix 用户定义的复制构造函数、赋值运算符和析构函数。它们都在您发布的代码中发挥了重要作用。
  • @uber08 - Matrix Matrix::operator=(Matrix aMatrix) 你应该通过 const 引用而不是值来传递。此外,您应该返回一个引用,而不是一个全新的对象。它有很多错误,并且会导致问题。
  • 我无法想象在错误条件下返回操作的 lhs 的充分理由。客户端代码很可能注定了标准错误上的各种错误计算和相应的错误消息。实际上,假设库拥有标准错误对于客户端的开发人员来说可能是痛苦的。也许他们对标准错误有特定的想法。也许他们将其重定向到 /dev/null 之类的东西。

标签: c++ multidimensional-array matrix-multiplication dynamic-memory-allocation


【解决方案1】:

您发布了一个复制构造函数和赋值运算符。您的赋值运算符有 4 个主要问题:

  1. 您应该通过 const 引用而不是值来传递参数。
  2. 您应该返回对当前对象的引用,而不是全新的对象。
  3. 如果new 在赋值过程中抛出异常,那么你已经通过预先删除内存搞砸了你的对象。
  4. 这是多余的。相同的代码出现在您的复制构造函数中。

您可以根据复制构造函数重写赋值运算符并缓解这些问题:

#include <algorithm>
//...
Matrix& Matrix::operator=(const Matrix& aMatrix)
{
    Matrix temp(aMatrix);
    swap(*this, temp);
    return *this;
}

void Matrix::swap(Matrix& left, Matrix& right)
{
   std::swap(left.rows, right.rows);
   std::swap(left.cols, right.cols);
   std::swap(left.element, right.element);
}

您只需将swap 函数添加到您的Matrix 类(可能作为private)函数中。

上面的代码使用copy/swap 成语,并缓解了我提到的所有问题。如果您的复制构造函数和析构函数编写正确,这将起作用。 SO上有很多线程都在谈论这个成语,但基本上发生的事情是这样的:

  1. 从传入的对象创建一个临时对象。如果new 投掷有问题,您的this 不会搞砸。

  2. this 的成员替换为临时成员。这将使用临时数据刷新this 对象,并为临时对象提供我们不再需要的旧数据。

  3. 用我们从this 给它的旧东西让临时死去吧。

  4. 返回this

至于其他方面,如果两个矩阵不能相乘,则抛出异常。不要返回不能真正反映所发生情况的虚假或令人困惑的 Matrix 对象。

此外,您应该首先编写运算符+=*=。为什么?因为实现运算符+* 可以分别以+=*= 的形式完成,而且您还可以获得+=*= 可用的额外好处。

例如:

Matrix& Matrix::operator+=(const Matrix &aMatrix)
{
    if(rows != aMatrix.rows || cols != aMatrix.cols)
       throw SomeException;
    for(int i = 0; i < rows; i++)
    {
        for(int x = 0; x < cols; x++)
            element[i][x] += aMatrix.element[i][x];
    }
    return *this;
}

Matrix Matrix::operator+(const Matrix &aMatrix)
{
   Matrix temp(*this);
   temp += aMatrix;
   return temp;
}

因此,如果矩阵大小有问题,运算符 + 和 += 将引发异常。请注意运算符+ 是如何根据+= 实现的。对**= 以及--= 采用相同的方法。

【讨论】:

  • 嗯,一切看起来都不错。感谢所有的帮助。就像我上面所说的,我需要看看我可以从提供的内容中修改哪些内容。话虽如此,如果允许建议的更改,您的答案可能会导致程序成功。接受答案。
【解决方案2】:

您需要弄清楚这些操作的语法和语义是什么。从您的示例看起来好像您想要类似的东西:

Matrix lhs = <something>;
Matrix rhs = <something>;
Matrix result = lhs * rhs;

“只是工作”。虽然这在语法上可能很好,但您需要考虑如果 lhs 和 rhs 不能相乘(或相加等)会发生什么。您还需要担心正确设置结果。您不能只返回一个“由行数、列数和指针数组组成的结构”并期望不会遇到可怕的内存管理问题。

导致产品消失的原因是什么?什么时候超出范围? C++ 对于线性代数来说不是很好的语言。

您需要做出的第一个决定是您是否打算为无效操作抛出异常,或者返回一些错误指示,可能是 bool 和一个 const char * 到错误消息。

那么您可能应该尝试编写一个带有结果引用和 2 个操作数引用的函数,如果矩阵支持该操作,则只需正确掌握基本代数即可。 (我还没有提到你的 Matrix 只是一个整数矩阵。)

在“现实生活”中,您可能会尝试找到一个管理所有这些的开源标准解决方案,因为需要非常小心地管理数值运算。

甚至可能有人认为这是一个在几十年前就已经在 Fortran 中得到解决的问题,并且直到今天,如果性能是一个问题,那么基础计算很可能是在 Fortran 中,或者是从 C 中移植的库。

【讨论】:

  • 感谢您的浏览。一旦函数被调用并且只有这个挥之不去的产品,我一直在为内存会发生什么而苦苦挣扎......不幸的是,我被提供给我们的原型绑定到了这个结构。我会看看你的其他建议。
  • @uber08 - 如上面评论中所述,您需要首先确保您的 Matrix 对象具有正确的复制语义。到目前为止,我们绝对没有任何迹象。
猜你喜欢
  • 1970-01-01
  • 2012-12-29
  • 2015-10-15
  • 1970-01-01
  • 2017-03-11
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
相关资源
最近更新 更多