Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





C++ y POO

Iniciado por sukhur, 05 de Enero de 2007, 10:12:24 AM

« anterior - próximo »

Pogacha

Bueno ... saludos, con el mismo respeto.

Zaelsius

No os sulfureis, al fin y al cabo C++ no es un lenguaje orientado a objetos puro, y no siempre existe una manera claramente más correcta que otra para hacer ciertas cosas...

(Leyendo el resto del hilo..)

Bueno sí, en este caso lo tengo claro: lo mismo que Tamat.

fiero

Cita de: "tamat"Yo lo que hago es tener un método abstracto en la clase base que se llame GetType() que retorne un int, y luego hago un enum o unos defines para asignar un valor a cada clase.

Así cuando quieras hacer algo propio de camion hago:


if ( vec[0]->GetType() == CLASS_CAMION)
   ((Camion*)vec[0])->GetCM3();


Esta solución la he visto en muchos sitios y me parece la mejor, luego puedes activar el RTTI que sirve para preguntar de qué tipo es una instancia en C++ pero no se muy bien como va.

Yo tambien utilizo este método, aunque sólo lo he usado con structuras, en las que siempre hay un campo "tipo" en la misma posición de la estructura. El método de tener una función getType() en la clase base me parece muy limpio, y no es demasiado engorroso.

un saludo
www.videopanoramas.com Videopanoramas 3D player

tamat

Cita de: "Fran"Segun yo, defines metodo abstracto en clase figuras Area
y luego en cada clase hija pones su método de calculo . Metodo a todas luces (para mi) mas corto, mas racional y mas facilmente legible y escalable. Por ejemplo, si quieres insertar un nuevo tipo de vehiculo, con tu metodo hay que declararlo en esos IF (si tienes N propiedades y 3 vehiculos nuevos), 3N ifs nuevos, con el mio no tienes q hacer nada.

Fran, no mees fuera de tiesto, el ejemplo que tu pones del area no tiene nada que ver, si ese fuese el caso yo tendría un método area que cada uno implementaria pero porque supondré todas las figuras tienen area, sin embargo en su caso habla de propiedades que SOLO una de las clases derivadas posee. El sistema que propones no solo rompe con la POO por todos lados sino que añade un overhead a cada instancia por cada método virtual que "podria tener una clase derivada".

Ciñiendome a las necesidades que él planteaba en su post (acceder a un método especifico de una clase derivada) yo le digo cómo se suele hacer. Pero como muy bien han dicho pogacha o bnl el problema es que dicho requisito ya parte de un mal diseño (aunque existen muchos casos donde se da).
Por un stratos menos tenso

Fran

Cita de: "tamat"
Cita de: "Fran"Segun yo, defines metodo abstracto en clase figuras Area
y luego en cada clase hija pones su método de calculo . Metodo a todas luces (para mi) mas corto, mas racional y mas facilmente legible y escalable. Por ejemplo, si quieres insertar un nuevo tipo de vehiculo, con tu metodo hay que declararlo en esos IF (si tienes N propiedades y 3 vehiculos nuevos), 3N ifs nuevos, con el mio no tienes q hacer nada.

Fran, no mees fuera de tiesto, el ejemplo que tu pones del area no tiene nada que ver, si ese fuese el caso yo tendría un método area que cada uno implementaria pero porque supondré todas las figuras tienen area, sin embargo en su caso habla de propiedades que SOLO una de las clases derivadas posee. El sistema que propones no solo rompe con la POO por todos lados sino que añade un overhead a cada instancia por cada método virtual que "podria tener una clase derivada".

Ciñiendome a las necesidades que él planteaba en su post (acceder a un método especifico de una clase derivada) yo le digo cómo se suele hacer. Pero como muy bien han dicho pogacha o bnl el problema es que dicho requisito ya parte de un mal diseño (aunque existen muchos casos donde se da).

