Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Duda memoria dinámica vs estática

Iniciado por Gallo, 05 de Diciembre de 2011, 12:25:47 PM

« anterior - próximo »

Gallo

Buenas, estoy intentando realizar un port de una lib de Objective C a C++, en Objective C prácticamente para todo lo que son objetos se utilizan punteros y los objetos se alojan siempre de forma dinámica (es como en Java pero aquí se pone el * siempre). Al ir pasando a C++ muchas de estas cosas las puedo ir poinendo en memoria estática pero realmente no se cuando es bueno que sea estático o dinámico, por ejemplo, supongamos que tenemos una clase evento con un nombre.


class Event{
std::string name;
};


En Objective-C seria  NSString* name;, pero al pasarlo a C++ puedo o bien hacer std::string name; o bien std::string* name; y este tipo de cosas se repite constantemente. Supongamos otros casos:


class EventHandler{
std::map<std::string,Event> eventListeners;
};

class Coord{
int x,y;
};

class Actor{
Coord posicion;
};


Aquí me vuelve a pasar lo mismo, el pripio map deberia ser un puntero? y los tipos clave y valor? O en el caso del actor, la posición podria calcularse en otro método y pasarsela al Actor, bien por referencia o bien como puntero.
Tengo claro que las variables locales de una función las perderé y que tengo que guardar una copia o bien alojar dinámicamente y guardar una copia del puntero, eso sabria mantenerlo correctamente, el problema es que no se cual de las dos opciones es la mas conveniente, tanto por rendimiento como por ser mas o menos propensa a fallos.

¿Es malo mantener la memoria dinámica bajo mínimos e intentar que todo se aloje de forma estática? Yo prefiero usar la memoria dinámica lo mínimo posible para poder mantener el control con Pools y Stacks propios sin demasiado esfuerzo. También tengo claro que para la carga de archivos como texturas o modelos usaria la dinámica claro, ahí no hay duda. Hablamos de una lib de game engine para gestionar escenas, eventos, etc.

Saludos.

Warchief

Declarar los miembros como variables no implica que se asignan a memoria estatica, se asignan en el espacio de memoria de la instancia.
Por ejemplo, para el caso del actor:


class Coord{
int x,y;
};

class Actor{
Coord posicion;
};

void main()
{
Actor a; // x e y estan en memoria estatica porque 'a' esta en memoria estatica
Actor* b = new Actor(); // x e y estan en memoria dinamica porque 'b' esta en memoria dinamica
}


Hecha esa aclaracion:

+ Utiliza variables en vez de punteros cuando el miembro "pertenece" a la instancia, y no planeas cambiar donde esta alojado en memoria durante la vida del objeto.
+ Usa punteros cuando el objeto no pertenece a la instancia, quieres apuntar a distintos objetos durante la vida de dicho objeto contenedor, o el valor puede ser nulo.
+ Usa referencias en vez de punteros si puedes asignar el valor "del puntero" en el constructor, no es nulo, y la memoria apuntada nunca cambia.


class Coord {
int x,y;
};

class Actor {
Coord posicion;  // siempre quieres tener un par de coordenadas
        Actor* parent; // el padre no pertence al actor, puede cambiar y puede ser nulo

        Actor() : parent(0) { }
};

void main()
{
Actor a; // x e y estan en memoria estatica, al igual que el puntero 'parent' (los x bytes que ocupa el puntero). Sin embaro, parent no apunta a nada
Actor* b = new Actor(); // x e y estan en memoria dinamica, al igual que el puntero 'parent'. Sin embaro, parent no apunta a nada

  // suponiendo que parent es public
  a.parent = b; // el puntero 'parent' en a apunta a b. Nada ha cambiado en memoria excepto el valor en el puntero 'parent'
}


En el caso del map. Si miras la implementacion por defecto del map que uses, te encontraras con un allocator en la declaracion:

template<class _Kty, class _Ty, class _Pr = less<_Kty>, class _Alloc = allocator<pair<const _Kty, _Ty> > > class map ...

Cuya implementacion por defecto es:

return ((_Ty _FARQ *)::operator new(_Count * sizeof (_Ty)));


En otras palabras, los objetos alojados en el map hace una copia en memoria dinamica.


class EventHandler{
std::map<std::string,Event> eventListeners;
};


El map en EventHandler ocupa siempre lo mismo; al anyadir objetos, el map se encarga de reservar la memoria dinamica necesaria.

Finalmente ¿Es malo mantener la memoria dinámica bajo mínimos e intentar que todo se aloje de forma estática?
La memoria dinamica (heap) es lenta pero hay mucha mas disponible, la estatica (stack) rapida pero limitada. En un juego o lib para juegos, los objetos (y por extension sus variables, sean punteros o no) se asignan en memoria dinamica.

En general, lo que vas a asignar en memoria estatica (stack) son las variables locales de la funcion que esta ejecutando:

// ejemplo: dist y bIsClose se alojan en stack
bool IsClose( const Actor& a, const Actor& b )
{
  float dist = (a.position - b.position).size();
  bool bIsClose = ( dist < 10 );
  return bIsClose;
}


Gallo

No había pensado en la primera aclaración que has dicho, en el fondo si tengo una clase Game, y todo está como variables y no como punteros dentro de Game, pero en el main hago un Game* g = new Game(); estoy metiendo todo eso en el heap, salvo como has dicho las variables locales de las funciones/métodos.

Las reglas sobre la propiedad del objeto me parecen muy adecuadas, ya le estaba dando vueltas a algo similar, normalmente el puntero es mas útil cuando te interesa tener una referencia al objeto pero que no forme parte del objeto que lo está usando, es exactamente la norma que necesitaba.

Ocurre que trabajo diariamente con Java y como ya he dicho lo que hago es un port de Objective-C, como en ambos lenguajes TODO son referencias al objeto alojado dinámicamente no sabia como traducirlo adecuadamente al C++ donde tengo menos experiencia y existen las dos formas de hacerlo, seguro que esto le tiene que pasar a mas de uno.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.