C ++中的析构函数和类[Memory Leak]

时间:2015-06-12 11:50:22

标签: c++ class memory-leaks valgrind destructor

我在释放内存方面遇到了问题。首先,我去展示我的构造函数,析构函数和私有部分:

第1课:

class VectorDinamico {

private:
    int util, tam;
    int *vect;

   /*Rest of the class*/
}

VectorDinamico::VectorDinamico() {
    util = 0;
    tam = 10;
    vect = new int[10];
}

VectorDinamico::VectorDinamico(const VectorDinamico &v){
    vect = new int[v.tam];

    for (int i = 0 ; i < v.util; i++) {
        vect[i] = v.vect[i];
    }
    util = v.util;
    tam = v.tam;
 }

VectorDinamico::~VectorDinamico() {
    delete []vect;
}

VectorDinamico& VectorDinamico::operator= (const VectorDinamico &v) {
    if (this != &v) {
        delete []vect;

        vect = new int[v.tam];

        for (int i = 0 ; i < v.util; i++) {
            vect[i] = v.vect[i];
        }
        util = v.util;
        tam = v.tam;
    }
    return *this;
}

工作正常(Valgrind:退出时没有使用)

第2课:

class Conjunto {
private:
    VectorDinamico datos;

    /*Rest of the class*/
}

//Is empty because it uses the VectorDinamico constructor( I think )
Conjunto::Conjunto() {}

Conjunto::Conjunto( const Conjunto &c) {
   datos = c.datos; // = Is overloaded in VectorDinamico
}

//Is empty because it uses the VectorDinamico destructor( I think )
Conjunto::~Conjunto() {};

Conjunto& Conjunto::operator=( const Conjunto &c) {
    if (this != &c)
        datos = c.datos;

    return *this;
}

工作正常(Valgrind:退出时没有使用)

第3课:(我认为这是问题所在)

class SetConjuntos{
private:
    Conjunto *conjuntos;
    int util, tam;

    /*Rest of the class*/
}

SetConjuntos::SetConjuntos(){
    util = 0;
    tam = 10;
    conjuntos = new Conjunto[10];
}

SetConjuntos::SetConjuntos(const SetConjuntos &sc){
    util = sc.util;
    tam = sc.tam;
    conjuntos = new Conjunto[tam];

    for(int i = 0 ; i < util ; i++)
    conjuntos[i]=sc.conjuntos[i];
}
//Here is my problem ( I think )
//anyway I've left it empty, because I think it uses the Conjunto destructor's
SetConjuntos::~SetConjuntos(){
    //delete []conjuntos; ( Cause segmetation fault )
}

我认为SetConjuntos析构函数不正确,因为Valgrind输出是:

==6838== LEAK SUMMARY:
==6838==    definitely lost: 2,912 bytes in 4 blocks
==6838==    indirectly lost: 11,320 bytes in 180 blocks

SetConjuntos的析构函数必须如何?

由于

-----------解决了添加operator = to SetConjuntos ----------------

我没有实现赋值运算符,因为他认为如果他没有明确使用则没有必要。我错了。

现在第3课是:

class SetConjuntos{
private:
    Conjunto *conjuntos;
    int util, tam;

    /*Rest of the class*/
}

SetConjuntos::SetConjuntos(){
    util = 0;
    tam = 10;
    conjuntos = new Conjunto[10];
}

SetConjuntos::SetConjuntos(const SetConjuntos &sc){
    util = sc.util;
    tam = sc.tam;
    conjuntos = new Conjunto[tam];

    for(int i = 0 ; i < util ; i++)
    conjuntos[i]=sc.conjuntos[i];
}

SetConjuntos::~SetConjuntos(){
    delete []conjuntos; //(  NO cause segmetation fault )
}

SetConjuntos& SetConjuntos::operator=(const SetConjuntos &sc){
    if(this != &sc){
        util = sc.util;
        tam = sc.tam;
        Conjunto *conjuntos_loc = new Conjunto[tam];

        for(int i = 0 ; i < util ; i++)
            conjuntos_loc[i]=sc.conjuntos[i];

        delete[] conjuntos;
        conjuntos = conjuntos_loc;
    }
    return *this;
}

4 个答案:

答案 0 :(得分:2)

显示的代码没有明显的错误,因此错误在于您未在问题中出现的内容。