:) Yo no meo fuera del tiesto. Simplemente digo que esa forma que he expuesto es bastante usada. De hecho esa forma de declarar métodos aunque luego no los uses es en lo que se basa los interfaces de Java por ejemplo. Da mucha mas flexibilidad. Disminuye código (como por ejemplo podrás ver en www.kbcafe.com/articles/OOP.Concepts.pdf (mira abstraction)) y sobre todo da claridad al mismo sin tener que retocar el código cada vez q te cambian cualquier cosa o quieres introducir un nuevo tipo de clase. Vamos . Para mi no solo no está en contraposicion a la OOP sino que es una manera inteligente de usarla. Y si está en contra encuentrame un sitio donde diga q está en contra xq yo me he molestado incluso en mirar mi olvidado "Manual de Referencia C++ con anotaciones" de STROUSTRUP  (Addison-Wesley) y lo único q sí he encntrado es que no le gusta hacer exactamente lo q tú haces. Página 247 , clases derivadas :
[..]
Segundo: Si se proporciona una forma sencilla , conveniente de preguntar de qué tipo es un objeto, se pondría en marcha un estilo de programación que se apoyaría en tener un campo de tipo o preguntar que tipo, mas que en el uso de funciones virtuales. Cuando se tomo la decision de no incluir en C++ esa posibilidad (q x lo q veo no es de C++ basico) ejerció mucha influencia el hecho de saber que, en los programas de Simula, este estilo condujo a que el código resultase confuso y no modular.
Por otro lado, la ausencia de tal utilidad hace algunas tareas bastante mas dificiles - como la entrada/salida de objetos- porque el usuario se ve obligado a construirse mecanismos propios....
[...]

Creo que si el creador de C++ consideró q no era necesario el mecanismo de RTTI sino que era mejor usar métodos virtuales, por algo será. De todos modos creo q esta discusion se basa simple y llanamente en falta de experiencia. Es evidente cuando has tirado mas de 2,5 millones de lineas de código (y no hace falta que venga a decirlo STROUSTRUP , ni que lenguajes como Java implementen algo similar en los interfaces (con toda la sobrecarga que supone esto), ni el ejemplo del enlace de las piernas y lo que pone debajo) que una cosa
es mucho mucho más flexible y clara que la otra.

Por cierto es la edición del 94. Cuando yo usaba C++.

Diferencial

Creo que existe una confusión porque tanto lo que dice fran como lo que dice tamat esta en lo cierto. Es decir, para metodos comunes a las clases hijas lo metes en la clase base como virtual puro y lo redefines en las clases hijas. Pero para metodos propios de la clase hija es decir el ejemplo que ha dado
sukhur, tienes que hacer obligatoriamente el cast si es que los quieres meter todos dentro de un vector. Pero la mejor respuesta es la de bnl ya que creo que lo que quiere hacer sukhur esta mal planteado, deberias de separar los objetos o hacerte una estructura mejor para evitar esos casts.
PARA TENER COSAS QUE NUNCA HAS TENIDO, TENDRÁS QUE HACER COSAS QUE NUNCA HAS HECHO.

sukhur

Hola de nuevo, como ya había dicho es un ejercicio inventado y claro, me ha salido de tal manera que no tiene solución si atiendo a lo que me han explicado en clase segun veo.

Gracias a todos.

fiero

Fran, lo que explicas tiene mucho sentido y está claro que es bastante lógico. Estoy de acuerdo que para el ejemplo planteado por sukhur es una solución válida. Sin embargo, en la práctica esto no se dá casi nunca.

Por ejemplo, en las MFC (no estoy diciendo que las MFC sean ningún ejemplo a seguir), existe la clase CWnd (ventana genérica) de la que derivan otros objetos como combos, botones, listas, dialogos... Hay muchas funciones de la biblioteca que devuelven un puntero a CWnd y es el programador el que debe saber de que tipo de objeto se trata, ya que la clase base no tiene tooodas las funciones virtuales de tooodas las posibles funciones de las clases derivadas.

un saludo
www.videopanoramas.com Videopanoramas 3D player

zupervaca

Mi granito de arena para ayudarte:

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <conio.h>

class Vehiculo
{
public:
Vehiculo( const char* matricula, const char* fecha, int precio, const char* motor )
{
this->matricula = matricula;
this->fecha = fecha;
this->motor = motor;
this->precio = precio;
}

const char* getMatricula()
{
return this->matricula;
}

const char* getFecha()
{
return this->fecha;
}

int getPrecio()
{
return this->precio;
}

const char* getMotor()
{
return this->motor;
}

virtual const int getType() = 0;

private:
const char* matricula, *fecha, *motor;
int precio;
};

