Saludos,
tras una dura reorganización de código de mi juego en curso, he llegado al problema de como controlar errores de memoria en los construtores de copia. No he tenido que usar memoria dinamica en constructores porque se añaden objetos en tiempo real (pej: tile.addUnit()) o son compartidos (pej: unidad->equipo), pero el problema imagino que es el mismo.
Según he entendido, en C++ los constructores no devuelven nada, así que no puedo poner "return NULL". Qué hacer entonces si al reservar memoria dinámica en un constructor falla?
El código es para copiar uno de los mapas precargados al puntero de partida a jugar, que debería ser el que se modifica.
Map(const Map& ref) {
int i, j;
// Do not copy premap or minimap
preMap = NULL;
miniMap = NULL;
// Copy players
for(i=0; i<TEAMS_NUM; i++)
teams[i] = ref.getTeams(i);
w = ref.getW();
h = ref.getH();
// Fallara?¿?¿?
board = new Tile* [h];
for (i=0; i<h; i++) {
board[i] = new Tile[w];
}
... // mas codigo para copiar las unidades
}
try {
board = new Tile* [h];
for (i=0; i<h; i++) {
board[i] = new Tile[w];
}
} catch (std::bad_alloc) {
y ahora que?
for(i) board[i] = NULL; ?
board = NULL; ?
}
y mirar en lo copiado si board == NULL?
o directamente olvidar el constructor de copia y utilizar otra función:
Map* Map::makeCopy(Map* orig);
Gracias de antemano.
Lo más correcto seria capturar la excepción, avisar de que falta memoria y cerrar el programa. Normalmente, si falla la reserva de memoria es que algo va mal.. no tendria mucho sentido continuar la ejecución... ¿no?
El control de errores es bueno, pero sin llegar a lo enfermizo (uoh)
Uhm, cierto, creo que un fatalError(FE_MEM_ERR); irá de perlas.
Tx.
Dos cosas:
- no uses excepciones (refencia: more effective c++)
- no usas new o delete en constructores
El último punto es la típica recomendación aunque la verdad un new de un objeto que no suponga gran consumo de memoria no hace daño a nadie. Yo muchas veces uso el típico IsOk para saber si va todo bien.
Por último lo del fatalError aunque suene cutre porque jode todas y cada una de las buenas costumbres de programador es una muy buena solución porque me pregunto yo que qué e puede hacer después de que no puedas reservar memoria? XD
un saludo
Cita de: "ethernet"- no usas new o delete en constructores
entonces que se hace con punteros? todas las clases llenas de metodos init o set?
gracias por las recomendaciones.
CitarPor último lo del fatalError aunque suene cutre porque jode todas y cada una de las buenas costumbres de programador es una muy buena solución porque me pregunto yo que qué e puede hacer después de que no puedas reservar memoria? XD
quizas un sleep(numero aleatorio), tirar algun buffer o algo que se pueda tirar (esto es.. liberar algo de memoria) y luego volver a intentar la peticion, si vuelve a fallar, se hace otro sleep( otro numero aleatorio x 2 ) ... hasta que te aburres, no te queda nada mas que liberar, asi que sales lo mas dignamente posible.
lo del sleep es porque igual alguna aplicacion esta monopolizando toda la memoria o todo el disco duro momentaneamente.
aun otra solucion es no dejar que nuestro programa arranque cuando hay menos espacio libre que memoria instalada, pero esto es una chuleria, por supuesto :D
Cita de: "Warchief"Cita de: "ethernet"- no usas new o delete en constructores
entonces que se hace con punteros? todas las clases llenas de metodos init o set?
gracias por las recomendaciones.
Todo lo susceptible de fallar debe estar monitorizado, a nadie se lo ocurriría abrir un archivo e intertar leer sin antes haber comprobado su apertura, pues con esto igual.
Lo que está claro es que los extremismos no van ninguna parte y no creo que algo así cree problemas a nadie
template <class T>
class foo
{
Object* _obj;
public:
foo():
_obj(new T)
{}
...
};
(siempre que T no sea una clase MemoryManager.. o algo así :P)
un saludo
Uhm, templates, ese gran desconocido para mí. No he tenido tiempo de mirarme ese tema, quizá deba echarle un vistazo. Gracias por la recomendación.
El template lo he puesto para fardar.. :P
jasjasjas, pensé que era por el template el asunto. Ok, meter el new en las preasignaciones del constructor (no he encontrado un nombre mejor para 'preasignaciones'). Y ya que estamos, la diferencia entre eso y hacer el new dentro del constructor es ... ¿?
Pues que en esa llamada estoy llamando al construcor explícitamente del objeto y si lo hicieras en el constructor el objeto ya estaría construído y estarías asignándolo. No tiene mucho sentido si es un puntero, pero mira este caso:
class foo
{
public:
foo(int a);
};
class wee
{
foo _var;
wee() //ERROR de compilación
{}
};
class wee
{
foo _var;
wee() //ERROR de compilación
{
_var(3); //no tiene sentido, error
}
};
class wee
{
foo _var;
wee():
_var(3) //OK (ole)
{}
};
espero que te sirva, un saludo
Uhm, interesante, sí. Tx