Problema al sobrecargar el operador +

Iniciado por cNoob, 18 Diciembre 2017, 20:07 PM

0 Miembros y 1 Visitante están viendo este tema.

cNoob

Ya se por que sucede el error que se muestra en este post, pero ahora el problema es otro (se dice en mi respuesta al mismo) no hace falta leer el código del programa para saber cual es mi duda. Por favor leed mi respuesta en el hilo para poder ayudarme, gracias.

Hola, he creado el siguiente programa para manejar y operar polinomios, consta de una clase llamada termino (c*x^e) y una clase llamada polinomio que es básicamente un array de términos y sus correspondientes operaciones, así como un método para mostrar el polinomio por pantalla. El codigo es algo largo pero el problema está principalmente en el método operator+, el cual si pido que muestre por pantalla el resultado (antes del return) el polinomio es correcto, pero una vez que lo devuelve y lo asigna a otro objeto polinomio y hago que este ultimo se muestre por pantalla es incorrecto (espero haberme hecho entender). Aquí os dejo el codigo:

main.cpp
Código (cpp) [Seleccionar]
#include <iostream>
#include "CPolinomio.h"

using namespace std;

int main()
{
   CPolinomio p1(5,7);
   CPolinomio p2(3,6);
   CPolinomio p3;

   p1.asignarTermino(4,5);
   p1.asignarTermino(10,3);
   p2.asignarTermino(4,3);
   p2.asignarTermino(5,5);
   p2.mostrarPolinomio(); cout << " + ";
   p1.mostrarPolinomio(); cout << endl;
   p3 = p1 + p2; cout << endl;
   (p1 + p2).mostrarPolinomio(); cout << endl;
   p2.mostrarPolinomio();
   //p2 = p1 + p1 + p1;
   //p2.mostrarPolinomio();
   //(p1+p1).mostrarPolinomio();

   return 0;
}


CTermino.h
Código (cpp) [Seleccionar]
#ifndef CTERMINO_H
#define CTERMINO_H


class CTermino
//Cada uno de los terminos del polinomio
{
private:
   double coeficiente;
   int exponente;
public:
   CTermino(double = 0, int = 0);
   void asignarCoeficiente (double);
   void asignarExponente (int);
   double obtenerCoeficiente (void) const;
   int obtenerExponente (void) const;
   void mostrar(void) const;
};

#endif // CTERMINO_H


CTermino.cpp
Código (cpp) [Seleccionar]
#include "CTermino.h"
#include <iostream>

CTermino::CTermino(double c, int e) :
coeficiente(c), exponente(e)
{
   if(e < 0){exponente = -e;}
}

void CTermino::asignarCoeficiente (double c) {coeficiente = c;}

void CTermino::asignarExponente (int e)
{
   if(e < 0){exponente = -e;}
   else{exponente = e;}
}

double CTermino::obtenerCoeficiente (void) const {return coeficiente;}

int CTermino::obtenerExponente (void) const {return exponente;}

void CTermino::mostrar(void) const//es muy enrrevesado para mostrar los terminos como se suele hacer en algebra...
{
   if(!(coeficiente >= 0)){std::cout << " ";}
   if(coeficiente > 0) std::cout << " +";
   if(coeficiente != 1 && coeficiente != -1) {std::cout << coeficiente;}
   if(coeficiente == -1 && exponente == 1) std::cout << "-";
   if(exponente > 0)
   {
       if(exponente == 1) std::cout << "x";
       else std::cout << "x^" << exponente;
   }
}


CPolinomio.h
Código (cpp) [Seleccionar]
#ifndef CPOLINOMIO_H
#define CPOLINOMIO_H
#include "CTermino.h"


class CPolinomio
{
private:
   CTermino* termino;
   int grado;
   void crearPolinomio (int);
   void ampliar (int);
public:
   CPolinomio(double = 0,int = 0);
   ~CPolinomio();
   int obtenerGrado (void) const;
   void asignarTermino (double , int);
   void mostrarPolinomio(void) const;
   const CPolinomio operator=(const CPolinomio&);
   const CPolinomio operator+(const CPolinomio&) const;
   const CPolinomio operator*(const CPolinomio&) const;
};

#endif // CPOLINOMIO_H


CPolinomio.cpp
Código (cpp) [Seleccionar]
#include "CPolinomio.h"
#include <iostream>

CPolinomio::CPolinomio(double c, int e)//Constructor que crea la matriz de terminos del tamaño necesario
{
   grado = e; if(e < 0) {grado = -e;}//si el exponente es negativo lo cambia
   crearPolinomio(e);
   termino[e].asignarCoeficiente(c);//asigna el coeficiente al exponente correcto
}

