[C con Clase] Sobre un Problema en un Sistema con Listas Enlazadas

Steven Davidson srd4121 en njit.edu
Lun Ago 30 22:06:26 CEST 2010


Hola Edery,

On 8/30/2010 8:31 AM, Edery Rodriguez wrote:
> Buenos dias... Si, precisamente por ahi esta el problema, me muestra
> los nombres de los directorios, más no el de los archivos que se
> deberían estar agregando al momento de insertar todos los datos... De
> todas formas muestro a continuación el código del modulo Agregar
> Directorio:
>

Ciertamente, el problema comienza con 'AgregarDir()', pero el error está 
en la clase-plantilla 'Lista'. Te lo explico más abajo.

>
> bool Administracion::AgregarDir(string ND, string NA, int TA)
> {
>       nodo<Directorio> *apDir;
>       nodo<Registro> *apreg;
>       Directorio Dir;
>       Registro Reg;
>       bool enc=false;
>
>       if(!ListaDir.Llena())
>       {
>           if(BuscarDir(ND)==true) //Busca si el Directorio Existe, si
> no, solo agrega el Archivo
>           {
>               if(Dir.BuscarRegistro(NA)==false)
>              {
>                   Dir.AgregarRegistro(NA,TA);
>                   return true;
>               }
>               else
>                  return false;
>           }
>           else                    // Si el Directorio no Existe,
> entonces lo crea e inserta el archivo que viene para este
>           {
>
>               Dir = Directorio(ND);
>

Aquí empiezan los problemas. Debido a la falta de un constructor copia 
para 'Lista' en 'Directorio', ya que esta clase-plantilla contiene un 
puntero como miembro. Por lo tanto, el constructor copia por defecto 
simplemente copia todos los miembros de un objeto a sus homólogos en el 
otro. Como estás copiando punteros, realmente no mantienes copias de la 
información, sino de los punteros. Esto significa que apuntas al mismo 
objeto.

En este caso, el objeto siendo apuntado es temporal y por tanto 
desaparecerá después de ejecutar esta sentencia. Esto deja al puntero de 
la lista en 'Dir' apuntando a algo que no existe.

Al principio, esto no es un grave problema, pero como copias listas 
posteriormente, entonces sí empezamos a tener problemas de memoria.

La solución es implementar un constructor copia para la clase-plantilla 
'Lista'. En este momento, también podríamos aliviar todas estas 
asignaciones al instanciar el objeto ahora:

Director Dir(ND);

Recuerda que esto es C++ y no C. No tenemos que crear todas las 
variables al principio de la función; podemos crearlas justo antes de 
necesitarlas.

>               if(ListaDir.Vacia())
>               {
>                   ListaDir.InsComienzo(Dir);//Reg = Registro(NA,TA);
>                   Dir.AgregarRegistro(NA,TA);

Esta lógica no es correcta. Agregas 'Dir' a la lista de directorios, 
pero luego modificas 'Dir'. Este cambio no involucra la lista de 
directorios. He aquí el problema de la desaparición de los registros por 
cada directorio.

Deberías construir 'Dir' completamente antes de agregarlo. Esto es,

Dir.AgregarRegistro(NA,TA);
ListaDir.InsComienzo(Dir);

Sin embargo, aquí tenemos el problema que expliqué anteriormente. La 
función miembro, 'InsComienzo()', internamente copia 'Dir' al nodo de la 
lista, pero 'Dir' contiene otra lista sin un constructor copia correcto. 
Al final, 'ListaDir' contendrá directorios cuyas listas de registros 
contienen punteros a información que ya no existe. La razón de que no 
exista es porque estaban en 'Dir', pero éste deja de existir al terminar 
de ejecutar esta función, 'AgregarDir()', porque es un objeto local.

>                   return true;
>               }
>               else
>               {
>                    apDir=ListaDir.ObtPrimero();
>                    while(ListaDir.ObtProx(apDir)!=NULL)
>                    {
>                        apDir=ListaDir.ObtProx(apDir);
>                    }
>                    ListaDir.InsDespues(apDir, Dir);
>                    Reg = Registro(NA,TA);

Esto no tiene sentido, tal y como has definido las funciones miembro de 
'Directorio'. Estaría bien si existies una forma de agregar un objeto de 
la clase 'Registro', pero como no lo has hecho, entonces lo anterior es 
superfluo.

>                    Dir.AgregarRegistro(NA,TA);
>                    return true;
>               }
>
>           }
>       }
>       else
>           return false;
> };
>
>
>

Como he dicho, la solución es crear un constructor copia que realice la 
tarea de copiar todos los nodos de una lista a ésta, mientras se 
construye. Esto debería ser así:

Lista( const Lista &ref );


También aconsejo que definas las funciones miembro en todas tus clases 
como constantes donde se requiera. Por ejemplo,

Para 'Lista':

bool Vacia() const;
bool Llena() const;
Tipo ObtInfo( Apuntador p ) const;


Para 'Directorio':

string getNombreDir() const;
int VerCantidad() const;
float MontoTotal() const;
void MostrarRegistros() const;
bool BuscarRegistro( string BuscReg ) const;

etcétera.

Por último, sugiero que te acostumbres a pasar objetos como referencias 
y a veces como referencias a objetos constantes. Por ejemplo,

bool Directorio::BuscarRegistro( const string &BuscReg ) const;
void Directorio::setNombreDir( const string &ND );
string Directorio::getNombreDir() const;


Espero haber aclarado las dudas.

Steven





Más información sobre la lista de distribución Cconclase