在重载算术运算符中调用C ++析构函数

时间:2013-02-21 14:44:07

标签: c++ matrix operator-overloading

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

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的是垃圾指针..

请建议我......

6 个答案:

答案 0 :(得分:2)

您需要为您的类定义复制构造函数。复制构造函数需要为mat分配内存并复制数据。

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

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

答案 1 :(得分:2)

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

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

这违反了所谓的 Rule of Three ,这是一种编程最佳实践,表示只要您的类明确定义了复制构造函数,赋值运算符或析构函数,那么它应该定义所有。理由是,定义其中一个的类最有可能是因为它管理一些资源,并且为了正确处理资源释放/获取逻辑,所有这三个特殊成员函数都需要。

请注意,在C ++ 11中,您还可以使用 move 构造函数,只需指定指针即可执行Matrix内容的传输。使您移动的对象无效。

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

当然,如果你引入了一个移动构造函数,你必须相应地修改你的类析构函数,以检查你是否真的必须释放已分配的内存:

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

    ...
}

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:-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;
}

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