4

我有一个用于神经网络程序和重载算术运算符的定制矩阵库。这是类声明:

class Matrix{
public:
int m;
int n;
double **mat;
Matrix(int,int);
Matrix(int);
Matrix(const Matrix& that):mat(that.mat),m(that.m),n(that.n)
    {
        mat = new double*[m];
        for(int i = 0;i<m;i++)mat[i] = new double[n];
    };
~Matrix();
friend istream& operator>>(istream &in, Matrix &c);
friend ostream& operator<<(ostream &out, Matrix &c);
Matrix operator+(const Matrix& other);
};

这是 + 操作的函数定义:

 Matrix Matrix::operator+(const Matrix& other)
    {
        Matrix c(m,n);
        for(int i=0;i<m;i++)
        {
           for(int j = 0; j<n;j++)
               c.mat[i][j] = mat[i][j] + other.mat[i][j];
        }
        return c;
    }

我试图以各种方式实现它并且错误是相同的......这是一个实例

Matrix x(m,n); //m and n are known
x = a+b; // a and b are also m by n matrices

我已经使用断点调试了代码,这是错误... 运算符函数中的局部矩阵'c'在返回之前被破坏,因此分配给x的是垃圾指针..

请给我一些建议...

4

6 回答 6

2

您需要为您的类定义一个复制构造函数。复制构造函数将需要为数据分配内存mat并制作数据副本。

没有这个,当你return c构造一个新对象时,它的值与matas相同c。当c随后超出范围时,它会删除c.mat. 结果, 的副本c留下了一个悬空指针。

完成此操作后,您还应该实现一个赋值运算符。

于 2013-02-21T14:46:01.787 回答
2

您返回的值用于初始化一个临时值,然后在您返回的值被销毁后将该临时值复制到结果中。这是正常行为(除非呼叫因 NRVO 而被忽略)。

但是,由于您的类没有明确定义的复制构造函数,因此将调用隐式生成的复制构造函数,这只会将指针 ( mat) 复制到已被返回对象的析构函数释放的内容。

这违反了所谓的“三法则”,这是一种编程最佳实践,即每当您的类显式定义复制构造函数、赋值运算符或析构函数时,就应该定义所有这些。基本原理是定义其中一个的类很可能这样做,因为它正在管理一些资源,并且为了正确处理资源释放/获取逻辑,需要所有这三个特殊成员函数。

请注意,在 C++11 中,您还可以有一个移动构造函数,只需分配指针并使您从中移动的对象无效,就可以执行Matrix' 内容的传输。

Matrix(Matrix&& m)
{
    mat = m.mat;
    m.mat = nullptr;
}

当然,如果你引入了一个移动构造函数,你将不得不相应地修改你的类析构函数来检查你是否真的需要释放分配的内存:

~Matrix()
{
    if (m.mat == nullptr)
    {
        return;
    }

    ...
}
于 2013-02-21T14:47:43.023 回答
0

您的 Matrix 类有一个原始指针成员,并且可能在其构造函数中分配内存,但您没有复制构造函数或复制赋值运算符。

此外,您有一个析构函数,但没有复制构造函数或复制赋值运算符。这违反了三法则。

于 2013-02-21T14:47:04.360 回答
0

Matrix c是一个局部变量。因此,当创建它的方法结束时,它就会被销毁。在 C++ 中,这种不需要的情况通常通过复制对象来解决。您可以定义具有相同功能的复制构造函数和赋值运算符 =。复制的问题是它很慢,所以如果你希望它更快,你应该使用不同的方法而不是复制。例如,您可以将参数添加到调用方将引用传递给存储结果的现有矩阵对象的方法。

于 2013-02-21T14:47:20.370 回答
0

您需要一个复制构造函数和一个赋值运算符来为您的类制作对象的深层副本,因为编译器生成的函数不会。

编译器生成的复制构造函数和赋值运算符将简单地复制包含在类中的对象。在您的情况下,这些是 POD,因此自动生成的函数将简单地进行按位复制。在 的情况下double**,这将导致指针值的副本,而不是指向的值。结果,Matrix在析构函数从你下面拉出地毯之前,你最终得到了两个指向相同底层数据的对象。

于 2013-02-21T14:51:14.957 回答
-1

您应该更改代码以返回 Matrix *,而不是 Matrix 对象。这样,您可以确保 Matrix 对象存在于函数之后。(您当前的代码使 Matrix 对象成为函数变量,因此它将在函数结束后被删除)。

您的代码可能如下所示:

Matrix *Matrix::operator+(const Matrix& other)
{
    Matrix *c = new Matrix(m,n);
    for(int i=0;i<m;i++)
    {
       for(int j = 0; j<n;j++)
           c->mat[i][j] = mat[i][j] + other.mat[i][j];
    }
    return c;
}

编辑:显然这是不好的做法,我猜我今天也学到了一些东西:)

于 2013-02-21T14:49:05.880 回答