C++ C++;关于操作员过载和内存泄漏的参考

C++ C++;关于操作员过载和内存泄漏的参考,c++,memory-leaks,reference,operator-overloading,pass-by-reference,C++,Memory Leaks,Reference,Operator Overloading,Pass By Reference,我使用以下代码 Matrix operator * (const Matrix & obj) { Matrix m = Matrix(); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { m[i][j] = 0; for (int k = 0; k < 4; k++) m[

我使用以下代码

Matrix operator * (const Matrix & obj)
{
    Matrix m = Matrix();

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
        {
            m[i][j] = 0;

            for (int k = 0; k < 4; k++)
                m[i][j] += _data[i][k] * obj._data[k][j];
        }

    return m;
}
但我想这是非常未优化的,因为我假设我在操作符中创建的新矩阵正在疯狂地复制。如果我这么做的话

Matrix & operator * (const Matrix & obj)
{
    Matrix m = Matrix();

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
        {
            m[i][j] = 0;

            for (int k = 0; k < 4; k++)
                m[i][j] += _data[i][k] * obj._data[k][j];
        }

    return m;
}
矩阵和运算符*(常数矩阵和obj)
{
矩阵m=矩阵();
对于(int i=0;i<4;i++)
对于(int j=0;j<4;j++)
{
m[i][j]=0;
对于(int k=0;k<4;k++)
m[i][j]+=_数据[i][k]*obj._数据[k][j];
}
返回m;
}
将运算符的返回类型更改为对矩阵的引用,整个代码将完全停止工作(它会编译,只是矩阵没有按应有的方式相乘)

如果我把它改成

Matrix & operator * (const Matrix & obj)
{
    Matrix & m = * new Matrix();

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
        {
            m[i][j] = 0;

            for (int k = 0; k < 4; k++)
                m[i][j] += _data[i][k] * obj._data[k][j];
        }

    return m;
}
矩阵和运算符*(常数矩阵和obj)
{
矩阵&m=*新矩阵();
对于(int i=0;i<4;i++)
对于(int j=0;j<4;j++)
{
m[i][j]=0;
对于(int k=0;k<4;k++)
m[i][j]+=_数据[i][k]*obj._数据[k][j];
}
返回m;
}
现在它工作了,但我有一个坏的内存泄漏

那么我该如何解决这个问题呢?有优雅的解决方案吗


谢谢

第二个版本-引用返回的自动存储变量-是未定义的行为-不要这样做

第三个版本很糟糕,您不应该这样做-尽可能避免动态分配

第一个版本不应该比您预期的慢太多-假设所有的优化都已到位并且您的编译器支持RVO/NRVO(它可能支持)


另一种选择是返回一个智能指针——这样,在类中创建的对象不会在返回时被复制,而是一个指向该对象的托管指针(这很容易就会更加有效)。

您的第一种情况是正确的。它实际上可能是有效的(对于…)的某些定义,因为允许编译器优化副本(即,如果结果应该是正确的,它可能会创建新对象);这被称为“返回值优化”

你的第二个解决方案是垃圾;您正在返回对堆栈上对象的引用,该引用将在函数返回时消失。我想知道为什么它甚至没有给你警告

正如您所观察到的,第三种解决方案会泄漏内存,因为没有任何东西可以释放“新”对象。如果使用RVO,它的效率并不比第一个高,实际上效率更低,因为您会得到额外的“动态内存分配”开销


除了完全不使用操作符*进行乘法并找到一个更合适的API来乘法矩阵之外,您对此无能为力。根据您使用的名称判断,您可能可以从内部循环中提取一些乘法。

您的函数正在创建一个新对象,它应该按值返回该对象

无法返回对局部变量的引用,而将引用返回到动态分配的内存有多个问题:它无法解决任何问题(不会减少由表达式创建的对象的数量,并且由于动态分配,使每个对象的创建成本更高),并且会增加内存泄漏

如果没有类
矩阵的定义,就不清楚内存是在数组中处理的还是动态分配的。如果它是动态分配的,并且您有一个C++11编译器,那么您可以实现移动构造和移动分配,并且所有这些副本的成本都将消失

在C++03(以及C++11)中,您可以实现
操作符*=
,并手动处理创建的对象(假设您可以有效地执行此操作):

这将创建一个临时文件,并将所有乘法应用到位,从而减少副本数量


无论如何,我不会花太多时间在这方面,因为复制4x4矩阵并不昂贵。

为了优化,您可以使用三参数函数: 无效乘法(常数矩阵和a、常数矩阵和b、矩阵和结果)


通过这种方式,您可以确保不会有不必要的副本。

这个问题似乎更多地是关于优化,而不是其他任何问题

所以我不明白,如果您非常担心复制的潜在开销,为什么还要使用循环

制作一个接受16个参数的矩阵构造函数

然后:


为什么在第二个for循环中有m[i][j]的清除?我在一段时间内还没有使用C++,那么你的代码实际上是做什么的呢?“矩阵M=Matrix x”,“你的说法”矩阵M;“但是用活力……@ davIDID。你是对的,实际上我的构造函数将元素初始化为身份,所以我实际上不需要。”我是一名Java程序员:/@MartinMarinov-David Rodríguez-dribeas的答案应该是可以接受的。我将保留我的值以备将来参考,但他的更全面。在第一个版本中,无论编译器如何优化代码,都将有3个临时值。RVO将应用于每个
运算符的返回*
,但它将为该运算符的每次执行创建一个临时值(返回智能指针也是如此,这将导致动态分配的额外成本)
Matrix & operator * (const Matrix & obj)
{
    Matrix & m = * new Matrix();

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
        {
            m[i][j] = 0;

            for (int k = 0; k < 4; k++)
                m[i][j] += _data[i][k] * obj._data[k][j];
        }

    return m;
}
//v1 = m_proj * m_view * m_object * v1;
Matrix tmp = m_proj;
tmp *= m_view;
tmp *= m_object;
tmp *= v1;
v1 = tmp;
Matrix operator * (const Matrix & obj)
{
    return Matrix(
        _data[0][0]*_obj._data[0][0] + _data[0][1]*_obj._data[1][0] + _data[0][2]*_obj._data[2][0] + _data[0][3]*_obj._data[3][0], 
        _data[1][0]*_obj._data[0][0] + _data[1][1]*_obj._data[1][0] + _data[1][2]*_obj._data[2][0] + _data[1][3]*_obj._data[3][0], 
        _data[2][0]*_obj._data[0][0] + _data[2][1]*_obj._data[1][0] + _data[2][2]*_obj._data[2][0] + _data[2][3]*_obj._data[3][0], 
        // etc...
        );
}