Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Clases Anidadas En C++

Iniciado por Marci, 11 de Marzo de 2006, 05:23:08 PM

« anterior - próximo »

Marci

 Hola a todos. A ver si me podeis aclarar una duda. Supongamos que tenemos dos clases A y B.

La clase A seria:

#ifndef AH
#define AH
//---------------------------------------------------------------------------
#include "B.h"
//---------------------------------------------------------------------------
class A
{
   B b;
};
//---------------------------------------------------------------------------
#endif


La clase B seria:

#ifndef BH
#define BH
//---------------------------------------------------------------------------
#include "A.h"
//---------------------------------------------------------------------------
class B
{
   A a;
};
//---------------------------------------------------------------------------
#endif


Si compilo esto me da el siguiente error en la clase A "Type name expected". El problema lo resuelvo haciendo una declaracion anticipada de cada clase pero eso solo me permite trabajar con punteros. Por ejemplo en la clase A puedo definir un puntero B* pero no puedo declarar un objeto B. Alguien sabe como puedo hacer para no tener que trabajar con punteros en un caso como este.

P.D. Con punteros ya me va bien, es solo por si hay una forma más sencilla y evidente que a mi se me escapa ;)  

Zaelsius

 Deberias modificar el diseño de tu aplicación para evitar ese tipo de relaciones. El compilador jamás podrá generar el código que tu quieres(sin punteros)... intenta hacer una traza mental del orden de construcción de los objetos y verás por qué :rolleyes:

Me repito: mal diseño

LC0

 Cuando te pase una cosa de estas, lo mejor que puedes hacer es tirar de declaraciones de clases adelantadas:

Citar
//Fichero A.H
#ifndef A_H
#define A_H

class B;

class A
{
    B b;
};

#endif

//Fichero B.H
#ifndef B_H
#define B_H

class A;

class B
{
    A a;
};
#endif

Y los #include "(a|b).h", déjalos para los ficheros cpp de turno.

LC0

 
Citar
El problema lo resuelvo haciendo una declaracion anticipada de cada clase pero eso solo me permite trabajar con punteros
Perdón, perdón, perdón. Debería haberme leido el post entero  :rolleyes:  

Pogacha

 Como ha dicho Zaelsius ... dejas al compilador pensando: "que vino primero?, el huevo o la gallina?"  :P

Por si no te das cuenta aun:
A tiene un B, B tiene un A, A tiene un B, B tiene un A ... etc.

Saludos.

Marci

 
Cita de: " ZaelSiuS @ 11/03/06"
Me repito: mal diseño
El caso es que me doy cuenta de que estoy trampeando el error pero como funciona :rolleyes: . Además esto de programar es como la vida misma. Una vez que coges vicios...

Imaginaros que en este caso A es un objeto 3D (CMesh) y que B en una clase (CLoader) para cargar varios tipos de archivo (BMP, TGA, OBJ, 3DS...). Ahora lo estoy solucionado con las clases anidadas de la siguiente forma:

CMesh mesh;
CLoader loader;

loader.load(string file, (void*)mesh.this);


Como lo enfocariais vosotros?

tamat

 Pedazo de declaración recursiva. Lo dicho anteriormente, es un problema de diseño, deberias plantearte reestructurar, todos nos hemos encontrado con ese problema alguna vez.
Por un stratos menos tenso

zupervaca

 CMesh no deberia de saber que existe una CLoader, pero CLoader si deberia de saber como trabajar con CMesh ya que CMesh se deberia de poder crear y modificar sin CLoader, ejemplo:

Fichero CMesh

class CMesh
{
CMesh(int triangulos, int vertices,...)
{
...
}
}


Fichero CLoader

#include "CMesh.h"
class CLoader
{
CMesh *leer(char* nombre)
{
int triangulos, vertices;
...
return new CMesh(triangulos, vertices,...);
}
}

Sacrifai

 
Cita de: "Marci"
CMesh mesh;
CLoader loader;

loader.load(string file, (void*)mesh.this);


Como lo enfocariais vosotros?
Supongo que ese es un buen método, pero también podrias hacer que mesh fuese un puntero y entoces podrías hacer algo así:

mesh = loader.load(string file);