void CPolinomio::crearPolinomio(int g)//crea la matriz de objetos termino del tamaño necesario
{
   delete [] termino; termino = new CTermino [g+1];
   for (int i = 0; i <= g; i++) termino[i].asignarExponente(i);
}

void CPolinomio::ampliar (int g)//amplia la matriz terminos al tamaño pasado por parametro
{
   double coeficientes [grado+1];
   for(int i = 0; i <= grado; i++) coeficientes [i] = termino[i].obtenerCoeficiente();//copia los terminos en una matriz
   crearPolinomio(g);
   for (int i = 0; i <= grado; i++) termino[i].asignarCoeficiente(coeficientes [i]);//copia los terminos de vuelta al nuevo array
   grado = g;
}

CPolinomio::~CPolinomio()
{
   delete [] termino;
}

int CPolinomio::obtenerGrado(void) const {return grado;}

void CPolinomio::asignarTermino(double c, int e)
{
   if(e < 0) return;//SI NO ES VALIDO NO HACE NADA
   if(e > grado)//SI EL EXPONENTE ES MAYOR QUE EL GRADO CREA UN NUEVO POLINOMIO ADECUADO
   {
       ampliar(e);
       termino[e].asignarCoeficiente(c);
   }
   else {
       termino[e].asignarCoeficiente(c);//SI TODO ES NORMAL SIMPLEMENTE LO CAMBIA
   }
}

void CPolinomio::mostrarPolinomio(void) const//funcion que muestra el polinomio
{
   std::cout << "(";
   for(int i = grado; i > 0; i--) {if(termino[i].obtenerCoeficiente() != 0)termino[i].mostrar();}
   if(termino[0].obtenerCoeficiente() != 0) termino[0].mostrar();
   std::cout << ")";
}

const CPolinomio CPolinomio::operator=(const CPolinomio& pol)//operador =
{
   grado = pol.grado;
   ampliar(pol.grado);
   for(int i = 0; i <= pol.grado; i++) termino[i].asignarCoeficiente(pol.termino[i].obtenerCoeficiente());
}

const CPolinomio CPolinomio::operator+(const CPolinomio& pol) const//operador +
{

   if(grado == pol.grado)//si ambos grados son iguales (no hay que ampliar)
   {
       CPolinomio resultado(0, grado);
       for(int i = 0; i <= resultado.grado; i++)
       {
           resultado.termino[i].asignarCoeficiente(pol.termino[i].obtenerCoeficiente() + termino[i].obtenerCoeficiente());
       }
       resultado.mostrarPolinomio();//codigo experimental para saber si el error es al devolver el objeto
       return resultado;
   }
   else {
       if(grado > pol.grado)//si uno de los polinomios tiene mas grado
       {
           CPolinomio resultado(0, grado);
           for(int i = 0; i <= pol.grado; i++)
           {
               resultado.termino[i].asignarCoeficiente(pol.termino[i].obtenerCoeficiente() + termino[i].obtenerCoeficiente());
           }
           for(int i = pol.grado+1; i <= grado; i++) resultado.termino[i].asignarCoeficiente(termino[i].obtenerCoeficiente());
           resultado.mostrarPolinomio();//codigo experimental para saber si el error es al devolver el objeto
           return resultado;
       }
       if(grado < pol.grado)//si el otro polinomio es mayor
       {
           CPolinomio resultado(0, pol.grado);
           for(int i = 0; i <= grado; i++)
           {
               resultado.termino[i].asignarCoeficiente(pol.termino[i].obtenerCoeficiente() + termino[i].obtenerCoeficiente());
           }
           for(int i = grado+1; i <= pol.grado; i++) resultado.termino[i].asignarCoeficiente(pol.termino[i].obtenerCoeficiente());
           resultado.mostrarPolinomio();//codigo experimental para saber si el error es al devolver el objeto
           return resultado;
       }
   }
}

const CPolinomio CPolinomio::operator*(const CPolinomio& pol) const//operador *
{
   int gr = pol.grado*grado;
   CPolinomio resultado(gr);

   for (int i = 0; i <= grado; i++)
   {
       if(termino[i].obtenerExponente() != 0)
       {
           CPolinomio calculos;
           for(int x = 0; x <= pol.grado; x++)
           {
               calculos.asignarTermino(termino[i].obtenerCoeficiente() * pol.termino[x].obtenerCoeficiente(), termino[i].obtenerExponente() + pol.termino[x].obtenerExponente());
           }
           resultado = resultado + calculos;
       }
   }

   return resultado;
}