作为一个疯狂的猜测,你(可能是不自觉地)使用复制构造或分配,这会产生所有权问题。

记住规则:要么你没有析构函数,赋值运算符或复制构造函数,要么你可能需要所有这三个。

这个规则非常重要,如果碰巧遇到一个你不需要复制构造函数但你需要一个析构函数(不能想到一个,但它可能存在)的情况,请用一个清楚的文档记录下来评论说这不是你忘记的事情。

答案 1 :(得分:1)

我只想向您指出,有更好的方法来编写复制赋值运算符。我们来看看您的代码:

VectorDinamico& VectorDinamico::operator= (const VectorDinamico &v) {
    if (this != &v) {
        delete []vect;
            /* What happens here is that you delete vect [ ], but what
               happens if the next line 'vect = next int [ v.tam ];' throws
               an exception?
            */
        vect = new int[v.tam];

        for (int i = 0 ; i < v.util; i++) {
            vect[i] = v.vect[i];
        }
        util = v.util;
        tam = v.tam;
    }
    return *this;
}

看看The rule of three,在这里您将获得如何编写复制赋值运算符的详细说明。

答案 2 :(得分:1)

你似乎在学习15年前的C ++。

  1. 使用std::vector<int>而不是自己编写VectorDinamico
    • 这已经有正确的副本,作业等。
  2. 如果您无法 1 ,请使用std::unique_ptr<int[]>代替原始int *vect。它可以节省你必须编写析构函数。您仍然需要编写副本和副本分配。

    • 编写复制赋值的规范正确方法是首先编写swap方法或重载,如下所示:

      void VectorDinamico::swap(VectorDinamico &other) {
          std::swap(util, other.util);
          std::swap(tam,  other.tam);
          std::swap(vect, other.vect);
      }
      

      然后编写复制赋值运算符,以便它只重用复制构造函数,swap和析构函数:

      VectorDinamico& VectorDinamico::operator=(VectorDinamico const &other) {
          VectorDinamico tmp(other);
          tmp.swap(*this);
      }
      
      除了更简单的代码和更少的重复之外,这个好处是,如果分配失败抛出bad_alloc,两个原始向量都会处于明确定义的状态(原始代码会使左边的一个被破坏) )

  3. 使用std::unique_ptr包装其他两个类的原始指针成员,并停止手动编写析构函数

  4. 如果你正在编写复制构造函数和复制赋值运算符,你应该考虑编写移动版本

  5. 如果您正在运行valgrind并且发现泄漏,它将告诉您该内存的分配位置。只需确保您的构建中有调试符号

  6. 如果您不确定是否正在调用析构函数,只需添加一个print语句,然后在调试器中查看或逐步执行。看到像I think this is called here这样的评论只是告诉我你没有尝试过发现。

  7. 最后是您的问题:

    //Here is my problem ( I think )
    //anyway I've left it empty, because I think it uses the Conjunto destructor's
    SetConjuntos::~SetConjuntos(){
        //delete []conjuntos; ( Cause segmetation fault )
    }
    

    您的conjuntos是一个原始指针 - 您已在其他地方手动删除它们,这里有什么不同?你应该取消对此删除的评论。

    如果您使用此未注释的分段错误,请在gdb下运行它或获取核心转储并查看。还要看看valgrind或glibc是否警告你关于双重释放。

    几乎可以肯定的是,您正在删除的阵列成员之一已损坏,找到该损坏的起因是您的真正问题。

答案 3 :(得分:0)

SetConjutos的析构函数很好,就像你拥有它一样:

SetConjuntos::~SetConjuntos(){
    delete [] conjuntos;
}

但是如果你有指针成员(在VectorDinamico和SetConjutos中)你需要定义一个复制构造函数和赋值运算符:

VectorDinamico::VectorDinamico(const VectorDinamico &v) :
    util(v.util),
    tam(v.tam),
    vect(new int[10])
{
    //copy Content of v.vect to vect
}

VectorDinamico& Operator=(const VectorDinamico &v)
{
    //Copy util, tam and the CONTENT of vec
    return *this;
}

注意:您还需要为SetConjuntos定义复制构造函数和赋值运算符。

如果您没有复制构造函数和赋值运算符,则可能是您尝试多次释放内存。

正如您在我的复制构造函数中所看到的,我使用的是初始化列表,我也建议使用它。