Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Duda con Inicialización de Array de Objetos (https://www.clubdelphi.com/foros/showthread.php?t=87334)

aguml 16-12-2014 11:21:14

Duda con Inicialización de Array de Objetos
 
Hola amigos, estoy usando memoria dinamica y la obtengo bien pero lo que intento es poner toda esa memoria a 0 y lo hago usando memset.

Código PHP:

TAbsPicture *CurrentImage;
TVRLabel **pVRLabel 0;
TVRBand **pVRBand 0;
TVRPage **pVRPage 0;
AnsiString **rutas 0;
AnsiString **rutasDivisiones 0;

//Obtengo memoria para los elementos necesarios
rutas = new(nothrowAnsiString*[nSeleccionadas];
rutasDivisiones = new(nothrowAnsiString*[nTotalDivisiones];
pVRLabel = new(nothrowTVRLabel*[nSeleccionadas];
pVRBand = new(nothrowTVRBand*[nSeleccionadas];
pVRPage = new(nothrowTVRPage*[nSeleccionadas];

//Limpio todo lo necesario
memset(rutas0sizeof(rutas));
memset(rutasDivisiones0sizeof(rutasDivisiones));
memset(pVRLabel0sizeof(pVRLabel));
memset(pVRBand0sizeof(pVRBand));
memset(pVRPage0sizeof(pVRPage)); 

El caso es que no se como hacerlo ya que como lo tengo solo limpio 4 bytes y no es lo que quiero.
Luego he probado ha hacer esto:
Código PHP:

memset(rutas0sizeof(rutas)*nSeleccionadas);
memset(rutasDivisiones0sizeof(rutasDivisiones)*nTotalDivisiones);
memset(pVRLabel0sizeof(pVRLabel)*nSeleccionadas);
memset(pVRBand0sizeof(pVRBand)*nSeleccionadas);
memset(pVRPage0sizeof(pVRPage)*nSeleccionadas); 

Pero no se si lo hago bien o estoy poniendo a 0 lo que no debo ¿podeis indicarme como hacerlo?

aguml 16-12-2014 12:25:29

Se me olvidó poner la funcion donde libero toda esa memoria para que la veais y me digais si veis algo mal:
Código PHP:

void LiberarMemoria()
{
   if(
rutas != 0)
   {
      for(
int i 0nSeleccionadasi++)
         if(
rutas[i] != 0)
            
delete rutas[i];
      
delete[] rutas;
   }
   if(
rutasDivisiones != 0)
   {
      for(
int i 0nTotalDivisionesi++)
      {
         
DeleteFile(*rutasDivisiones[i]);
         if(
rutasDivisiones[i] != 0)
            
delete rutasDivisiones[i];
      }
      
delete[] rutasDivisiones;
   }
   if(
pVRLabel != 0)
   {
      for(
int i 0nSeleccionadasi++)
         if(
pVRLabel[i] != 0)
            
delete pVRLabel[i];
      
delete[] pVRLabel;
   }
   if(
pVRBand != 0)
   {
      for(
int i 0nSeleccionadasi++)
         if(
pVRBand[i] != 0)
            
delete pVRBand[i];
      
delete[] pVRBand;
   }
   if(
pVRPage != 0)
   {
      for(
int i 0nSeleccionadasi++)
         if(
pVRPage[i] != 0)
            
delete pVRPage[i];
      
delete[] pVRPage;
   }
   if(
CurrentImage != 0)
      
delete CurrentImage;



ecfisa 16-12-2014 15:00:31

Hola aguml.

Una consulta, ¿ La variable rutas es un arreglo lineal o bidimensional ?

Saludos :)

Ñuño Martínez 16-12-2014 15:34:08

Estás mezclando C y C++. Recuerda que son lenguajes diferentes.

Lo siguiente:
Código:

//Obtengo memoria para los elementos necesarios
rutas = new(nothrow) AnsiString*[nSeleccionadas];

Sí, estás obteniendo "memoria para los elementos necesarios", pero estás creando nSeleccionadas referencias a objetos de clase AnsiString, que se almacenan en una lista (array) referenciada por el puntero rutas. Ojo, porque no estás creando ningún objeto.

Por eso lo siguiente:
Código:

//Limpio todo lo necesario
memset(rutas, 0, sizeof(rutas));

no limpia nada. Lo que hace es asignar el valor 0 a los primeras 8 octetos (asumiendo 64bit) de la lista referenciada por rutas, independientemente del tamaño de la misma.

memset no sabe de objetos, sólo sabe de posiciones de memoria. Y sizeof devuelve el tamaño del tipo, no el número de elementos de una lista (salvo que lo hayan cambiado en la definición del lenguaje C++ 99 o posterior, que si es así me parece una cagada monumental...). Estas dos funciones son funciones C, no C++.

Para limpiar los objetos has de usar un bucle y limpiar cada objeto, uno a uno, de la forma apropiada, similar a tu código de liberación de memoria:
Código:

int Ndx;

  for (Ndx = 0; Ndx < nSeleccionadas; Ndx++)
    *(rutas[Ndx]) = ""; // La clase AnsiString sobrecarga el operador "=".

Nota: Hace años que no programo C++, y de hecho me mosquea lo de "new(nothrow)", pero creo que no he cometido ningún error. Aun así, si alguien lo tiene más fresco, que lo diga.

ecfisa 16-12-2014 15:58:09

Cita:

Empezado por Ñuño Martínez (Mensaje 486715)
...
Nota: Hace años que no programo C++, y de hecho me mosquea lo de "new(nothrow)", pero creo que no he cometido ningún error. Aun así, si alguien lo tiene más fresco, que lo diga.

No, tampoco creo que hayas cometido algún error, de allí la pregunta de mi mensaje anterior :). Por que según la declaración de la variable rutas, yo habría echo algo parecido a:
Código PHP:

{
  
AnsiString **rutas;

  
// asignar memoria
  
rutas = new AnsiString*[MAXROWS];
  for(
int r 0MAXROWSr++)
    
rutas[r] = new AnsiString[MAXCOLS];

  
// cargar algunos datos
  
for(int r 0MAXROWSr++)
    for (
int c 0MAXCOLSc++) rutas[r][c] = "Rta"+IntToStr(r+c)+" ";

  
// mostrar
  
for(int r 0MAXROWSr++) {
    
"";
    for (
int c 0MAXCOLSc++) += rutas[r][c];
    
ListBox1->Items->Add(s);
  }

  
// liberar memoria
  
for(int c 0MAXROWSc++) delete [] rutas[c];
  
delete [] rutas;


(La constante nothrow, hace que que la función no lance una excepción sino que devuelva un apuntador nulo)

Saludos :)

aguml 16-12-2014 16:15:20

a ver si me se explicar. Ya se que no estoy creando ningun objeto, lo que quiero limpiar es la lista de punteros, justo despues los creo y el poner todos a 0 antes es para luego al liberar la memoria con un condicional compruebo si es o no 0 y solo libero los que no sean 0. En teoria no es necesario ya que "new (nothrow)" devuelve 0 si falla con lo que ya estaria a 0. En el caso del array de AnsiString tambien podria utilizar un TListString pero no se que seria mejor ya que lo uso para almacenar las rutas de unas imagenes para luego poder borrarlas al terminar. No se si un array como el que hago es mas eficiente o no que un TListString ni se como se usa el TListString.

aguml 16-12-2014 16:22:39

solo necesito una lista de AnsiStrings, no necesito mas dimensiones.

aguml 16-12-2014 16:55:48

en realidad todos son listas, unos de objetos, otros de strings. Supongo que para las strings podria utilizar TStringList y para el resto TList pero no se usarlos ni como gestionar cuando deuna excepcion por falta de memoria. Supongo que seria "try[ lista->Add("ruta"); ]catch(...)[ //mensaje de error ]". Perdonen por todo pero es que en el movil no me deja ni poner corchetes, ni saltos de lineas ni nada. Para un TList ¿Como seria? ¿Como creo cada objeto y como lo destruyo y libero memoria en ambos casos?

ecfisa 16-12-2014 17:30:36

Hola aguml.

Si deseas trabajar con un array dinámico de elementos tipo AnsiString la declaración,
Código PHP:

AnsiString **rutas 0

es incorrecta, estas declarando una matriz de elementos tipo AnsiString, la declaración debería ser:
Código PHP:

  AnsiString *rutas

De todos modos creo que tal vez usando TStrings te resulte mas simple... Un ejemplo (sin mucho ornamento):
Código PHP:

TStrings *ts;

// Crear lista
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  
ts = new TStringList;
}

// Cargar texto y grafico en TStrings
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  for(
int i 0ImageList1->Counti++) {
    
Graphics::TBitmapbm = new Graphics::TBitmap;
    
ImageList1->GetBitmap(ibm); 
        
ts->AddObject(IntToStr(i),   // cadena
                     
(TObject*)bm);  // imágen
  
}
  
ListBox1->Items->Assign(ts);
}

// Mostrar en un ListBox
void __fastcall TForm1::ListBox1DrawItem(TWinControl *Controlint Index,
      
TRect &RectTOwnerDrawState State)
{
  
TListBox *lb static_cast<TListBox*>(Control);
  
Graphics::TBitmap *bm static_cast<Graphics::TBitmap*>(lb->Items->Objects[Index]);

  
lb->Canvas->FillRect(Rect);
  
lb->Canvas->Draw(Rect.left,Rect.topbm);  // mostrar bitmap
  
lb->Canvas->TextOut(Rect.left+bm->Width+10// mostrar cadena
    
Rect.toplb->Items->Strings[Index]);
}

// Liberar memoria
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  
delete ts;


(En el ejemplo los gráficos son extraidos de un TImageList)

Resultado:



Saludos :)

aguml 16-12-2014 23:16:38

en tu codigo veo cosas que no entiendo. Por ejemplo, creas un puntero de tipo TStrings y luego le asignas un TStringList ¿Por que no creas un simple AnsiString y luego haces un Add con el TStringList? Por cierto, si creo AnsiString *rutas, cuando quiera guardar una ruta supongo que seria rutas[i] pero ¿Y si quiero por ejemplo leer un caracter de una ruta en concreto? ¿Seria rutas[i][j]?

ecfisa 16-12-2014 23:46:12

Hola aguml.
Cita:

Empezado por aguml (Mensaje 486736)
en tu codigo veo cosas que no entiendo. Por ejemplo, creas un puntero de tipo TStrings y luego le asignas un TStringList..

La explicación es polimorfismo.


Cita:

Empezado por aguml (Mensaje 486736)
¿Por que no creas un simple AnsiString y luego haces un Add con el TStringList?

Quise ponerte un ejemplo mas completo para mostrar el potencial de la clase, pero podría haber sido:
Código PHP:

{
  
TStrings *rutas = new TStringList;

  for(
int i 015i++) rutas->Add("Ruta "+String(i));
  
ListBox1->Items->Assign(rutas);

  
delete rutas;



Cita:

Empezado por aguml (Mensaje 486736)
cuando quiera guardar una ruta supongo que seria rutas[i] pero ¿Y si quiero por ejemplo leer un caracter de una ruta en concreto? ¿Seria rutas[i][j]?

No, sería:
Código PHP:

  rutas->Strings[i][j]; 

Saludos :)

aguml 17-12-2014 09:47:48

lo de rutas[i][j] me referia en el caso de hacerlo con un AnsiString *rutas. Otra cosa, he estado leyendo que TListString tiene el metodo Delete, Count, y Clear y que para eliminar todos los miembros de la lista habia que hacer "for(int i=Lista->Count-1; i>=0;i--) Lista->Delete(i); delete Lista; Lista = null;" pero tambien he leido que el metodo Clear limpia la lista y libera toda la memoria ocupada por los miembros de la lista. Veo que tu te limitas a eliminar la lista sin eliminar antes sus miembros. ¿Puedes aclararme esos puntos por favor?

Ñuño Martínez 17-12-2014 10:43:29

La clase TListString gestiona la memoria por sí mismo. Por ejemplo, el código de su destructor es más o menos:
Código:

  ~TListString () { this->Clear (); }
Por lo tanto, no es necesario que llames al método Clear antes de destruirlo.

aguml 17-12-2014 11:56:23

ok, lo de recorrer la lista y borrar uno a uno los miembros con el metodo delete lo vi en un ejemplo donde usaba AddObject para añadir objetos a la stringlist y supongo que será por eso y que al no tener objetos no hace falta. Supongo que para el TList que tambien tengo que usar para algunos objetos si tendre que usar su metodo delete y eliminar todos los objetos antes de destruir laTList ¿No?

aguml 17-12-2014 12:05:44

por cierto, ¿Que diferencia hay entre crear TStrings *lista = new TStringList; o crear TStringList *lista = new *TStringList? ¿Que ventajas y pegas puede haber entre usar uno u otro?

ecfisa 17-12-2014 15:40:03

Hola aguml.

La ventaja de usar una variable de clase abstracta es que dicha variable/parámetro puede recibir cualquier clase derivada de la anterior.

Por ejemplo,
Código PHP:

void foo(TStream *Stream) {...} 

permite enviar cualquier clase derivada de TStream:
Código PHP:

{
  
TMemoryStream *ms;
  
TFileStream *fs;
  
TStringStream *ss;

  
foo(ms);
  
foo(fs);
  
foo(ss);
... 

Logicamente que se dispondrá como máximo, de los miembros y funciones miembros, declarados en la clase base.

En ejemplo anterior concretamente, no reporta beneficio ni perjuicio alguno. Pero, en el caso de haber necesitado del uso de la función miembro Sort(), hubiera usado:
Código PHP:

  TStrignList *sl

ya que al estar implementada en esta última, del otro modo no la habría tenido a mi disposición.

Saludos :)

ecfisa 17-12-2014 15:44:03

Hola aguml.
Cita:

Empezado por aguml (Mensaje 486751)
... Supongo que para el TList que tambien tengo que usar para algunos objetos si tendre que usar su metodo delete y eliminar todos los objetos antes de destruir laTList ¿No?

Si, así es. En el caso de TList es tu responsabilidad liberar la memoria ocupada.

Saludos :)

ecfisa 17-12-2014 16:16:59

Hola aguml.

Me olvidé de ponerte un ejemplo con TList:
Código PHP:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  
TList *= new TList;

  
GetTabOrderList(l);
  for (
int i 0l->Counti++)
    
ListBox1->Items->Add((static_cast<TControl*>(l->List[i]))->Name);

  
ShowMessage("¿ Aún ve los controles ?");

  for (
int i 0l->Counti++)
    
delete (static_cast<TControl*>(l->List[i]));
  
delete l;

  
ShowMessage("Ya no mas");


Pero recuerda que también dispones de la clase TObjectList que al igual que que la que te mencionó Ñuño te libera de la responsabilidad de liberar los objetos almacenados.

Saludos :)

aguml 17-12-2014 21:50:49

Entonces en mi caso mejor TObjectList. Tendré que ver como funciona esa.


La franja horaria es GMT +2. Ahora son las 08:26:02.

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