【问题标题】:Directly lost or indirectly lost - Valgrind issue直接丢失或间接丢失 - Valgrind 问题
【发布时间】:2018-01-10 22:49:37
【问题描述】:

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

【问题讨论】:

  • 看起来你只是想重塑std::shared_ptr。为什么不直接使用shared_ptr?如果这不可行,请从shared_ptr 开始,确保所有其他逻辑都正确,然后将shared_ptr 替换为您的自制啤酒。无需一次性调试。
  • 你也可以从shared_ptr 偷一点。将所有销毁逻辑构建到CountingReference 中,以便所有Matrix 具有超出范围的CountingReference 并且deletes 包含内存或不取决于引用计数。现在你正在强迫Matrix根据另一个对象的数据做出关于另一个对象的决定,这闻起来很糟糕。
  • 要完成问题并获得可构建的代码,您能否添加 Matrix.h 或其提供MatrixCountingReference 的可编译定义的子集?
  • 我添加了我的 Matrix.h 文件。
  • 感谢标题。我在代码中只看到一个countingReference--;,在CountingReference::detach。我希望Matrix 析构函数中也有一个。可能这就是问题所在。当包含对象被销毁时,不会减少引用计数器。

标签: c++ oop memory-leaks valgrind


【解决方案1】:

问题的症结:Matrix 析构函数测试dane-&gt;countingReference 但不会递减它,所以dane-&gt;countingReference 永远不会到达被销毁的点。一个dane-&gt;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 &lt;atomic&gt; 的情况下编写此线程,并且如果您使用&lt;atomic&gt;,则最好使用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 在上面的 cmets 中描述的另一个提示,我们最终得到的函数看起来像:

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 的复制构造函数导致两个 Matrixs 具有相同的引用计数数组,这将允许导致 += 与副本一起更改原始文件。这会很糟糕。没有人期望 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

【讨论】:

  • 谢谢!你的帖子很有帮助。 :)
【解决方案2】:

基于 Valgrind 网站中的此页面: http://valgrind.org/docs/manual/faq.html#faq.deflost 通常“间接丢失”是由于“肯定丢失”错误造成的。

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

并且在 operator+ 处:您创建一个新的 Matrix 对象并按值返回它,这意味着您创建了其中 2 个,并且在某些情况下(再次,因为术语 :dane->countingReference==-1 )您没有释放他们。

【讨论】:

  • 这个词我做了,因为我的讲师说我必须这样做:如果没有引用或引用自身,则计数器为 0,所以 0 表示“对象存在”所以我决定如果这个计数器是 -1 - 这意味着它必须删除对象。
  • 如果我错了请纠正我,你说的是计数器!=-1 表示有一个对象,-1 表示没有。因此,如果没有指向它的对象,则仅删除它,而是通过应该指向它的另一个对象将其删除?我看到你只在 deattach 中减少了计数器,你可能也需要在析构函数中这样做
  • 我已经在Destructor中添加了一行,但是还是不够。
猜你喜欢
  • 1970-01-01
  • 2012-05-01
  • 1970-01-01
  • 2016-08-28
  • 1970-01-01
  • 2017-12-06
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
相关资源
最近更新 更多