Cuando lo ejecuto se muestra lo siguiente:

( +3x^6 +5x^5 +4x^3) + ( +5x^7 +4x^5 +10x^3)
( +5x^7 +3x^6 +9x^5 +14x^3)
( +5x^7 +3x^6 +9x^5 +14x^3)( +5x^1981370576 +9.88131e-324)
( +3x^6 +5x^5 +4x^3)
Process returned 0 (0x0)   execution time : 0.111 s
Press any key to continue.


Espero que alguien sepa que puede ser, sospecho que estoy retornando el objeto resultado de operator+ mal...
Wannabe programador autodidacta de c++
"Usain Bolt comenzó gateando."

CalgaryCorpus

Sugiero que escribas tu propio constructor por default en la clase Polinomio, y que no ejecutes el que por default hace nada. De esta manera, en el main, cuando p3 es construido, sabes exactamente lo que se esta construyendo y no descansas en comportamientos automaticos provistos por el compilador.
Aqui mi perfil en LinkedIn, invitame un cafe aqui

cNoob

Cita de: CalgaryCorpus en 19 Diciembre 2017, 00:21 AM
De esta manera, en el main, cuando p3 es construido, sabes exactamente lo que se esta construyendo y no descansas en comportamientos automáticos provistos por el compilador.
En el constructor de la clase polinomio automáticamente se asigna el valor 0 al coeficiente de cada termino y su valor correspondiente al exponente (a no ser que se pase un coeficiente a un termino en los parámetros a la hora de crear el polinomio) por lo que esos "valores basura" que supongo que vienen de espacios de memoria no asignados no deberían de estar ahí
Wannabe programador autodidacta de c++
"Usain Bolt comenzó gateando."

cNoob

Vale, he descubierto que pasa, cuando el operador devuelve el objeto "resultado" el atributo "termino" es devuelto también y el destructor de la clase borra la memoria donde estaba el array con los términos, por lo que estos no son copiados como deberían por el operador = (si se elimina el destructor el programa funciona perfectamente, pero este es necesario para vaciar la memoria reservada por el operador new).
Mi pregunta ahora es: ¿como puedo hacer que primero se copien los términos y que cuando esta operación finalice se ejecute el destructor del objeto resultado?
Wannabe programador autodidacta de c++
"Usain Bolt comenzó gateando."

CalgaryCorpus

Aqui mi perfil en LinkedIn, invitame un cafe aqui

cNoob

Cita de: CalgaryCorpus en 19 Diciembre 2017, 17:56 PM
Crea un constructor de copia
Por qué un constructor copia solucionaría el problema?
Wannabe programador autodidacta de c++
"Usain Bolt comenzó gateando."

cNoob

Cita de: CalgaryCorpus en 19 Diciembre 2017, 17:56 PM
Crea un constructor de copia
Vale, acabo de escribir el siguiente constructor copia y el programa funciona perfectamente
Código (cpp) [Seleccionar]
CPolinomio::CPolinomio(const CPolinomio& P)
{
    grado = P.grado;
    crearPolinomio(grado);
    for (int i = 0; i <= grado; i++) termino[i] = P.termino[i];
}

Todavía no se por que se necesita el constructor copia... (si alguien puede explicármelo por favor) muchas gracias por la ayuda :D
Wannabe programador autodidacta de c++
"Usain Bolt comenzó gateando."

CalgaryCorpus

#7
Las variables locales se mueren o deberian morirse al terminar el bloque donde estan. El polinomio que aparece al lado derecho del operador = es una copia de la variable local que se murio. Si no tienes constructor de copia, entonces uno default se provee que hace copias sin mucha inteligencia, en particular con los punteros, generando alias.
2 objetos con punteros a los mismos datos, solo que uno de ellos los destruye y deja al otro apuntando al limbo.

Solucion? Constructor de copia que puede hacer copias mas inteligentes, en particular con los punteros haciendo que cada objeto apunte a su propia copia de datos y entonces da lo mismo si el objeto original se muere y destruye los datos, porque antes de hacerlo el otro objeto copio y dejo su propio puntero apuntando a su propia copia.


Aqui mi perfil en LinkedIn, invitame un cafe aqui

cNoob

Oh, entiendo... supuse que el compilador haría uso del operador de asignación para pasar los datos de un objeto al otro, pero ahora ya se que no. Gracias   ;D
Wannabe programador autodidacta de c++
"Usain Bolt comenzó gateando."