Pregunta

Parece que había un malentendido fundamental sobre C++ :<

Me gusta la polimórficos contenedor de la solución.Muchas gracias, por traer a mi atención :)


Por lo tanto, tenemos una necesidad de crear una relativamente genérico tipo de contenedor de objetos.También sucede para encapsular algunos negocios relacionados con la lógica.Sin embargo, necesitamos almacenar arbitraria de datos en este contenedor - de todo, desde tipos de datos simples a complejas clases.

Por lo tanto, uno podría saltar inmediatamente a la idea de una clase de plantilla y hacer con ella.Sin embargo, me he dado cuenta de C++ polimorfismo y las plantillas no juegan bien juntos.Es que hay alguna lógica compleja que vamos a tener que trabajar, prefiero seguir con cualquiera de las plantillas O polimorfismo, y no intenta luchar contra el C++ por lo que es hacer ambas cosas.

Por último, dado que yo quiero hacer uno o el otro, yo prefiero el polimorfismo.Me resulta mucho más fácil para representar restricciones como "este envase contiene tipos Comparables" - a la de java.

Me trae al tema de la pregunta:En la mayoría de resumen, me imagino que podría haber un "Contenedor" puro interfaz virtual que tiene algo parecido a "empujar(void* data) y pop(void* data)" (para el registro, no estoy realmente tratando de implementar una pila).

Sin embargo, yo realmente no se como void* en el nivel superior, por no hablar de la firma va a cambiar cada vez que quiero agregar una restricción para el tipo de datos de un contenedor de hormigón puede trabajar.

Resumiendo:Hemos relativamente complejo de contenedores que tienen distintas maneras de recuperar los elementos.Queremos ser capaces de variar las restricciones sobre los elementos que pueden entrar en los contenedores.Elementos que deben trabajar con varios tipos de contenedores (siempre que se cumplan las restricciones de que cada contenedor).

Editar:También debo mencionar que los contenedores deben ser polimórficos.Que es mi principal razón para no querer usar con plantillas de C++.

Así que debo dejar a mi amor para Java tipo de interfaces y vaya con las plantillas?Debo usar void* y estáticamente dejar todo?O debo ir con una clase vacía definición de "Elemento" que declara nada y usar eso como mi clase de nivel superior en el "Elemento" de la jerarquía?

Una de las razones por las que me encanta desbordamiento de la pila es que muchas de las respuestas que se proporcionan algunas visión interesante sobre otros enfoques que yo no había ni siquiera han considerado.Así que gracias de antemano por sus opiniones y comentarios.

¿Fue útil?

Solución

¿No puede tener una clase de Contenedor raíz que contenga elementos:

template <typename T>
class Container
{
public: 

   // You'll likely want to use shared_ptr<T> instead.
   virtual void push(T *element) = 0;
   virtual T *pop() = 0;
   virtual void InvokeSomeMethodOnAllItems() = 0;
};

template <typename T>
class List : public Container<T>
{
    iterator begin();
    iterator end();
public:
    virtual void push(T *element) {...}
    virtual T* pop() { ... }
    virtual void InvokeSomeMethodOnAllItems() 
    {
       for(iterator currItem = begin(); currItem != end(); ++currItem)
       {
           T* item = *currItem;
           item->SomeMethod();
       }
    }
};

Estos contenedores se pueden pasar polimórficamente:

class Item
{
public:
   virtual void SomeMethod() = 0;
};

class ConcreteItem
{
public:
    virtual void SomeMethod() 
    {
        // Do something
    }
};  

void AddItemToContainer(Container<Item> &container, Item *item)
{
   container.push(item);
}

...

List<Item> listInstance;
AddItemToContainer(listInstance, new ConcreteItem());
listInstance.InvokeSomeMethodOnAllItems();

Esto le brinda la interfaz de Contenedor de forma genérica con seguridad de tipo.

Si desea agregar restricciones al tipo de elementos que pueden estar contenidos, puede hacer algo como esto:

class Item
{
public:
  virtual void SomeMethod() = 0;
  typedef int CanBeContainedInList;
};

template <typename T>
class List : public Container<T>
{
   typedef typename T::CanBeContainedInList ListGuard;
   // ... as before
};

Otros consejos

Puede usar un contenedor estándar de boost: : any si está almacenando datos verdaderamente arbitrarios en el contenedor.

