Не могу найти утечку памяти

Я провел последнюю неделю, пытаясь выяснить эту утечку памяти, и я в отчаянии на данный момент. Буду рад любой помощи.

У меня есть класс Solver, который создает экземпляр класса PartialGraph на каждой итерации в методе решения (выполняя поиск в глубину). На каждой итерации PartialGraph следует копировать в стек и уничтожать.

Решатель.h

class Solver {
public:
Solver(Graph pg);
PartialGraph solve(PartialGraph p, int bestest);
Graph pg;
stack<PartialGraph>  stackk;
bool isSpanningTree(PartialGraph* p);
 Solver(const Solver& orig);
 ~Solver();

Solver.cpp

Solver:: Solver(const Solver& orig){
     this->pg=*new Graph(orig.pg);   
     }

Solver::Solver(Graph gpg) {
    this->pg=gpg;
    }

PartialGraph Solver::solve(PartialGraph init, int bestest){

    int best=bestest;
    int iterace=0;
    PartialGraph bestGraph;
    stackk.push(init);

    while(stackk.size()!=0) {

        PartialGraph m = stackk.top(); 
        stackk.pop();


          for(int i=m.rightestEdge+1;i<pg.edgeNumber;i++){

              *******(line 53 )PartialGraph* pnew= m.addEdge(pg.edges[i]);  


              if(m.generatedNodes==pnew->generatedNodes){
              pnew->~PartialGraph();
              continue;  }

              if(isSpanningTree(pnew)){     
              if(best>pnew->maxDegree){
              best=pnew->maxDegree;
              bestGraph=*pnew;
              }
              if(pnew->maxDegree==2){
              pnew->~PartialGraph();
              return bestGraph;
              }
             pnew->~PartialGraph();
             continue;
             }

             if(pnew->maxDegree==best){
             pnew->~PartialGraph();
             continue;   }

             stackk.push(*pnew);

             *******(line 101 )pnew->~PartialGraph();
           }

    }

return bestGraph;
}



bool Solver::isSpanningTree(PartialGraph* p){
   if(p->addedEdges!=this->pg.nodeNumber-1){return false;}
   return  p->generatedNodes==this->pg.nodeNumber;
}

 Solver::~Solver(){
 this->pg.~Graph();
 };

PartialGraph выглядит так, у него два массива, оба удалены в деструкторе. Каждый конструктор и оператор = выделяет новую память для массивов. (Класс Edge содержит три целых числа)

PartialGraph::PartialGraph(int nodeNumber,int edgeNumber) { 
    nodeCount=nodeNumber;
    edgeCount=0;
    nodes=new int[nodeCount];
    edges=new Edge[0]; 
    rightestEdge=-1;
    generatedNodes=0;
    addedEdges=0;
    for(int i=0;i<nodeCount;i++){
      this->nodes[i]=0;
     }

     maxDegree=0;
}

PartialGraph::PartialGraph(const PartialGraph& orig){
    this->nodes=new int[orig.nodeCount];
    edges=new Edge[orig.edgeCount];
    this->nodeCount=orig.nodeCount;
    this->rightestEdge=orig.rightestEdge;
    this->edgeCount=orig.edgeCount;
    this->maxDegree=orig.maxDegree;
    this->addedEdges=orig.addedEdges;
    this->generatedNodes=orig.generatedNodes;

    for(int i=0;i<this->nodeCount;i++){  
    this->nodes[i]=orig.nodes[i];            
    }

   for(int i=0;i<this->edgeCount;i++){ 
    this->edges[i]=orig.edges[i];        
    }
}

PartialGraph::PartialGraph(){
}

PartialGraph::PartialGraph(const PartialGraph& orig, int i){

    this->nodes=new int[orig.nodeCount];
    edges=new Edge[orig.edgeCount+1];
    this->nodeCount=orig.nodeCount;
    this->rightestEdge=orig.rightestEdge;
    this->edgeCount=orig.edgeCount;
    this->maxDegree=orig.maxDegree;
    this->addedEdges=orig.addedEdges;
    this->generatedNodes=orig.generatedNodes;

    for(int i=0;i<this->nodeCount;i++){ 
    this->nodes[i]=orig.nodes[i];
    }

   for(int i=0;i<this->edgeCount;i++){
  this->edges[i]=orig.edges[i];        
  }
}


PartialGraph &PartialGraph::operator =(const PartialGraph &orig){
nodes=new int[orig.nodeCount];
edges=new Edge[orig.edgeCount];
this->nodeCount=orig.nodeCount;
this->rightestEdge=orig.rightestEdge;
this->edgeCount=orig.edgeCount;
this->maxDegree=orig.maxDegree;
this->addedEdges=orig.addedEdges;
this->generatedNodes=orig.generatedNodes;
for(int i=0;i<this->nodeCount;i++){  
this->nodes[i]=orig.nodes[i];

}
for(int i=0;i<this->edgeCount;i++){

    this->edges[i]=orig.edges[i];        
}


 }

PartialGraph* PartialGraph::addEdge(Edge e){
PartialGraph* npg=new PartialGraph(*this, 1);
  npg->edges[this->edgeCount]=e;
npg->addedEdges++;
npg->edgeCount++;
if(e.edgeNumber>npg->rightestEdge){npg->rightestEdge=e.edgeNumber;}
npg->nodes[e.node1]=npg->nodes[e.node1]+1;
npg->nodes[e.node2]=npg->nodes[e.node2]+1;

if(npg->nodes[e.node1]>npg->maxDegree){npg->maxDegree=npg->nodes[e.node1];}
 if(npg->nodes[e.node2]>npg->maxDegree){npg->maxDegree=npg->nodes[e.node2];}

 if(npg->nodes[e.node1]==1){npg->generatedNodes++;}
 if(npg->nodes[e.node2]==1){npg->generatedNodes++;}
return npg;
}





PartialGraph:: ~PartialGraph() //destructor
{

    delete [] nodes;
    delete [] edges;
};

PartialGraph.h

class PartialGraph {
public:
PartialGraph(int nodeCount,int edgeCount);
PartialGraph* addEdge(Edge e);
PartialGraph(const PartialGraph& orig);
PartialGraph();
~PartialGraph();
static int counter;
PartialGraph(const PartialGraph& orig, int i);
void toString();
int nodeCount;
int edgeCount;
int generatedNodes;
int *nodes;
Edge *edges;

int maxDegree;
int rightestEdge;
int addedEdges;
PartialGraph &operator =(const PartialGraph &other); // Assn. operator
};

Он работает нормально, но когда входные данные слишком велики, я получаю плохое распределение. Valgrind говорит, что у меня есть утечка в строке 53 PartialGraph.cpp, но я почти уверен, что все экземпляры уничтожены в строке 101 или ранее в итерации.