class Moto : public Vehiculo
{
public:
Moto( const char* matricula, const char* fecha, int precio, const char* motor, int cm3, int velpunta )
: Vehiculo( matricula, fecha, precio, motor )
{
this->cm3 = cm3;
this->velpunta = velpunta;
}

int getCm3()
{
return this->cm3;
}

int getVelPunta()
{
return this->velpunta;
}

const int getType()
{
return Moto::Type;
}

static const int Type;

private:
int cm3, velpunta;
};
const int Moto::Type = (const int)(__int64)&Moto::Type;

class Camion : public Vehiculo
{
public:
Camion( const char* matricula, const char* fecha, int precio, const char* motor, const char* pma, int longitud )
: Vehiculo( matricula, fecha, precio, motor )
{
this->pma = pma;
this->longitud = longitud;
}

const char* getPma()
{
return this->pma;
}

int getLongitud()
{
return this->longitud;
}

const int getType()
{
return Camion::Type;
}

static const int Type;

private:
const char* pma;
int longitud;
};
const int Camion::Type = (const int)(__int64)&Camion::Type;

int main()
{
const int numVehiculos = 4;
Vehiculo* v[numVehiculos];
v[0] = new Camion( "B-23-AZ", "19/12/2005", 100000, "4 Valvulas", "PMA1", 500 );
v[1] = new Moto( "W-743-TH", "2/1/2007", 1000, "Pegasus", 500, 150 );
v[2] = new Moto( "X-364-YT", "5/6/2005", 1000, "Cañero", 500, 150 );
v[3] = new Camion( "A-7453-HC", "17/5/2006", 100000, "20 Valvulas", "PMA16", 600 );

char sz[512] = "";
for( int n = 0; n < numVehiculos; n++ )
{
Vehiculo* ve = v[n];
if( ve != NULL )
{
if( ve->getType() == Camion::Type )
{
Camion* camion = (Camion*)ve;
sprintf( sz, "n=%i, tipo=camion, getType=%i, idtipo=%i, pma=%s\r\n",
n, camion->getType(), Camion::Type, camion->getPma() );
}
if( ve->getType() == Moto::Type )
{
Moto* moto = (Moto*)ve;
sprintf( sz, "n=%i, tipo=moto, getType=%i, idtipo=%i, cm3=%i\r\n",
n, moto->getType(), Moto::Type, moto->getCm3() );
}
printf( sz );
delete ve;
}
}

getch();

return 0;
}

Lo unico que te puede interesar es como crear un tipo para cada clase unico sin tener que especificarlos, es decir, evitar que se pueda repetir un mismo tipo sin tener que conocer los demas.

Suerte :wink:

shephiroth

Me vais a perdonar, pero creo que mucha gente esta viciada de dar soluciones chapuzas en problemas mas complejos y se han olvidado de la teoria basica de POO.

Lo del getType es un apaño mas bien feo que se suele usar cuando se tienen mil tipos de objetos y la base madre existe solo para poder vectorizarlos de mala manera. Desde mi punto de vista lo mas normal seria para imprimir los datos crear un virtual mostrar y luego codificarla en cada clase como se maneja individualmente. Para acceder a sus variables obligaria a usar un cast, que quien quiera acceder que sepa pq accede. Si lo unico q quieres es mostrar los datos con el virtual mostrar sobra.

Si quieres hacer cosas mas elaboradas deberas crearlas siempre en funciones virtuales y que hereden (ejemplo mover, cargar, encender, etc...).

Insisto, lo del getType me parece un apaño chapuza que os habeis enviciado por otros problemas.

Zaelsius

Cita de: "shephiroth"Lo del getType es un apaño mas bien feo que se suele usar cuando se tienen mil tipos de objetos y la base madre existe solo para poder vectorizarlos de mala manera.

Vale, a ver cómo harías tú esto:

- Tenemos una colección de formularios formados por widgets de múltiples tipos. Clase base "Widget", clases derivadas "Botón","Lista","CheckBox" etc. Toda la funcionalidad codificada en C++.