Pero si tu método te da buenos resultados...para que cambiar.

Se me adelantó zupervaca  (rules) .

Marci

 
Cita de: " zupervaca @ 11/03/06"
CMesh no deberia de saber que existe una CLoader, pero CLoader si deberia de saber como trabajar con CMesh ya que CMesh se deberia de poder crear y modificar sin CLoader
Ahi esta mi mal diseño :angry: . Todas las clases que necesitan cargar datos de un archivo llevan una funcion load desde la que se llama a CLoader. El codigo completo seria asi:

void __fastcall CMesh::Load(AnsiString asFile)
{
   // Cargamos los datos en la malla
   CLoader Loader;
   Loader.Load(asFile, this);

   // Conseguimos un identificador para el buffer object
   m_CGLExtensions.glGenBuffersARB(1, &m_iId);

   // Creamos el buffer object
   m_CGLExtensions.glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, m_iId);

   // Cargamos los datos en el buffer object
   m_CGLExtensions.glBufferDataARB();
}


El mesh = loader.load(string file) ya lo pense pero como estoy dentro de la clase tendria que ser this = loader.load(string file) y no se puede

Sacrifai

 Yo creo que lo mejor sería que el tema de calgar el mesh no fuese un método de la clase CMesh, sino que lo hiciese un ResourceManager o algo así.

zupervaca

 Para este tema es mejor tener tres clases como minimo, malla, lectores de formato de mallas y administrador de recursos, la malla no conoce a nadie, los lectores de formato de mallas conoce solo a la clase malla y el administrador de recursos conoce a todos los formatos de mallas, este ultimo se puede hacer que los conozca estaticamente o dinamicamente, es decir, que los conozca por que los hemos implementado directamente dentro de la clase o los conozca por que tengamos una clase base para todos los lectores que se entiende con el administrador de recursos, tipo plugin, este ultimo sin duda es la mejor eleccion.

AK47

 Eso no es una clase anidada, sino una dependencia cíclica. Y no, no puede solucionarse sin punteros (no se si se puede con referencias), porque cuando haces
A a;

El compilador necesita saber cuanto ocupa un objeto de tipo A, y no puede saberlo de la forma en que lo tienes. En cambio, un puntero a cualquier tipo siempre ocupa igual, por eso se permite declarar punteros de clases que no están "todavía" definidas, pero que "existirán", que es lo que le dices cuando pones cosas como "class A;".

De paso, una clase anidada sería algo así:

class A
{
 class B
 {
 };
};


gdl

 
Cita de: "AK47"Y no, no puede solucionarse sin punteros (no se si se puede con referencias)
Sí se puede solucionarse con referencias. El código sería:

class A;
class B
{
public:
 B(A & padre) : a(padre) {}
 A & a;
};

class A
{
public:
 A(): b(*this) {}
 B b;
};


Te dará posiblemente un warning porque estás usando una referencia a this en la lista de inicialización de un constructor pero si no se usa la referencia en el constructor, funiona.

La solución es asimétrica ya que ambos objetos se tienen que construir a la vez y eso se hace construyendo A que contiene a B.

josette

 Yo utilizaría y de hecho lo hago asi la siguiente estructura:

Cmesh.h:
class Cmesh
{
}

CmeshLoader.h:
#include "Cmesh.h"
class CmeshLoader
{
  bool load(const char*,Cmesh&);
};

Cmesh no tiene porque saber del loader. Pero loader si tiene que saber como leer a Cmesh.

Otra cosa es donde se almacene el mesh, que como se ha comentado sera cosa de un manager

de forma que:
CmeshMgr.h:
#include "Cmesh.h"
#include "CmeshLoader.h"
typedef std::vector TVec_Mesh;
class CmeshMgr
{
  TVec_mesh m_Vec_Mesh;

  bool load(const char* fileMesh)
  {
     Cmesh Mesh;

     CmeshLoader ML;
     if (!ML.load(fileMesh,Mesh))
     {
        return (false);
     }

     m_Vec_Mesh.push_back(Mesh);
  }
}

Se puede mejorar haciendo
typedef std::vector TVec_mesh;

asi no hay que hacer tantas copias...  espero que te ayude.







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.