Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: digimikeh en 18 Junio 2019, 22:11 pm



Título: Sobrecarga operador+ y miembro puntero...
Publicado por: digimikeh en 18 Junio 2019, 22:11 pm
Hola nuevamente:

El siguiente código:

Código
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. struct s{
  6.  
  7. private:
  8.    double * d = nullptr;      
  9.  
  10. public:
  11.    s() : d{new double}{}
  12.    ~s(){ if (d != nullptr) delete d; }
  13.    void setd(const double _d){ *this->d = _d; }
  14.    double getd() const { return *this->d; }
  15.  
  16. };
  17.  
  18. s operator+(const s & _s0, const s & _s1){
  19.    s ns;
  20.    ns.setd(_s0.getd() + _s1.getd());
  21.    return ns;
  22. }
  23.  
  24. int main(){
  25.  
  26.    s s0;
  27.    s s1;
  28.  
  29.    s s2;
  30.    s2 = s0 + s1;
  31.  
  32.    return 0;
  33. }
  34.  
  35.  

Aqui listo para ejecutar:
https://onlinegdb.com/rJNPQ68kr (https://onlinegdb.com/rJNPQ68kr)

Al ejecutar, obtengo el mensaje de error:

*** Error in `./a.out': double free or corruption (fasttop): 0x0000000000795c80 ***                                             
Aborted (core dumped)


Estoy seguro que el problema esta entre el puntero privado y el delete en el destructor, ya que si no uso puntero, compila bien.

Sin embargo, he puesto puntero porque quiero entender qué está pasando.  Me da la impresión de que en la definición del operador+ como se crea el objeto ns al terminar el ámbito, se está destruyendo y esta eliminando el puntero.  Lo cual parece valido, pero no tendría por qué corromper la ejecución, se supone que estoy destruyendo un objeto de copia, no los operandos utilizados.



Título: Re: Sobrecarga operador+ y miembro puntero...
Publicado por: RayR en 18 Junio 2019, 23:29 pm
Lo que sucede es que cuando se llega aquí:

Código
  1.    s2 = s0 + s1;

el objeto temporal creado por el operador sobrecargado (ns) es asignado a s2. Esto hace que se ejecute el operador de asignación (operator=) para struct s. Dado que tú no programaste ninguno, se usa el generado por el compilador. Éste simplemente copia los valores de las variables miembro, es decir, que luego de la copia, s2.d apunta a la misma dirección que ns.d. Esa memoria se libera cuando ns es destruida. Y luego, cuando s2 es destruida, naturalmente, se vuelve a intentar liberar la misma dirección, lo cual es incorrecto. Que se trate de copias es irrelevante. Lo que importa es que apuntan a la misma dirección, y es un error intentar liberar memoria ya liberada. Pero eso ni siquiera es lo peor. Como te mencioné, en la línea 30, s2.d apuntará a la dirección de ns.d, pero antes de esa línea apuntaba a otra dirección distinta, la cual jamás fue liberada, por lo que tienes una "fuga de memoria" o memory leak.

La solución es sobrecargar también operator=, y en él no copiar los punteros, sino los valores de las direcciones a las que apuntan (reservando y liberando memoria, según sea necesario). Imagino que aún no habrás visto ese tema, pero ya llegarás y verás cómo implementarlo. En general, siempre que tengamos punteros habría que sobrecargar el operador de asignación y proporcionar un constructor de copia.


Título: Re: Sobrecarga operador+ y miembro puntero...
Publicado por: digimikeh en 19 Junio 2019, 00:48 am
Tienes razón!... me había preocupado de sobrecargar el operador + y no pensé en las consecuencias, debí haber cargado ademas el operador = y después << para imprimirlo...

La lección que me dio esto es que debo pensar en cada sobrecarga de operador necesaria pues una lleva a la otra...

Gracias


Título: Re: Sobrecarga operador+ y miembro puntero...
Publicado por: Loretz en 19 Junio 2019, 02:21 am

El que se mete con uno se está metiendo con todos...

https://en.cppreference.com/w/cpp/language/rule_of_three (https://en.cppreference.com/w/cpp/language/rule_of_three)

Además:
Un comentario y un error:

El comentario es que cuando dices:
Código:
double getd() const { return *this->d; }
no necesitas aclarar que se trata del miembro 'd' de 'this', de eso no hay dudas. El puntero this se usa en las expresiones cuando es *realmente* necesario, por ejemplo, para salvar alguna ambigüedad (necesario), o porque los nombres de los miembros y parámetros ya han hecho un lío tal que no se sabe quién es quién (un parche que suele usarse para dejar en claro situaciones como "el lío este yo no lo hice así que ahora aguantarse el this").

Y el error:
getd() devuelve *d, y el problema aquí es que *d no fue inicializado, no se le asignó un valor; y pretender acceder en modo lectura (cuando haces s2 = s0 + s1;) a una variable sin inicializar es lo que el estándar define como "Undefined Behavior", que significa que puede suceder cualquier cosa (normalmente mala).





Título: Re: Sobrecarga operador+ y miembro puntero...
Publicado por: @XSStringManolo en 19 Junio 2019, 04:26 am
Al hacer s2 = s0 + s1 que se supone que hace? Suma s0 d + s1 d ?


Título: Re: Sobrecarga operador+ y miembro puntero...
Publicado por: digimikeh en 19 Junio 2019, 05:22 am
@#Loretz.. efectivamente, olvide darle un valor por default en el constructor.
Respecto al puntero this, pues si, es que el código es borrador, lo hago nada mas que para fines de practica... en un reléase, el this lo utilizo siempre y cuanod tenga dos o mas variables que tengan el mismo nombre..

Aproposito, muchas gracias por compartir lo de la regla de 3...
creo que defintivamente tengo que echarle una repasadita al constructor de copia ademas..


#@String Manolo, pues si, solo que como el puntero d es privado, estoy accediendo con el getter..