- Por otro lado tenemos un script en Lua/Python/Loquesea que accede a uno de los widgets, llamado "dificultad" y del tipo "Lista", para preguntarle por el ítem actualmente seleccionado. ¿Pero cómo sabemos que el widget "dificultad" efectivamente es del tipo "Lista" y que por tanto ofrece el método deseado? Pues o haces un getType() y lo compruebas, o usas un dynamic_cast<> y capturas la excepción, o usas el operador typeof.

No es ninguna chapuza, es que no hay otra manera de hacer las cosas cuando no sabes de antemano de qué tipo es la instancia que estás tratando, y necesitas algo diferente de ella.

En Java tienes instanceof y en otro lenguajes modernos hay mecanismos similares. Si en C++/juegos no se usa RTTI más a menudo, y se usan los getType "a mano", es por evitar la más mínima sobrecarga y poder robar algunos ciclos más a la CPU... pero eso ya es otra historia.

Citarquien quiera acceder que sepa pq accede
¿Nos fiamos del programador? ¿Y la gestión de errores? ¿Dejamos que el programa explote o lo dejamos fallar en 'silencio' a pesar de las consecuencias posteriores?

zupervaca

Entonces segun tu... ¿como obtendrias los cm3 de un objeto moto ya que hay mas programadores en tu empresa y no sabes si lo van a necesitar para realizar alguna operacion en la que no has pensado? sabiendo claro que la lista puede contener camiones u otros tipos de vehiculos.

Nota: Es que si sabes el tipo de objeto con el que trabajas no hace falta llamar a la funcion getType, pero nunca esta de mas poner alguna forma de identificar la clase, ademas el c++ y otros lenguajes vienen con esta funcionalidad, con lo que tal mal como solucion no puede ser :?

swapd0

Segun los libros, si estas usando RTTI no estas programando al estilo C++, yo, y supongo que todos, lo hemos tenido que usar alguna vez...

De todas formas, ¿por que no te planteas el diseño? no el de las clases, pero podrias hacer una clase que hiciera de contenedor, y al añadirle una moto o camion, lo meta en listas distintas, asi no tendrias problemas de llamar a una funcion que esta implementada solo en una de las clases.

Zaelsius

@Swapd0: Si coges un libro de C++ nuevo(p.ej. The C++ Programming Language, Stroustrup, 2002) verás que habla del RTTI y presenta casos donde es necesario su uso. Habla del typeid, el dynamic_cast, etc..

No entiendo cómo usar un contenedor "inteligente" con N listas soluciona el problema. Le pides al contenedor que te devuelva la instancia X y él te la da. Sigues sin saber su tipo. O bien le pides que te devuelva la instancia X del tipo Y, y él comprobará que efectivamente está en la lista Y. ¿Pero y si no está? De nuevo otra comprobación de error. No ganas nada dejando la comprobación de tipo al contenedor, y complicas el sistema innecesariamente.

La comprobación de tipos es necesaria e ineludible en algunos casos. No existe ningun patrón de diseño mágico que lo solucione.

Fran

Cita de: "shephiroth"Me vais a perdonar, pero creo que mucha gente esta viciada de dar soluciones chapuzas en problemas mas complejos y se han olvidado de la teoria basica de POO.

Lo del getType es un apaño mas bien feo que se suele usar cuando se tienen mil tipos de objetos y la base madre existe solo para poder vectorizarlos de mala manera. Desde mi punto de vista lo mas normal seria para imprimir los datos crear un virtual mostrar y luego codificarla en cada clase como se maneja individualmente. Para acceder a sus variables obligaria a usar un cast, que quien quiera acceder que sepa pq accede. Si lo unico q quieres es mostrar los datos con el virtual mostrar sobra.

Si quieres hacer cosas mas elaboradas deberas crearlas siempre en funciones virtuales y que hereden (ejemplo mover, cargar, encender, etc...).

Insisto, lo del getType me parece un apaño chapuza que os habeis enviciado por otros problemas.

Totalmente de acuerdo. El propio Ellis Stroustrup se negó a meterlo en el Ansi C++ inicial xq para eso estan los virtuales como he transcrito arriba de su famoso libro






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.