代码之家  ›  专栏  ›  技术社区  ›  Bartek Dettlaff

直接损失或间接损失-Valgrind问题

  •  1
  • Bartek Dettlaff  · 技术社区  · 7 年前

    我的矩阵程序有问题。

    我的错误如下:

    24 bytes in 1 blocks are indirectly lost in loss record 1 of 7
    ==5334==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==5334==    by 0x402B26: Matrix::CountingReference::CountingReference(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x401725: Matrix::Matrix(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x401305: main (in /home/detek/Pulpit/Matrix/main)
    ==5334== 
    ==5334== 24 bytes in 1 blocks are indirectly lost in loss record 2 of 7
    ==5334==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==5334==    by 0x402B26: Matrix::CountingReference::CountingReference(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x401725: Matrix::Matrix(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x40187D: Matrix::operator+(Matrix const&) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x40144E: main (in /home/detek/Pulpit/Matrix/main)
    ==5334== 
    ==5334== 72 bytes in 3 blocks are indirectly lost in loss record 3 of 7
    ==5334==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==5334==    by 0x402B7D: Matrix::CountingReference::CountingReference(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x401725: Matrix::Matrix(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x401305: main (in /home/detek/Pulpit/Matrix/main)
    ==5334== 
    ==5334== 72 bytes in 3 blocks are indirectly lost in loss record 4 of 7
    ==5334==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==5334==    by 0x402B7D: Matrix::CountingReference::CountingReference(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x401725: Matrix::Matrix(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x40187D: Matrix::operator+(Matrix const&) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x40144E: main (in /home/detek/Pulpit/Matrix/main)
    ==5334== 
    ==5334== 120 (24 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 7
    ==5334==    at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==5334==    by 0x401712: Matrix::Matrix(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x401305: main (in /home/detek/Pulpit/Matrix/main)
    ==5334== 
    ==5334== 120 (24 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 6 of 7
    ==5334==    at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==5334==    by 0x401712: Matrix::Matrix(int, int) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x40187D: Matrix::operator+(Matrix const&) (in /home/detek/Pulpit/Matrix/main)
    ==5334==    by 0x40144E: main (in /home/detek/Pulpit/Matrix/main)
    

    我的代码在这里:

    这是矩阵。cpp公司

    #include <iostream>
    #include <cstdlib>
    #include <fstream>
    #include <ctime>
    #include "Matrix.h"
    using namespace std;
    
    ostream& operator<<(ostream& o,const Matrix& Object)
    {
        for(int i=0;i<Object.dane->wiersz;i++)
        {
            for(int j=0;j<Object.dane->kolumna;j++)
            {
                o<<Object.dane->wsk[i][j]<<" ";
            }
            o<<endl;
        }
        return o;
    }
    
    Matrix::Matrix()
    {
        dane=new CountingReference();
    }
    
    Matrix::Matrix(int wiersz, int col)
    {
        dane=new CountingReference(wiersz,col);
    }
    
    Matrix::Matrix(const Matrix& Object)
    {
        Object.dane->countingReference++;
        dane=Object.dane;
    }
    
    Matrix::~Matrix()
    {
        if(dane->countingReference==-1)
        {
            delete dane;
        }
    }
    
    int Matrix::readref()
    {
        return this->dane->countingReference;
    }
    
    Matrix& Matrix::operator=(const Matrix& Object)
    {
        Object.dane->countingReference++;
        if(dane->countingReference==0)
        {
            delete dane;
        }
        dane=Object.dane;
        return *this;
    }
    
    Matrix Matrix::operator+(const Matrix& Object) throw(string)
    {
        Matrix A(dane->wiersz,dane->kolumna);
        if(dane->wiersz!=Object.dane->wiersz || dane->kolumna!=Object.dane->kolumna)
        {
            string wyjatek = "Nie sa rowne.";
            throw wyjatek;
        }
        else{
            int i,j;
            for(i=0; i<dane->wiersz;i++)
            {
                for(j=0; j<dane->kolumna; j++)
                {  
                    A.dane->wsk[i][j]=dane->wsk[i][j]+Object.dane->wsk[i][j];
                }
            }
        }
        return A;
    }
    
    Matrix Matrix::operator-(const Matrix& Object) throw(string)
    {
        Matrix A(dane->wiersz,dane->kolumna);
        if(dane->wiersz!=Object.dane->wiersz || dane->kolumna!=Object.dane->kolumna)
        {
            string wyjatek = "Nie sa rowne.";
            throw wyjatek;  
        }
        else{
            for(int i=0;i<dane->wiersz;i++)
            {
                for(int j=0;j<dane->kolumna;j++)
                {
                    A.dane->wsk[i][j]=dane->wsk[i][j]-Object.dane->wsk[i][j];
                }
            }
        }
        return A;
    }
    
    Matrix Matrix::operator*(const Matrix& Object) throw(string)
    {
        double temp=0;
        Matrix A(dane->wiersz,dane->kolumna);
        if(dane->kolumna!=Object.dane->wiersz)
        {
            string wyjatek = "Nie sa rowne";
            throw wyjatek;
        }
        else{
            for(int i=0;i<dane->wiersz;i++)
            {
                for(int j=0;j<Object.dane->kolumna;j++)
                {
                    for(int k=0;k<dane->kolumna;k++)
                    {
                        temp+=dane->wsk[i][j]*Object.dane->wsk[j][k];
                    }
    
                    A.dane->wsk[i][j]=temp;
                }
            }
        }
    
        return A;
    }
    
    Matrix Matrix::operator+=(const Matrix& Object) throw(string)
    {
        if(dane->wiersz!=Object.dane->wiersz || dane->kolumna!=Object.dane->kolumna)
        {
            string wyjatek = "Nie sa rowne.";
            throw wyjatek;  
        }
        else{
            dane=dane->detach();
            for(int i=0;i<dane->wiersz;i++)
            {
                for(int j=0;j<dane->kolumna;j++)
                {
                    dane->wsk[i][j]+=Object.dane->wsk[i][j];
                }
            }
        }
    
        return *this;
    }
    
    Matrix& Matrix::operator-=(const Matrix& Object) throw(string)
    {
        if(dane->wiersz!=Object.dane->wiersz || dane->kolumna!=Object.dane->kolumna)
        {
            string wyjatek = "Nie sa rowne.";
            throw wyjatek;  
        }
        else{
            dane=dane->detach();
            for(int i=0;i<dane->wiersz;i++)
            {
                for(int j=0;j<dane->kolumna;j++)
                {
                    dane->wsk[i][j]-=Object.dane->wsk[i][j];
                }
            }
        }
        return *this;
    }
    
    Matrix& Matrix::operator*=(const Matrix& Object) throw(string)
    {
        if(dane->kolumna!=Object.dane->wiersz)
        {
            string wyjatek = "Nie sa rowne.";
            throw wyjatek;
        }
        else
        {
            int m=0,n=0;
            double temp=0;
            m=dane->wiersz;
            n=Object.dane->kolumna;
            Matrix A(m,n);
            for(int i=0;i<dane->wiersz;i++)
            {
                for(int j=0;j<Object.dane->kolumna;j++)
                {
                    for(int k=0;k<dane->kolumna;k++)
                    {
                        temp+=dane->wsk[i][k]*Object.dane->wsk[k][j];
                    }
    
                    A.dane->wsk[i][j] = temp;
                    temp=0;
                }
            }
    
            dane=dane->detach();
            for(int i=0;i<m;i++)
            {
                for(int j=0;j<n;j++)
                {
                    dane->wsk[i][j]=A.dane->wsk[i][j];
                }
            }       
        }
        return *this;
    }
    
    Matrix& Matrix::LoadFromFile(const char *string)
    {
        int m=0,n=0;
        ifstream inFile;
        inFile.open(string);
        if(inFile.good())
        {
            inFile>>m;
            inFile>>n;
            dane=dane->detach();
    
            for(int i=0;i<m;i++)
            {
                for(int j=0;j<n;j++)
                {
                    inFile>>dane->wsk[i][j];
                }
            }
            inFile.close();
        }
        return *this;
    }
    
    double Matrix::read( int i, int j) const
    {
        return dane->wsk[i-1][j-1];
    }
    
    void Matrix::write(int i, int j, const double x)
    {
        dane = dane->detach();
        dane->wsk[i-1][j-1] = x;    
    }
    
    bool Matrix::operator == (const Matrix& Object)
    {   
        int i,j;
        for(i=0;i<dane->wiersz;i++)
        {
            for(j=0;j<dane->kolumna;j++)
            {
                if(dane->wsk[i][j]!=Object.dane->wsk[i][j])
                {
                    return false;
                }   
            }
        }
        return true;
    }
    
    Matrix& Random(Matrix& Object)
    {
        for(int i=0;i<Object.dane->wiersz;i++)
        {
            for(int j=0;j<Object.dane->kolumna;j++)
            {
                Object.dane->wsk[i][j]=rand()%100+1;
            }
        }
    
        return Object;
    }
    
    Matrix::Mref Matrix::operator()(int i, int j)
    {
        return Mref(*this,i,j);
    }
    
    Matrix::CountingReference::CountingReference()
    {
        wiersz=0;
        kolumna=0;
        wsk=NULL;
        countingReference=0;
    }
    
    Matrix::CountingReference::CountingReference(int wier, int kol)
    {
        countingReference=0;    
        wiersz=wier;
        kolumna=kol;
        wsk=new double*[wier];
        for(int i=0;i<wier;i++)
        {
            wsk[i]=new double[kol];
        }
    }
    
    Matrix::CountingReference::~CountingReference()
    {
        for(int i=0;i<wiersz;i++)
        {
            delete [] wsk[i];
        }
    
        delete [] wsk;
        wsk=NULL;
    }
    
    Matrix::CountingReference *Matrix::CountingReference::detach()
    {
        CountingReference *pointer;
    
        if(countingReference==0)
        {
            return this;
        }
    
        pointer=new CountingReference(wiersz,kolumna);
    
        for(int i=0;i<wiersz;i++)
        {
            for(int j=0;j<kolumna;j++)
        {
                pointer->wsk[i][j]=wsk[i][j];
            }
        }
        countingReference--;
        return pointer;
    }
    
    Matrix::Mref::operator double() const
    {
        return s.read(i,k);       
    }
    
    Matrix::Mref &Matrix::Mref::operator = (double c)
    {
        s.write(i,k,c); 
        return *this;
    }
    
    Matrix::Mref &Matrix::Mref::operator = (const Mref& ref)
    {
        return operator= ((double)ref);   
    }
    

    主要的cpp公司

    #include <iostream>
    #include <cstdlib>
    #include <ctime>
    #include <fstream>
    #include "Matrix.h"
    
    using namespace std;
    
    int main()
    {
    
        Matrix A(3,3);
        Matrix B(3,3); 
        Matrix C(3,3);
        Matrix D(3,3);
        Matrix F();
    
        Random(B);
        Random(C);
        Random(D);
    
        cout<<"B: "<<endl<<B<<endl;
        cout<<"C: "<<endl<<C<<endl;
        cout<<"D: "<<endl<<D<<endl;
        cout<<A.readref()<<endl;
        A=B+C;
        cout<<A.readref()<<endl;
        A=B=C=D;
        cout<<A.readref()<<endl;
    return 0;
    }
    

    我知道,在我的析构函数中,我不会释放内存。但是我已经检查了所有的东西,仍然没有发现问题。。。我甚至不知道应该释放哪些指针。你能帮帮我吗?

    编辑

    这是我的矩阵。h类

     #include <iostream>
    #include <cassert>
    
    using namespace std;
    
    class Matrix{
        private:
            class CountingReference
            {
                friend class Matrix; 
                public:
                    double **wsk;
                    int wiersz;
                    int kolumna;
                    int countingReference;
                    CountingReference();
                    CountingReference(int, int);
                    ~CountingReference();
                    CountingReference* detach();
            };
            CountingReference *dane;
    
        public:
    
            class Mref
            {   
                friend class Matrix;
                Matrix& s;
                int i,k;
                Mref (Matrix& m, int r, int c): s(m), i(r), k(c) {}   
    
                public:
                    operator double() const;
                    Mref& operator = (double c);
                    Mref& operator = (const Mref& ref);
            };
    
            friend ostream& operator<<(ostream& o,const Matrix&);
            friend ostream& operator<<(const Matrix&, ostream& o);
            Matrix();
            Matrix(int, int);
            Matrix(const Matrix&);
            ~Matrix();
            Matrix& operator=(const Matrix&);
            Matrix operator+(const Matrix&) throw(string);
            Matrix operator-(const Matrix&) throw(string);
            Matrix operator*(const Matrix&) throw(string);
            Matrix operator+=(const Matrix&) throw(string);
            Matrix& operator-=(const Matrix&) throw(string);
            Matrix& operator*=(const Matrix&) throw(string);
            bool operator == (const Matrix &);
            Matrix& LoadFromFile(const char*);
            friend Matrix& Random(Matrix&);
            double read(int, int) const;
            int readref();  
            void write(int, int, const double);
            Mref operator()(int, int);
            friend class CountingReference;
    };
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   user4581301    7 年前

    问题的症结在于: Matrix 析构函数测试 dane->countingReference 但并没有减少它,所以 丹麦->计数参考 永远不要到达被摧毁的点。A. dane->countingReference--; 使用调试器进行演练,以逐个找出错误,可以解决这一问题,但是 矩阵 CountingReference 令人不安,使簿记变得比需要的困难得多。

    一个类应该尽可能少地承担责任,最好是一个。这通常使编写、维护和调试更容易。 矩阵 负责矩阵操作、引用计数和内存管理、降级 计数参考 仅仅是一个容器。

    如果 矩阵 ,坚持只做矩阵操作,只需编写和测试矩阵操作即可。

    如果 计数参考 被分拆为一个同等的班级,负责跟踪与引用计数和内存管理密切相关的职责, 矩阵 可以继续做真正的工作:做数学。让我们看看如何让 计数参考 . 首先,我们利用了C++的基本概念资源分配是初始化或RAII( What is meant by Resource Acquisition is Initialization (RAII)? )帮助管理我们的记忆。

    class CountingReference
    {
    private:
        int * countingReference;
    public:
        int wiersz;
        int kolumna;
        double **wsk;
        CountingReference();
        CountingReference(int, int);
        CountingReference(const CountingReference &);
        ~CountingReference();
        CountingReference& operator=(CountingReference rhs);
    };
    

    与原始版本类似,但请注意 countingReference 成员是一个指针以及复制和赋值操作符,用于遵守C++的另一个基本概念,即三的规则( What is The Rule of Three? ). 计数参考 遵守三条规则允许 矩阵 利用 The Rule of Zero . 现在,我们将忽略五的规则。

    没有必要 CountingReference* detach();

    注意,这不是线程安全的。如果您试图在没有 using <atomic> 如果您正在使用 <原子(&T); 你也可以 use std::shared_ptr 让整个问题消失。

    实施:

    CountingReference::CountingReference() :
            countingReference(new int(1)), wiersz(0), kolumna(0), wsk(NULL)
    
    {
    }
    
    CountingReference::CountingReference(int wier, int kol) :
            countingReference(new int(1)), wiersz(wier), kolumna(kol), wsk(new double*[wier])
    {
        for (int i = 0; i < wier; i++)
        {
            wsk[i] = new double[kol];
        }
        cout << countingReference << " was created \n";
    }
    

    除了使用 member initializer list , 计数参考 作为指针,以及 计数参考 从1开始,因为如果引用计数与实例数匹配并销毁0上的共享数据,则更直观。

    计数参考 是指针,因为 计数参考 共享数据必须具有相同的引用计数器。基本上不是 矩阵 实例共享指向 计数参考 ,他们现在都有自己的 计数参考 共享相同的 计数参考 . 这就是RAII发挥作用的地方,为我们节省了很多工作。

    还要注意一些调试语句,以便我知道我是否在给您处理错误代码。我可能仍然在提供糟糕的代码,但如果是这样,至少这个错误不是什么愚蠢的事情。

    CountingReference::CountingReference(const CountingReference & src) :
            countingReference(src.countingReference), wiersz(src.wiersz), kolumna(src.kolumna), wsk(src.wsk)
    
    {
        ++(*countingReference);
        cout << countingReference << " was copied (" << *countingReference << ")\n";
    }
    

    复制构造函数。存储相同的数组和 计数参考 作为源和增量 计数参考 跟踪参考文献的数量。三人法则的第一部分。

    CountingReference::~CountingReference()
    {
        --(*countingReference);
        cout << countingReference << " was decremented (" << *countingReference << ")\n";
        if (!*countingReference)
        {
            cout << countingReference << " was destroyed\n";
            for (int i = 0; i < wiersz; i++)
            {
                delete[] wsk[i];
            }
            delete[] wsk;
            delete countingReference;
        }
    }
    

    析构函数。如果有 计数参考 如果引用计数递减,则超出范围;如果计数现在为0,则释放数组并 计数参考 . 这会自动执行所有清理,因此不会泄漏内存。三人法则的第二部分。

    CountingReference & Matrix::CountingReference::operator=(CountingReference rhs)
    {
        cout << countingReference << " was replaced by " << rhs.countingReference << "\n";
        swap(countingReference, rhs.countingReference);
        swap(wsk, rhs.wsk);
        swap(wiersz, rhs.wiersz);
        swap(kolumna, rhs.kolumna);
        return *this;
    }
    

    赋值运算符。使用 Copy and Swap Idiom . 这不是执行任务的最有效方式,但它是万无一失的。三人法则的第三部分。

    矩阵 最后看起来像

    class Matrix
    {
    private:
        class CountingReference
        {
        private:
            int * countingReference;
        public:
            int wiersz;
            int kolumna;
            double **wsk;
            CountingReference();
            CountingReference(int, int);
            CountingReference(const CountingReference &);
            ~CountingReference();
            CountingReference& operator=(CountingReference rhs);
        };
        CountingReference dane;
    
    public:
    
        friend ostream& operator<<(ostream& o, const Matrix&);
        friend ostream& operator<<(const Matrix&, ostream& o);
        Matrix();
        Matrix(int, int);
        static Matrix clone(const Matrix &);
        Matrix operator+(const Matrix &) const;
        Matrix operator-(const Matrix &) const;
        Matrix operator*(const Matrix &) const;
        Matrix operator+=(const Matrix&);
        Matrix& operator-=(const Matrix&);
        Matrix& operator*=(const Matrix&);
        bool operator ==(const Matrix &);
        friend Matrix& Random(Matrix&);
    };
    

    不需要自定义析构函数、复制构造函数或赋值运算符,因为矩阵没有需要管理的资源(零规则),所以它们由编译器生成。

    我已放弃所有异常说明符( throw(string) )因为在我的经验中,它们造成了很多痛苦,却没有什么益处。但别只相信我, read what Herb Sutter has to say on the topic . 他通常知道自己在说什么。

    函数实现大大简化,因为它们不再需要担心 detach 并应用从sbi的优秀 What are the basic rules and idioms for operator overloading? Paul Mackenzie在上面的评论中描述了另一个提示,我们最终得到的函数如下所示:

    Matrix Matrix::operator+=(const Matrix& Object)
    {
        if (dane.wiersz != Object.dane.wiersz
                || dane.kolumna != Object.dane.kolumna)
        {
            string wyjatek = "Nie sa rowne.";
            throw wyjatek;
        }
        else
        {
            for (int i = 0; i < dane.wiersz; i++)
            {
                for (int j = 0; j < dane.kolumna; j++)
                {
                    dane.wsk[i][j] += Object.dane.wsk[i][j];
                }
            }
        }
    
        return *this;
    }
    
    Matrix Matrix::operator+(const Matrix &Object) const
    {
        return Matrix.clone(*this) +=Object;
    }
    

    我们稍微偏离了标准( return Matrix(*this) +=Object; )这里是因为 operator+ 不应修改源参数。 矩阵 的复制构造函数产生两个 矩阵 ,这将允许 += 更改原件和副本。这会很糟糕。没有人期望x=10+2将10变成12。

    clone 看起来像

    Matrix Matrix::clone(const Matrix &Object)
    {
        Matrix result(Object.dane.wiersz,
                      Object.dane.kolumna);
        for (int i = 0; i < result.dane.wiersz; i++)
        {
            for (int j = 0; j < result.dane.kolumna; j++)
            {
                result.dane.wsk[i][j] = Object.dane.wsk[i][j];
            }
        }
        return result;
    }
    

    Link to complete code and test case.

    我没有检查任何与正确管理引用计数数组无关的程序逻辑。所有矩阵操作逻辑都未经检查,可能是虚假的。

    不要将此用于生产代码。有一个非常好的 std::shared\u ptr 在标准库中。

        2
  •  1
  •   Daniel    7 年前

    根据Valgrind网站上的此页面: http://valgrind.org/docs/manual/faq.html#faq.deflost 通常,“间接丢失”是“完全丢失”错误的结果。

    似乎有些情况下构造函数没有删除类的所有字段 当dane->countingReference=-1. 在这种情况下,如何删除内部字段? valgrind似乎最不喜欢计数引用构造函数之后分配的数据发生的事情

    在操作符+:您创建一个新的矩阵对象并按值返回,这意味着您创建了其中的2个,并且在某些情况下(同样,由于术语:dane->countingReference===-1),您不会释放它们。