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

Directly lost or indirectly lost - Valgrind issue

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

基于 Valgrind 站点中的此页面: http://valgrind.org/docs/manual/faq.html#faq.deflost 通常 "indirectly lost " 是 "definitly lost" 错误的结果。

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

并在 operator+ 处:您创建了一个新的 Matrix 对象并 return 它的值,这意味着您创建了其中的 2 个并且在某些情况下(同样,由于术语 :dane->countingReference== -1 )你不释放他们。

问题的关键:Matrix 析构函数测试 dane->countingReference 但不递减它,因此 dane->countingReference 永远不会到达被销毁的点。 dane->countingReference--; 并使用调试器检查任何差一错误将解决此问题,但 MatrixCountingReference 之间的耦合令人不安,并且使簿记很多比需要的更困难。

A class 的职责应该尽可能少,最好是一个。这通常会使编写、维护和调试变得更容易。 Matrix 负责矩阵操作、引用计数和内存管理,将 CountingReference 归为一个容器。

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

如果 CountingReference 被拆分为一个同等的 class 负责跟踪引用计数和内存管理的紧密相关职责,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 observing The Rule of Three allows Matrix to take advantage of The Rule of Zero。现在我们将忽略五规则。

不需要CountingReference* detach();

注意这不是线程安全的。如果您尝试在没有 using <atomic> and if you are using <atomic> you might as well 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 具有相同的引用计数数组,这将允许 += 更改原始副本。这将是糟糕的。没有人期望 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