Clase pila estática

Iniciado por GominaTilted, 29 Octubre 2019, 12:46 PM

0 Miembros y 3 Visitantes están viendo este tema.

K-YreX

#10
Bueno, a ver. Lo primero es que en la imagen estás usando un constructor de Pila que recibe un parámetro (int) y ese constructor no sé qué hace por lo que supondré que el parámetro es el tamaño de la pila y el constructor sin parámetros pone un tamaño estándar de 3.

Los <new> y los <delete> no se usan a lo loco pensando "así me aseguro de que hay espacio reservado", no. Tienes que saber cuando hay espacio reservado y cuando no, reservar la memoria justa y necesaria y liberarla correctamente.

Los constructores son las funciones que llaman los objetos cuando se crean. Estos objetos como justo se están creando no tienen nada todavía por lo que sí necesitas reservar memoria. El de por defecto está bien, el de con parámetros supondré que también pero el de copia no. Cuando creas una instancia con el constructor de copia, no le estás asignando espacio.
Código (cpp) [Seleccionar]

Pila miPila1(5); // sigo suponiendo que esto es una pila de longitud 5
Pila miPila2(miPila1); // miPila2 no tiene memoria reservada para el array interno


El destructor como bien dices debe liberar la memoria de esa Pila. Y es más, no necesitas <Vaciar()> la Pila; con liberar la memoria reservada sería suficiente.

La función <Copiar()> no está bien. Esa función la usarás siempre sobre instancias que has creado antes por lo que ya tienen memoria reservada. La duda es: tendrá el mismo tamaño la Pila sobre la que copias que la Pila que copias? Es algo que no puedes asegurar si puedes crear Pilas de distintos tamaños por lo que lo que tendrías que hacer es liberar la memoria de la que quieres copiar y asignarle la misma longitud que a la otra nuevamente (pero sin olvidar primero liberar la memoria que ya tenía).
Código (cpp) [Seleccionar]

Pila pilaTam5(5); // se reserva memoria para 5 elementos
Pila pilaTam10(10); // se reserva memoria para 10 elementos
pilaTam5.Copiar(pilaTam10); // la funcion Copiar() tiene que borrar la memoria para 5 elementos y crear nueva memoria para 10


El operador = está bien ya que haces lo que te digo para la función <Copiar()> pero claro, lo de borrar la memoria que tenía y crear memoria nueva... una de dos, o lo haces dentro de la función <Copiar()> o lo haces fuera pero no lo hagas dos veces.




EDIT: Si las funciones <Altura()> y <Altura2()> son para mostrar el tamaño de la Pila, no tiene sentido que las hagas externas a la clase y reciban la Pila como parámetro. Es mejor que las hagas dentro de la clase y que no crees dos funciones diferentes para lo mismo.
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

GominaTilted

Ahora tengo dos constructores, el por defecto y el que le paso el tamaño de la pila. Todas las cosas que no tengan sentido en cuanto al planteamiento del problema es (como Altura y Altura2), tienen que ver con el enunciado de la práctica, está hecho para que si no te va la sobrecarga no te funcionen bien las recursivas con referencias.
Entonces a lo que iba, si le paso al de copia por parámetro el tamaño máximo de la pila no va, pero si no se lo paso lo ejecuta en bucle.
Mis constructores:
Pila::Pila()
{
    cima = -1;
    max = 10;
    accesos = 0;
   
    v = new int[max];
   
}

Pila::Pila(int n)
{
    cima = -1;
    max = n;
    accesos = 0;
   
    v = new int[max];
   
}

Pila::Pila (const Pila & p)
{
  cima = -1;
  max = 10;
  accesos = 0;
  v = new int[p.max];
  Copiar(p);
}

Pila::~Pila()
{
  delete[] v;
}


Y aquí Copiar() y la sobrecarga:

void Pila::Copiar(Pila p)
{
  delete [] v;
  v = new int [p.max];
  for (int i = 0; i <= p.cima; i++)
  {
    cima++;
    v[i] = p.v[i];
  }
}

const Pila& Pila::operator= (const Pila & p)
{
  cout << "Se ejecuta la sobrecarga" << endl;
  Copiar(p);
  return p;
}


La función Copiar() funciona bien, ya que al ponerle cout saca los valores correctos, entonces el error supongo que es del constructor de copia. Cuando no entra en bucle infinito me sale el error "double free or corruption (fasttop)", el cual he buscado en stack overflow pero no he entendido mucho, simplemente que es posible que al hacer la asignación libere el espacio de memoria de la parte derecha, no izquierda. Copia bien en la pila 2, pero la original la modifica y se queda 0 0 3 4, como si hubiese borrado las dos primeras posiciones o fueran espacios de memoria a los que no se puede acceder.

K-YreX

El constructor de copia tiene un problema y es que le asignas 10 a <max> en vez de asignarle <p.max> ya que la longitud del nuevo tiene que ser la misma que la del que copias.
Y la función <Copiar()> tiene otro problema y es que estás incrementando <cima> pero en ningún momento la pones a -1 antes de empezar a incrementar y tampoco actualizas el valor de <max>. Además como ya no estás usando las funciones <Apilar()> y <Desapilar()> en la función <Copiar()> (y por tanto ya no estás modificando la original) ahora sí puedes (y debes) pasar el parámetro constante y por referencia.
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

GominaTilted

Cita de: YreX-DwX en 31 Octubre 2019, 13:41 PM
El constructor de copia tiene un problema y es que le asignas 10 a <max> en vez de asignarle <p.max> ya que la longitud del nuevo tiene que ser la misma que la del que copias.
Y la función <Copiar()> tiene otro problema y es que estás incrementando <cima> pero en ningún momento la pones a -1 antes de empezar a incrementar y tampoco actualizas el valor de <max>. Además como ya no estás usando las funciones <Apilar()> y <Desapilar()> en la función <Copiar()> (y por tanto ya no estás modificando la original) ahora sí puedes (y debes) pasar el parámetro constante y por referencia.
No sabía si tenía que actualizar el valor de max, ya que como el array se crea en función de p.max, pero claro, no había pensado que interviene todo el objeto. El resto podría haber estado dándole vueltas 1 mes y no lo saco xD. Muchas gracias, ya va. ¿Por qué en la función copiar es conveniente pasar el objeto por referencia y ponerlo como const? Gracias de nuevo, esta es la última pregunta de verdad ^^.

K-YreX

Siempre que los objetos que se pasan como parámetro no se van a modificar es recomendable pasarlos constantes para asegurar que pase lo que pase no se va a modificar (como decirle al compilador "no me dejes modificar este objeto ni por error") y por referencia para que así se pase la dirección de memoria del objeto original y no se cree una copia local (ya que si no se pasa por referencia, se pasa por valor y entonces lo que se hace es crear una copia del objeto con su consiguiente gasto de memoria).
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

GominaTilted

Cita de: YreX-DwX en 31 Octubre 2019, 14:06 PM
Siempre que los objetos que se pasan como parámetro no se van a modificar es recomendable pasarlos constantes para asegurar que pase lo que pase no se va a modificar (como decirle al compilador "no me dejes modificar este objeto ni por error") y por referencia para que así se pase la dirección de memoria del objeto original y no se cree una copia local (ya que si no se pasa por referencia, se pasa por valor y entonces lo que se hace es crear una copia del objeto con su consiguiente gasto de memoria).
Muchas gracias, no lo sabía.