I am learning to use Valgrind to detect errors in my program. I have encountered a memory leak error. This is the code:
Class node
#ifndef NODO_H_INCLUDED
#define NODO_H_INCLUDED
#include <iostream>
template <typename DATOA, typename DATON>
struct arista;//declaración previa
template <typename DATON, typename DATOA>
struct nodo
{
DATON datonodo;
unsigned short int nPadres;
nodo<DATON,DATOA>* siguiente;
arista<DATOA,DATON>* adyacente;
//constructor
nodo (DATON N=0, unsigned short int numPadres=0, nodo<DATON,DATOA>* s=nullptr, arista<DATOA, DATON>* a=nullptr):datonodo(N),nPadres(numPadres),siguiente(s),adyacente(a) {}
//constructor copia
nodo (const nodo<DATON,DATOA>& n)
{
std::cout<<"nodo Constructor copia"<<std::endl;
datonodo=n.datonodo;
nPadres=n.nPadres;
siguiente=nullptr;
adyacente=nullptr;
}
//destructor
~nodo()
{
siguiente=nullptr;
adyacente=nullptr;
//borrar pila?
//borrar datonodo?
}
//operador de asignacion
nodo& operator = (const nodo<DATON, DATOA>& n)
{
if (this!=&n)
{
datonodo=n.datonodo;
nPadres=n.nPadres;
siguiente=nullptr;
adyacente=nullptr;
}
return *this;
}
};
#endif // NODO_H_INCLUDED
Class edge
#ifndef ARISTA_H_INCLUDED
#define ARISTA_H_INCLUDED
template <typename, typename>
struct nodo;
template <typename DATOA, typename DATON>
struct arista
{
DATOA datoarista;
nodo<DATON,DATOA>* destino;
nodo<DATON,DATOA>* origen;
arista<DATOA,DATON>* siguiente;
arista<DATOA,DATON>* anterior;
//constructores
arista(DATOA A=0, nodo<DATON,DATOA>* d=nullptr, nodo<DATON,DATOA>* o=nullptr,
arista<DATOA,DATON>* s=nullptr, arista<DATOA,DATON>* an=nullptr):datoarista(A),destino(d),origen(o),
siguiente(s), anterior(an) {}
arista (const arista<DATOA,DATON>& a)
{
datoarista=a.datoarista;
destino=nullptr;
origen=nullptr;
siguiente=nullptr;
anterior=nullptr;
}
//operador de asignacion
arista& operator = (const arista<DATOA, DATON>& a)
{
if (this!=&a)
{
datoarista=a.datoarista;
destino=nullptr;
origen=nullptr;
siguiente=nullptr;
anterior=nullptr;
}
return *this;
}
~arista()
{
//std::cout<<"Destructor arista"<<std::endl;
}
};
#endif // ARISTA_H_INCLUDED
Class graph
#ifndef GRAFO_H
#define GRAFO_H
#include <iostream>
#include <stack>
#include <list>
#include "arista.h"
#include "nodo.h"
template <typename DATON, typename DATOA>
class Grafo
{
typedef DATON datonodo_t;
typedef DATOA datoarista_t;
typedef nodo<datonodo_t,datoarista_t>* pNodo;
typedef arista<datoarista_t,datonodo_t>* pArista;
typedef nodo<datonodo_t,datoarista_t> t_nodo;
typedef arista<datoarista_t,datonodo_t> t_arista;
public:
//constructor
Grafo():Raiz(nullptr),nNodos(0)
{
std::cout<<"Constructor grafo"<<std::endl;
}
Grafo (pNodo nodo)
{
//Raiz = new t_nodo(*nodo);
Raiz = nodo;
nNodos=1;
}
Grafo (datonodo_t nodo)
{
Raiz = new t_nodo(nodo);
}
//constructor copia
Grafo (const Grafo& G);
//funciones
/************auxiliares de nodos y aristas***************/
void anadirNodo (pNodo& n);
void eliminarNodo(pNodo& n);
//void anadirArista (pNodo& NodoOrigen, pNodo& NodoDestino, pArista& precedente, pArista& NuevaArista);
void eliminarArista (pArista& A);
pArista hallarArista (pNodo& nodopadre, pNodo& nodohijo);
/*********insertar,eliminar,copiar elementos del grafo*******************/
void Insertar (pNodo& padre, pNodo& hijo, pArista& NuevaArista, int posicion=-1);
void borrarNodos(pArista& A);
void Copiar(pNodo& padre, pNodo& hijo, pArista NuevaArista, pArista precedente=nullptr);
pNodo CrearGrafoAPartirDeNodo(const pNodo& nodo);
/******funciones para recorrer el grafo*************************/
std::list<pNodo> recorrerNodos() const;
std::list<pNodo>& recorrerGrafo(const pNodo& inicio);
std::list<pNodo>& lista_recorrerGrafo(const pNodo& inicio);
/**********comprobaciones necesarias****************/
bool esReferenciaCircular(pNodo& padre, pNodo& hijo);
bool existeHermano (pNodo& padre, pNodo& hijo);
bool existeNodo (const pNodo& n);
/***********otras*******************************/
void guardaAristas (const pNodo& n);
void guardaAristasParaCopia (const pNodo& n);
pNodo posicionarseEnNodo(const datonodo_t& dato);
template <typename T>
void VaciarPila(std::stack<T>& pila);
/***********consultoras*************************/
const pNodo& LeeRaiz() const
{
return Raiz;
}
~Grafo();
private:
pNodo Raiz;
int nNodos;
std::stack<pArista> pilaAristas;
std::stack<pArista> pilaAristasParaCopia;
std::stack<pNodo> pilaNodos;
std::list<pNodo>nodos;
std::stack<std::pair<pArista,int> >aristasConNiveles;
std::list<std::pair<pNodo,int> >nodosConNiveles;
//funciones privadas porque son de uso interno
void InsertarHijo(pNodo& padre, pNodo& hijo, pArista& NuevaArista, pArista& precedente=nullptr);
void InsertarHijo(pNodo& padre, pNodo& hijo, datoarista_t valorArista, pArista precedente=nullptr);
};
//********************//
//destructor del grafo//
//********************//
template <typename datonodo_t, typename datoarista_t>
Grafo<datonodo_t,datoarista_t>::~Grafo()
{
std::cout<<"Iniciamos el destructor"<<std::endl;
//borro la pila de aristas antes de usarla.
VaciarPila(pilaAristas);
pArista A;
//meto todas las arista del nodo Raiz en la pila
guardaAristas(Raiz);
//empiezo a borrar todas las ramas que hay en la pila
while (!pilaAristas.empty())
{
A=pilaAristas.top();
pilaAristas.pop();
borrarNodos(A);
}
delete Raiz;
std::cout<<"Borrada la raiz"<<std::endl;
}
//*****************//
//constructor copia//
//*****************//
template <typename datonodo_t, typename datoarista_t>
Grafo<datonodo_t, datoarista_t>::Grafo (const Grafo<datonodo_t, datoarista_t>& G)
{
std::cout<<"CONSTRUCTOR COPIA"<<std::endl;
Raiz = CrearGrafoAPartirDeNodo(G.Raiz);
std::cout<<"Copia terminada"<<std::endl;
}
//**************//
//añadir un nodo//
//**************//
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::anadirNodo(pNodo& n)
{
if (Raiz==nullptr)
{
Raiz=n;
}
else
{
pNodo indice=Raiz;
//me posiciono al final de la lista
while (indice && indice->siguiente!=0)
{
indice=indice->siguiente;
}
indice->siguiente=n;
}
nNodos++;
}
//****************//
//eliminar un nodo//
//****************//
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::eliminarNodo(pNodo& n)
{
pNodo anterior=Raiz;
//avanzo anterior hasta el nodo anterior al que quiero borrar
while (anterior->siguiente!=n)
{
anterior=anterior->siguiente;
}
if (anterior==Raiz)//primer elemento
{
Raiz->siguiente=n->siguiente;
}
else
{
anterior->siguiente=n->siguiente;
}
delete n;
nNodos--;
}
//*******************//
//eliminar una arista//
//*******************//
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::eliminarArista(pArista& A)
{
pArista aux = A;
pNodo indice=Raiz;
//veo si la arista a borrar es la adyacente de algún nodo (primera arista)
while (indice && indice->adyacente!=A)
{
indice=indice->siguiente;
}
if (indice)//si finalmente la arista a borrar es adyacente de indice
{
//si es arista única
if (!A->anterior && !A->siguiente)
{
//std::cout<<"Es arista unica!!!"<<std::endl;
}
indice->adyacente = aux->siguiente;
}
if (aux->anterior)
{
aux->anterior->siguiente = aux->siguiente;
}
if (aux->siguiente)
{
aux->siguiente->anterior=aux->anterior;
}
delete aux;
}
//***********************************************//
//borrar todos los nodos que penden de una arista//
//***********************************************//
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::borrarNodos(pArista& A)
{
pNodo hijo=A->destino;
//quitar un padre al nodo hijo
if (hijo->nPadres>0) hijo->nPadres--;
//si todavía es hijo de algún padre....
if (hijo->nPadres)
{
//...me limito a borrar la arista que une al padre con el hijo
eliminarArista(A);
}
else //si se queda huerfanito
{
//si el hijo es hoja no tiene aristas que salgan de él, luego adyacente apunta a 0
if (!hijo->adyacente)
{
//me posiciono en la arista a borrar
eliminarArista(A);
eliminarNodo(hijo);//lo saco de la lista general de nodos
}
else
{
guardaAristas(hijo);
eliminarArista(A);
eliminarNodo(hijo);
while (!pilaAristas.empty())
{
A=pilaAristas.top();
pilaAristas.pop();
borrarNodos(A);
}
}
}
}
/***********************/
/****Insertar nodo *****/
/***********************/
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::Insertar(pNodo& padre, pNodo& hijo, pArista& NuevaArista, int posicion)
{
if (Raiz==nullptr)
{
Raiz = padre;
}
if (!existeNodo(padre))
{
anadirNodo(padre);
}
if (!existeNodo(hijo))
{
anadirNodo(hijo);
}
//inserto la arista
if (!esReferenciaCircular(padre,hijo) && !existeHermano(padre,hijo))//comprobacion necesaria
{
hijo->nPadres++;//añado un padre
NuevaArista->origen=padre;
NuevaArista->destino=hijo;
//caso 1. Primer nodo que cuelga del nodo padre
if (!padre->adyacente)
{
padre->adyacente = NuevaArista;
}
else
{
//caso 2. Poner el nuevo nodo en primer lugar habiendo otros nodos existentes
if (posicion==0)
{
NuevaArista->siguiente=padre->adyacente;
padre->adyacente->anterior=NuevaArista;
padre->adyacente=NuevaArista;
}
else//casos 3 y 4. El primer paso comun es situar la arista precedente a la que quiero insertar
{
pArista precedente = padre->adyacente;
//caso 3. Si no se especifica posicion esta vale -1 y se inserta al final
if (posicion<0)
{
while(precedente && precedente->siguiente)
{
//std::cout<<"INSERTO AL FINALe"<<std::endl;
precedente=precedente->siguiente;
}
}
//caso 4. Me situo en la posicion dada
else
{
//std::cout<<"INSERTO POR POSICION"<<std::endl;
for (int i=0; i<posicion-1; i++)
{
precedente = precedente->siguiente;
}
}
//por ultimo ligo la nueva arista
if (precedente && precedente->siguiente)
{
precedente->siguiente->anterior=NuevaArista;
}
NuevaArista->siguiente=precedente->siguiente;
NuevaArista->anterior=precedente;
precedente->siguiente=NuevaArista;
}
}
}
}
//**************************//
//insertar version con dato //
//**************************//
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::InsertarHijo(pNodo& padre, pNodo& hijo, datoarista_t valorArista, pArista precedente)
{
pArista nueva = new t_arista(valorArista);
InsertarHijo(padre, hijo, nueva, precedente);
}
//*******************************************//
//hallar la arista precedente a un nodo dado //
//*******************************************//
template <typename datonodo_t, typename datoarista_t>
arista<datoarista_t,datonodo_t>* Grafo<datonodo_t,datoarista_t>::hallarArista (pNodo& nodopadre, pNodo& nodohijo)
{
pArista precedente = nullptr;
if (nodopadre->adyacente)
{
precedente= nodopadre->adyacente;
while(precedente->siguiente)
{
if (precedente->destino==nodohijo)
{
//std::cout<<"Retorno precedente"<<std::endl;
return precedente;
}
else
{
precedente=precedente->siguiente;
}
}
}
std::cout<<"No retorno nada"<<std::endl;
return precedente;
}
//**********************************************************************//
//copia un grafo a partir de un nodo dado en una parte del grafo actual //
//**********************************************************************//
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::Copiar(pNodo& padre, pNodo& hijo, pArista NuevaArista, pArista precedente)
{
std::cout<<"FUNCION COPIAR "<<padre->datonodo<<"-->"<<hijo->datonodo<<std::endl;
pArista copianuevaarista = nullptr;
if (NuevaArista)
{
copianuevaarista = new t_arista(*NuevaArista);
}
else
{
copianuevaarista = new t_arista();
}
pNodo copiahijo = posicionarseEnNodo(hijo->datonodo);
if (!copiahijo)
{
copiahijo = new t_nodo(*hijo);
copiahijo->nPadres--;
}
guardaAristasParaCopia(hijo);
pArista A = padre->adyacente;
int pos=0;
while (A && A!=precedente)
{
pos++;
A=A->siguiente;
}
Insertar(padre,copiahijo,copianuevaarista,pos);
while (!pilaAristasParaCopia.empty())
{
pArista actual = pilaAristasParaCopia.top();
pNodo nieto = actual->destino;
pilaAristasParaCopia.pop();
std::cout<<"Copia hijo adyacente: "<<copiahijo->adyacente<<std::endl;
pArista prec = hallarArista(copiahijo,nieto);
pNodo repadre = posicionarseEnNodo(actual->origen->datonodo);
if (repadre)
{
Copiar(repadre, nieto, actual,prec);
}
}
}
//********************************************//
//Crea un grafo a partir de un nodo dado //
//********************************************//
template <typename datonodo_t, typename datoarista_t>
nodo<datonodo_t,datoarista_t>* Grafo<datonodo_t,datoarista_t>::CrearGrafoAPartirDeNodo(const pNodo& nodo)
{
pNodo raiz = nullptr;
if (nodo)
{
raiz = new t_nodo(*nodo);
}
Raiz=raiz;//tengo que definir ya la raiz aqui aunque no tenga mucho sentido porque la necesito definida para otras funciones
guardaAristasParaCopia(nodo);
while (!pilaAristasParaCopia.empty())
{
pArista AristaHijo = pilaAristasParaCopia.top();
pilaAristasParaCopia.pop();
Copiar(raiz,AristaHijo->destino,AristaHijo,nodo->adyacente->anterior);
}
return raiz;
}
//******************************************//
//recorre toda la lista de nodos del grafo //
//******************************************//
template <typename datonodo_t, typename datoarista_t>
std::list<nodo<datonodo_t,datoarista_t>*>Grafo<datonodo_t,datoarista_t>::recorrerNodos() const
{
//nodos.clear();
std::list<pNodo> nodos;
if (Raiz)
{
pNodo indice = Raiz;
nodos.push_back(indice);
while (indice->siguiente)
{
indice= indice->siguiente;
nodos.push_back(indice);
}
}
return nodos;
}
//*************************************//
//recorrer el arbol a partir de un nodo//
//*************************************//
template <typename datonodo_t, typename datoarista_t>
std::list<nodo<datonodo_t,datoarista_t>*>& Grafo<datonodo_t,datoarista_t>::recorrerGrafo(const pNodo& inicio)
{
VaciarPila(pilaAristas);
nodos.clear();
return lista_recorrerGrafo(inicio);
}
template <typename datonodo_t, typename datoarista_t>
std::list<nodo<datonodo_t,datoarista_t>*>& Grafo<datonodo_t,datoarista_t>::lista_recorrerGrafo(const pNodo& inicio)
{
pArista A;
if (inicio)
{
guardaAristas(inicio);
while (!pilaAristas.empty())
{
A=pilaAristas.top();
pilaAristas.pop();
nodos.push_back(A->destino);
lista_recorrerGrafo(A->destino);
}
}
return nodos;
}
//*********************************************************************//
//busca si un nodo que quiero copiar como hijo de otro es padre de éste//
//necesario para evitar referencias circulares //
//*********************************************************************//
template <typename datonodo_t, typename datoarista_t>
bool Grafo<datonodo_t,datoarista_t>::esReferenciaCircular(pNodo& padre, pNodo& hijo)
{
if (padre==hijo)
{
std::cout<<"Referencia $$ circular"<<std::endl;
return true;
}
else
{
pArista A;
bool encontrado=false;
guardaAristas(hijo);
while (!pilaAristas.empty())
{
A=pilaAristas.top();
pilaAristas.pop();
if (A->destino==padre)
{
//vacio la pila
while (!pilaAristas.empty())
pilaAristas.pop();
std::cout<<"Referencia circular"<<std::endl;
return true;
}
return encontrado || esReferenciaCircular(padre,A->destino);
}
}
return false;
}
//*************************************************************//
//comprueba la existencia de un nodo igual al que quiero copiar//
//necesario para evitar tener dos nodos iguales colgando del //
//mismo padre //
//*************************************************************//
template <typename datonodo_t, typename datoarista_t>
bool Grafo<datonodo_t,datoarista_t>::existeHermano (pNodo& padre, pNodo& hijo)
{
pArista A=padre->adyacente;
while (A!=nullptr)
{
//std::cout<<"posicionada la arista"<<A<<std::endl;
if (A->destino==hijo)
{
return true;
}
A=A->siguiente;
}
return false;
}
//*********************************************//
//comprueba la presencia de un nodo en la lista//
//para evitar su inclusión por duplicado //
//*********************************************//
template <typename datonodo_t, typename datoarista_t>
bool Grafo<datonodo_t,datoarista_t>::existeNodo (const pNodo& n)
{
pNodo indice=Raiz;
if (n==indice)
return true;
else
{
while (indice->siguiente)
{
indice=indice->siguiente;
if (n==indice)
{
return true;
}
}
}
return false;
}
//***************************************************//
//guarda las aristas que penden de un nodo en la pila//
//***************************************************//
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::guardaAristas (const pNodo& n)
{
pArista A;
if (n && n->adyacente) //si hay aristas
{
A=n->adyacente;
//me posiciono en la última arista
while (A->siguiente!=nullptr)
{
A=A->siguiente;
}
//empiezo el recorrido hacia atrás
while (A->anterior!=nullptr)
{
pilaAristas.push (A); //meto la arista en la pila
A=A->anterior;
}
pilaAristas.push (A); //meto la primera arista en la pila*/
}
}
template <typename datonodo_t, typename datoarista_t>
void Grafo<datonodo_t,datoarista_t>::guardaAristasParaCopia(const pNodo& n)
{
pArista A;
if (n && n->adyacente) //si hay aristas
{
A=n->adyacente;
//me posiciono en la última arista
while (A->siguiente!=nullptr)
{
A=A->siguiente;
}
while (A->anterior!=nullptr)
{
if (A->destino)
pilaAristasParaCopia.push (A); //meto la arista en la pila
A=A->anterior;
}
if (A->destino)
pilaAristasParaCopia.push (A); //meto la arista en la pila
}
}
//*******************************************************************************************************//
//si existe un nodo con el valor a comparar, devolvemos ese nodo. En caso concreto devolvemos valor nulo //
//*******************************************************************************************************//
template <typename DATON, typename DATOA>
nodo<DATON,DATOA>* Grafo<DATON,DATOA>::posicionarseEnNodo(const datonodo_t& dato)
{
std::cout<<"Buscando el nodo con codigo: "<<dato<<std::endl;
pNodo indice=Raiz;
while (indice)// && indice->siguiente)
{
if (indice->datonodo == dato)
{
return indice;
}
indice=indice->siguiente;
}
return nullptr;
}
//*********************//
// vaciar la pila dada //
//*********************//
template <typename DATON, typename DATOA>
template <typename T>
void Grafo<DATON,DATOA>::VaciarPila(std::stack<T>& pila)
{
while (!pila.empty())
{
pila.pop();
}
}
#endif // GRAFO_H
A main.cpp to handle it:
#include <list>
#include <iomanip>
#include "./grafo.h"
typedef char datonodo_t;
typedef int datoarista_t;
typedef nodo<datonodo_t,datoarista_t>* pNodo;
typedef arista<datoarista_t,datonodo_t>* pArista;
typedef nodo<datonodo_t,datoarista_t> t_nodo;
typedef arista<datoarista_t,datonodo_t> t_arista;
void verLista (std::list<pNodo>lista)
{
for (auto it = lista.begin(); it!=lista.end(); ++it)
{
std::cout<<(*it)->datonodo;
}
std::cout<<std::endl;
}
void verLista(std::list<std::pair<pNodo,int>>lista)
{
for (auto it=lista.begin(); it!=lista.end(); ++it)
{
std::cout<<std::setw((*it).second)<<"-"<<(*it).first->datonodo<<"-"<<&(*it)<<std::endl;
}
std::cout<<std::endl;
}
int main(int argc, char *argv[])
{
pNodo A = new t_nodo('A');
pNodo B = new t_nodo('B');
pNodo C = new t_nodo('C');
pNodo D = new t_nodo('D');
pNodo E = new t_nodo('E');
pNodo F = new t_nodo('F');
pNodo G = new t_nodo('G');
pArista AB = new t_arista(20);
pArista AC = new t_arista(20);
pArista AD = new t_arista(20);
pArista BE = new t_arista(20);
pArista BF = new t_arista(20);
pArista BG = new t_arista(20);
//pArista CG = new t_arista(20);
Grafo<datonodo_t,datoarista_t>g(A);
g.Insertar(A,B,AB);
g.Insertar(A,C,AC);
g.Insertar(A,D,AD,0);
g.Insertar(B,E,BE);
g.Insertar(B,F,BF);
g.Insertar(B,G,BG);
//g.Insertar(C,G,CG);
std::cout<<"lista original: ";
verLista(g.recorrerNodos());
verLista(g.recorrerGrafo(g.LeeRaiz()));
std::cout<<"Raiz: "<<g.LeeRaiz()->datonodo<<std::endl;
Grafo<char,int>g1(g);
std::cout<<"lista duplicada g1: ";
verLista(g1.recorrerNodos());
verLista(g1.recorrerGrafo(g1.LeeRaiz()));
std::cout<<"Raiz g1: "<<g1.LeeRaiz()->datonodo<<std::endl;
return 0;
}
If the program runs as it is now (without creating the CG edge or inserting it), there are no memory leakage errors, but if I decompose these last two lines there is. When I invoke the constructor copy of the graph, it traverses the original graph node by node. The idea -in the function Copiar()
of the class Grafo
is that if in the target graph the node does not exist, it is created and linked to the graph by means of its edges, but if it already exists, it is only linked. It is an implementation of the adjacency list method.