 (244,944 direct, 116 indirect) bytes in 5,103 blocks are definitely lost in         
   at 0x4C2AA37: operator new(unsigned long) 
  (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
  by 0x4039F6: PartialGraph::addEdge(Edge) (PartialGraph.cpp:107)
  by 0x404197: Solver::solve(PartialGraph, int) (Solver.cpp:53)
  by 0x4016BA: main (main.cpp:35)

  LEAK SUMMARY:
  definitely lost: 246,305 bytes in 5,136 blocks
   indirectly lost: 1,364 bytes in 12 blocks

Я даже сделал счетчик экземпляров, и казалось, что я уничтожаю все экземпляры. Как я уже сказал, я действительно в отчаянии, и помощь будет приветствоваться


person user1792161    schedule 01.11.2012    source источник
comment
Этот код ужасно трудно читать. Вы должны переформатировать его, чтобы получить лучшие результаты отсюда.   -  person dmarra    schedule 01.11.2012
comment
надеюсь это лучше   -  person user1792161    schedule 01.11.2012


Ответы (1)


Короткий ответ: вы никогда не должны вызывать деструктор напрямую. Вместо этого используйте delete, поэтому везде, где у вас есть pnew->~PartialGraph();, у вас должно быть delete pnew;. В общем, каждому new где-то должен соответствовать delete. Только будьте осторожны, в этом правиле есть некоторая хитрость, потому что несколько удалений могут сопоставляться с одним новым, и наоборот.

Бонусные утечки, которые я обнаружил при просмотре кода:

  • Первая строка исполняемого кода в вашем посте: this->pg=*new Graph(orig.pg);. Еще одно общее правило: если у вас есть код, который выполняет *new Foo(...), вы, вероятно, создаете утечку (и, возможно, создаете ненужную работу для себя). В этом случае this->pg = orig.pg должно работать нормально. Ваш текущий код копирует orig.pg во вновь выделенный объект, а затем копирует вновь созданный объект в this->pg, что приводит к двум операциям копирования. this->pg = orig.pg — это всего лишь одна копия.
  • Первая пара строк PartialGraph::operator=(). Конструкторы копирования, операторы присваивания и деструкторы могут быть трудны для правильного понимания. Во всех ваших конструкторах вы создаете новые узлы и ребра, и у вас есть соответствующие удаления в деструкторе, так что все в порядке. Но когда вы выполняете оператор присваивания, вы перезаписываете указатели на существующие массивы, но не удаляете их. Перед созданием новых необходимо удалить существующие массивы.

Наконец, эй. Я знаю, что форматирование кода для StackOverflow может быть болезненным, но попытка прочитать код в Solver::solve() более чем болезненна, потому что отступы не соответствуют структуре кода. Когда я просмотрел этот пост, было 23 просмотра и ни одного ответа. Это 23 человека, которые были заинтересованы в решении вашей проблемы, но, вероятно, форматирование их оттолкнуло. Если бы вы потратили дополнительные 23 минуты на форматирование кода, и это сэкономило бы каждому из этих людей более одной минуты, вы бы сэкономили вселенную некоторое время (помимо того, что, вероятно, быстрее получили ответ).

person Tom Panning    schedule 02.11.2012
comment
Это было! Это работает, спасибо. Я правильно понимаю, что вызов деструктора напрямую приводит к тому, что переменные-члены не освобождаются из кучи? - person user1792161; 06.11.2012
comment
Вызов деструктора позволяет объекту выполнять любую внутреннюю очистку, которая ему необходима, но не освобождает сам объект. - person Tom Panning; 06.11.2012