Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Duda sobre destrucción de objetos compuestos por otros objetos (https://www.clubdelphi.com/foros/showthread.php?t=63463)

noob 15-02-2009 17:06:36

Duda sobre destrucción de objetos compuestos por otros objetos
 
Hola, me gustaría saber bien cómo destruir objetos pues aunque si no lo hago el programa funciona sin errores no me gusta ir dejando memoria sin liberar.

Yo usaba siempre Destroy para destruir objetos pero en otro hilo y en este otro de este foro me recomendaron destruir con Free aquellos objetos que no fueran hilos.

Hace poco pregunté cómo saber si un objeto estaba creado y ahí me recomendaron usar FreeAndNil(Objeto); para destruir el objeto.

Y la duda que tengo es, si tenemos un objeto con varios atributos cuyos tipos son otros objetos, yo tengo dos opciones de destruir:

1) Desde dentro de la clase del objeto:

Código Delphi [-]
destructor MiClase.Destroy; 
begin   
  FreeAndNil(Atributo1);   
  FreeAndNil(Atributo2);   
  FreeAndNil(Atributo3);   
  FreeAndNil(Atributo4); 
end;

2) Desde fuera de la clase del objeto (en otra clase que use a la anterior):

Código Delphi [-]
FreeAndNil(MiObjetoCompuesto);

En el segundo caso no implemento el método destroy en la clase MiClase y sólo llamo a FreeAndNil desde la clase externa.

¿Qué opción es la correcta? ¿Serían las dos opciones equivalentes?

Otra cosa, ¿Delphi no destruye todos los objetos creados al cerrar la aplicación?

Saludos.

DarkMan 15-02-2009 17:16:03

FreeAndNil lo que hace es llamar al método Free del objeto y luego asignar la variable que apunta hacia él con "nil", después ese método free se encarga de llamar al método destroy. Es por eso que uno no quita del otro, realmente estas usando los dos con FreeAndNil.

Cita:

Otra cosa, ¿Delphi no destruye todos los objetos creados al cerrar la aplicación?
Sí, lo hace, pero ojo porque hay excepciones, espera a que alguien que sepa más del tema te comente sobre esto.

noob 15-02-2009 17:42:17

Cita:

Empezado por DarkMan (Mensaje 338190)
... después ese método free se encarga de llamar al método destroy ...

He hecho esto:

Unit1:

Código Delphi [-]
unit Unit1; 

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
    ObjMiClase: cMiClase;
  public
    { public declarations }
  end; 

var
  Form1: TForm1; 

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ObjMiClase := cMiClase.Create;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FreeAndNil(ObjMiClase);
end;

end.

Unit2:

Código Delphi [-]
unit Unit2; 

interface

uses Unit3;

type
  cMiClase = class
               private
                 Obj: cMiClase2; 
               public
                 constructor Create;
                 destructor Destroy; override;
             end;

implementation

constructor cMiClase.Create;
begin
  Obj := cMiClase2.Create;
  inherited Create;
end;

destructor cMiClase.Destroy;
begin
  FreeAndNil(Obj);
  inherited Destroy;
end;

end.

Unit3:

Código Delphi [-]
unit Unit3; 

interface

type
  cMiClase2 = class
                private
                public
                  destructor Destroy;
              end;

implementation

destructor cMiClase2.Destroy;
begin
  inherited Destroy;
end;

end.

y al pulsar el botón el objeto se destruye pero no llama al método Destroy como dices.

DarkMan 15-02-2009 17:56:45

Es que me da la impresión de que estas creando mal el objeto.
Aquí:
Código Delphi [-]
procedure TForm1.FormCreate(Sender: TObject);
begin
  ObjMiClase.Create;
end;

Estas creando una instancia del objeto pero no asignas su dirección de memoria a ninguna variable. Tendrías que hacer algo como:

Código Delphi [-]
ObjMiClase:= cMiClase.Create;

En este otro caso:
Código Delphi [-]
destructor cMiClase.Destroy;
begin
  FreeAndNil(Obj);
end;

Estas liberando un objeto que no has creado, por defecto apunta a "nil" así que no hay liberación. De todas formas en una clase que se destruye yo no veo necesario el uso de FreeAndNil, pero no es incorrecto. Pienso que con obj.free valdría.

noob 15-02-2009 18:01:34

Cita:

Empezado por DarkMan (Mensaje 338194)
Es que me da la impresión de que estas creando mal el objeto.
Aquí:
Código Delphi [-]procedure TForm1.FormCreate(Sender: TObject); begin ObjMiClase.Create; end;


Estas creando una instancia del objeto pero no asignas su dirección de memoria a ninguna variable. Tendrías que hacer algo como:

Código Delphi [-]ObjMiClase:= cMiClase.Create;

Cierto, que error tan tonto, ya lo edité.

Pero FreeAndNil sigue sin llamar al método Destroy.

DarkMan 15-02-2009 18:18:09

Sí que lo llama, sino te lo crees analicemos el código de cada procedimiento:

FreeAndNil:
Código Delphi [-]
procedure FreeAndNil(var Obj);
var
  Temp: TObject;