Parece que preferiría tener algo como un boost :: ptr_container donde cualquier cosa que pueda almacenarse en el contenedor tiene que derivarse de algún tipo de base, y el contenedor solo puede darle referencias al tipo de base.

Lo simple es definir una clase base abstracta denominada Container, y subclase para cada tipo de elemento que desee almacenar.A continuación, puede utilizar el estándar de cualquier clase de colección (std::vector, std::list, etc.) para almacenar punteros a Container.Ten en cuenta que, puesto que usted va a almacenar punteros, usted tiene que manejar su asignación/desasignación.

Sin embargo, el hecho de que usted necesita en una sola colección para almacenar objetos de tan salvajemente diferentes tipos es un indicio de que algo puede estar mal con el diseño de su aplicación.Puede ser mejor para revisar la lógica de negocio antes de implementar esta super-contenedor genérico.

El polimorfismo y las plantillas funcionan muy bien juntos, si los usa correctamente.

De todos modos, entiendo que desea almacenar solo un tipo de objetos en cada instancia de contenedor. Si es así, use plantillas. Esto evitará que almacene el tipo de objeto incorrecto por error.

En cuanto a las interfaces de contenedor: Dependiendo de su diseño, tal vez también pueda hacerlas con plantillas, y luego tendrán métodos como void push(T* new_element). Piense en lo que sabrá sobre el objeto cuando quiera agregarlo a un contenedor (de un tipo desconocido). ¿De dónde vendrá el objeto en primer lugar? ¿Una función que devuelve void*? ¿Sabes que será comparable? Al menos, si todas las clases de objetos almacenados están definidas en su código, puede hacer que todas hereden de un antepasado común, digamos, Storable, y use Storable* en lugar de void push(Storable* new_element).

Ahora, si ve que los objetos siempre se agregarán a un contenedor mediante un método como <=>, entonces realmente no habrá valor agregado al convertir el contenedor en una plantilla. Pero entonces sabrás que debería almacenar Storables.

En primer lugar, las plantillas y el polimorfismo son conceptos ortogonales y juegan bien juntos. A continuación, ¿por qué quieres una estructura de datos específica? ¿Qué pasa con las estructuras de datos STL o boost? ) no funciona para usted.

Dada su pregunta, parece que estaría haciendo un mal uso de la herencia en su situación. Es posible crear & Quot; restricciones " en lo que va en sus contenedores, especialmente si está utilizando plantillas. Esas restricciones pueden ir más allá de lo que su compilador y enlazador le darán. En realidad, es más incómodo para ese tipo de cosas con herencia y es más probable que se dejen errores para el tiempo de ejecución.

Utilizando el polimorfismo, básicamente se le deja una clase base para el contenedor y clases derivadas para los tipos de datos. La clase base / clases derivadas pueden tener tantas funciones virtuales como necesite, en ambas direcciones.

Por supuesto, esto significaría que también necesitaría ajustar los tipos de datos primitivos en clases derivadas. Si reconsidera el uso de plantillas en general, aquí es donde usaría las plantillas. Cree una clase derivada de la base, que es una plantilla, y úsela para los tipos de datos primitivos (y otros en los que no necesite más funcionalidad de la que proporciona la plantilla).

No olvides que podrías hacerte la vida más fácil escribiendo typedefs para cada tipo de plantilla, especialmente si luego necesitas convertir uno de ellos en una clase.

También puede consultar The Boost Concept Check Biblioteca (BCCL) que está diseñada para proporcionar restricciones en los parámetros de plantilla de clases con plantilla, sus contenedores en este caso.

Y solo para reiterar lo que otros han dicho, nunca he tenido problemas para mezclar polimorfismo y plantillas, y he hecho algunas cosas bastante complejas con ellos.

No podría tener que renunciar a interfaces similares a Java y usar plantillas también. Sugerencia de Josh de un El Contenedor de plantilla base genérico ciertamente le permitiría pasar polimórficamente los Contenedores y sus hijos, pero también podría implementar interfaces como clases abstractas para ser los elementos contenidos. No hay ninguna razón por la que no pueda crear una clase abstracta de IComparable como sugirió, de modo que podría tener una función polimórfica de la siguiente manera:

class Whatever
{
   void MyPolymorphicMethod(Container<IComparable*> &listOfComparables);
}

Este método ahora puede tomar cualquier elemento secundario de Contenedor que contenga cualquier clase que implemente IComparable, por lo que sería extremadamente flexible.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top