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

时间:2018-01-10 22:49:38

标签: c++ oop memory-leaks valgrind

我的Matrix程序出了问题。

有我的错误:

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)

我的代码在这里:

它的Matrix.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);   
}

的main.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;
}

我知道,在我的析构者的某个地方,我不会释放记忆。但是我已经检查了所有内容,但仍然没有找到问题...我甚至不知道我应该释放哪些指针。你能帮帮我吗?

修改

这是我的Matrix.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 个答案:

答案 0 :(得分:1)

问题的关键:Matrix析构函数测试dane->countingReference但不减少它,因此dane->countingReference永远不会到达被破坏的点。一个dane->countingReference--;和一个带调试器的步骤来挑选任何一个一个错误将解决这个问题,但是MatrixCountingReference之间的耦合令人不安并且使得这本书保持了很多比实际需要的更难。

一个班级应尽可能少负责,最好是一个。这通常使编写,维护和调试更容易。 Matrix负责矩阵操作和引用计数以及内存管理,将CountingReference降级为仅仅容器。

如果Matrix坚持只做矩阵操作,那么你需要编写和测试的就是矩阵操作。

如果将CountingReference分解为负责跟踪引用计数和内存管理的紧密相关责任的同等级别,Matrix可以继续其实际工作:做数学。让我们来看看我们如何做出更聪明的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?)。 countingReference观察“三规则”允许Matrix利用The Rule of Zero。现在我们将忽略五法则。

不需要CountingReference* detach();

请注意,这不是线程安全的。如果你试图在没有using <atomic>的情况下编写这个帖子,那么使这个帖子安全是非常非常讨厌的,如果你使用<atomic>,你可能会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 listcountingReference是指针之外,这些构造函数与原始文件类似,而countingReference从1开始,因为如果引用计数更直观,它们更直观匹配实例数并销毁0上的共享数据。

countingReference是一个指针,因为共享数据的CountingReference的所有实例必须具有相同的引用计数器。基本上,不是Matrix实例共享指向CountingReference的指针,它们现在都有自己的CountingReference共享相同的countingReference。这是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以跟踪引用的数量。 “三条规则”的第1部分。

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;
    }
}

析构。当任何CountingReference超出范围时,如果递减引用计数并且计数现在为0,则释放数组并countingReference。这样可以自动完成所有清理工作,从而避免泄漏内存。 “三规则”第2部分。

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。这不是执行任务的最有效方式,但它是万无一失的。 “三规则”第3部分。

Matrix看起来像

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&);
};

不需要自定义析构函数,复制构造函数或赋值运算符,因为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+不应修改源参数。 Matrix的复制构造函数导致两个Matrix s具有相同的引用计数数组,这将允许+=更改原始文件和副本。这将是坏事。没有人希望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_ptr

答案 1 :(得分:0)

基于Valgrind网站的这个页面: http://valgrind.org/docs/manual/faq.html#faq.deflost 通常“间接丢失”是由于“明确丢失”的错误。

似乎有些情况下您的构造函数不会删除您类的所有字段 当dane-&gt; countingReference!= - 1时会发生什么? 在这种情况下如何删除内部字段? valgrind似乎不喜欢在Counting引用构造函数

之后分配的数据

并在operator +:您创建一个新的Matrix对象并按值返回,这意味着您创建了其中的两个,在某些情况下(再次,因为术语:dane-&gt; countingReference == - 1)不要释放他们。