begin
  Temp := TObject(Obj);
  Pointer(Obj) := nil;
  Temp.Free;
end;

llama a Free del objeto que indiques en Obj.

Obj.free:
Código Delphi [-]
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

Aquí llama al método destroy del propio objeto.

Si te fijas, para que se llame al método destroy existe la condición de que el objeto exista:
Código Delphi [-]
...
 if Self <> nil then
...

Así que en conclusión podemos sacar que fallas también a la hora de crear ese objeto, puede que estés cometiendo el mismo error.

Edito:
en la unit2 te faltan los inherited dentro del constructor y destructor.

noob 15-02-2009 19:07:50

Cita:

Empezado por DarkMan (Mensaje 338196)
Así que en conclusión podemos sacar que fallas también a la hora de crear ese objeto, puede que estés cometiendo el mismo error.

He puesto ShowMessage('Objeto creado'); dentro del método constructor Create de la clase cMiClase y sí que se muestra el mensaje a la hora de crear el objeto, por lo que sí que lo crea bien (ya corregí ese fallo de creación).

En el método destructor Destroy de la clase cMiClase he puesto ShowMessage('Objeto destruido'); pero este mensaje no aparece por lo que concluyo que el método Destroy no se llega a ejecutar.

Cita:

Empezado por DarkMan (Mensaje 338196)
Edito:
en la unit2 te faltan los inherited dentro del constructor y destructor.

Lo voy a corregir esto, también, puede que sea la causa del problema.

Saludos.

Crandel 15-02-2009 19:34:59

Te hago dos obeservaciones

1. Te falta el override del destructor, tiene que ser asi:
Código Delphi [-]
destructor Destroy; override;

2. Llama al inherited del destructor al final

Código Delphi [-]
destructor cMiClase.Destroy;
begin
  FreeAndNil(Obj);
  inherited Destroy;
end;

Crandel 15-02-2009 19:37:03

Tercera observación :p, se que lo que pussite es un ejemplo sencillo, pero no es suele ser conveniete crear un objeto en en Create de una clase y destruirlo en un boton.

Al González 15-02-2009 22:03:30

Cita:

Empezado por DarkMan (Mensaje 338194)
Es que me da la impresión de que estas creando mal el objeto.
Aquí:
Código Delphi [-]
procedure TForm1.FormCreate(Sender: TObject);
begin
  ObjMiClase.Create;
end;

Estas creando una instancia del objeto pero...

Sólo aclarar que esa sintaxis (variable.constructor) no crea ninguna instancia. Invito a leer este otro hilo: http://www.clubdelphi.com/foros/show...225#post326225 :)

Saludos.

Al González.

noob 15-02-2009 22:44:17

Cita:

Empezado por Crandel (Mensaje 338200)
Te hago dos obeservaciones

1. Te falta el override del destructor, tiene que ser asi:
Código Delphi [-]destructor Destroy; override;

Ahora sí que se ejecuta el método, ese era el gran fallo, pensaba que al igual que en el constructor no era necesario redefinir el método. Editado queda.

Cita:

Empezado por Crandel (Mensaje 338200)
2. Llama al inherited del destructor al final

Código Delphi [-]destructor cMiClase.Destroy; begin FreeAndNil(Obj); inherited Destroy; end;

Gracias, he cometido otro fallo de principiante aquí :D. Lo he editado también.

Cita:

Empezado por Crandel (Mensaje 338201)
Tercera observación , se que lo que pussite es un ejemplo sencillo, pero no es suele ser conveniete crear un objeto en en Create de una clase y destruirlo en un boton.

Efectivamente es un ejemplo pero sí que acostumbro a crear objetos nada más crearse la aplicación, ¿por qué no es conveniente? Por otro lado no acostumbro a destruir objetos pulsando botones.

Saludos.

Crandel 16-02-2009 00:28:33

Cita:

Empezado por noob (Mensaje 338210)
Gracias, he cometido otro fallo de principiante aquí . Lo he editado también.

Pues cometiste otro más :p . Sólo debias invertir en el Destroy y no en el contructor.

La idea es pedir que cree todo lo que necesita la clase base antes de que cree nuestras cosas; y al destruir, destruimos todas nuestras cosas antes que los elementos propios de la clase base.

Cita:

Empezado por noob (Mensaje 338210)
Efectivamente es un ejemplo pero sí que acostumbro a crear objetos nada más crearse la aplicación, ¿por qué no es conveniente? Por otro lado no acostumbro a destruir objetos pulsando botones.

Justamente eso, si creas los objetos en el Create, destruilos cuando se destruye en el formulario y no en el evento un botón.

solo lo aclaraba por las dudas.

Aclaración del FreeAndNil, como se ve en el código de esta función que publico DarkMan, la unica accion agreda es la de asignar al puntero del objeto el valor nil. Por lo que no tiene sentido llamarlo cuando no es necesario, principalmente en el destroy, simplemente llama al metodo Free.

La función esta realmente definida para los casos en los que los objetos se crean dinamicamente y en un determinado momento necesitas diferenciar su estado. Entonces puesdes preguntar
Código Delphi [-]
if objeto <> nil then


La franja horaria es GMT +2. Ahora son las 19:10:12.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi