Pregunta

Considere el siguiente código de ejemplo:

class Foo
{
};

class Bar : public Foo
{
};

class FooCollection
{
protected:
    vector<shared_ptr<Foo> > d_foos;
};

class BarCollection : public FooCollection
{
public:
    vector<shared_ptr<Bar> > &getBars()
    {
        // return d_foos won't do here...
    }
};

Tengo un problema como este en mi proyecto actual. El código del cliente usa BarCollection, que almacena consejos para Bars en d_foos que se declara en FooCollection. Ahora me gustaría exponer la colección de punteros a barras al código del cliente. Podría darle acceso al código del cliente al vector de punteros a Foosy lanza estos a los consejos para Bars en el código del cliente, pero esto se siente mal ya que el cliente no tiene que saber sobre FooEx existencia.

También podría definir un get() miembro que recupera objetos de d_foos Y los lanza, pero esto se siente bastante torpe. Preferiblemente, me gustaría volver a d_foos como un vector<shared_ptr<Bar> > &, pero parece que no puedo hacer esto.

También podría ser que mi diseño sea simplemente incorrecto. Sin embargo, parecía la más solución natural, como Bar es una especialización de Foo y BarCollection es una especialización de FooCollection Y comparten funcionalidad.

¿Podría sugerir buenas soluciones para implementar? getBars en BarCollection o mejores alternativas de diseño?

Editar:

Resulta que mi diseño era realmente malo. Barcollection no es una foocollection, a pesar de requerir toda la funcionalidad de Foocollection. Mi solución actual basada en las respuestas a continuación, que es mucho más limpia, ahora es:

class Foo
{
};

class Bar : public Foo
{
};

template<class T>
class Collection
{
    vector<shared_ptr<T> > d_items;
};

typedef Collection<Foo> FooCollection;

class BarCollection : public Collection<Bar>
{
    // Additional stuff here.
};

¡Gracias por todas las excelentes sugerencias y ejemplos!

¿Fue útil?

Solución

template<class T>
class MyContainer {
  vector<shared_ptr<T> > d_foos;
public:
  vector<shared_ptr<T> > & getVector();
};

class FooCollection : public MyContainer<Foo> {
};

class BarCollection : public MyContainer<Bar> {
};

Otros consejos

Sugeriría exponer a los iteradores de sus clases de contenedores, en lugar del contenedor de miembros. De esa manera, no importará cuál sea el tipo de contenedor.

El problema es que está tratando de mezclar y combinar dos tipos diferentes de polimorfismo independientes diferentes de una manera que no funcionará. El polimorfismo de las plantillas de compilación, tiempo de compilación, no le permitirá sustituir un tipo de base por un tipo derivado. El sistema de plantilla de C ++ no hace asociación entre

class<Foo>

y

class<Bar>

Una sugerencia podría ser crear un adaptador derivado de Foo que se lanzaría a la clase correcta:

 template <class derived, class base>
 class DowncastContainerAdapter
 {
 private:
     std::vector< boost::shared_ptr<base> >::iterator curr;
     std::vector< boost::shared_ptr<base> >::const_iterator end;
 public:
     DowncastContainerAdapter(/*setup curr & end iterators*/)
     {
         // assert derived actually is derived from base
     }

     boost::shared_ptr<derived> GetNext()
     {
         // increment iterator
         ++curr;
         return dynamic_cast<base>(*curr);
     }

     bool IsEnd()
     {
         return (curr == end);
     }
 };

Tenga en cuenta que esta clase tendrá el mismo problema que un iterador, la operación en el vector puede invalidar esta clase.

Otro pensamiento

Es posible que no se dé cuenta, pero puede estar perfectamente bien devolver un vector de Foo. El usuario del bar ya debe tener pleno conocimiento de Foo, ya que al incluir bar.h debe obtener foo.h a través de Bar.H. La razón es que para que Bar herede de Foo, debe tener pleno conocimiento de la clase a través de Foo.H. Sugeriría, en lugar de usar la solución anterior, si es posible hacer que Foo (o una súper clase de Foo) sea una clase de interfaz y pasar por vectores de punteros a esa clase de interfaz. Este es un patrón bastante común y no elevará las cejas que esta solución inestable que se me ocurrió :). Por otra parte, puede tener sus razones. Buena suerte de cualquier manera.

La pregunta es, ¿por qué harías eso? Si le da al usuario una colección de punteros para bar, supone que solo hay barras, por lo que almacenar internamente los consejos en una colección para foo no tiene sentido. Si almacena diferentes subtipos de foo en su colección de puntero a Foo, no puede devolverlo como una colección de punteros para barrar, ya que no todos los objetos son barras. En el primer caso, (usted sabe que solo tiene barras) debe usar un enfoque plantado como se sugirió anteriormente. De lo contrario, tienes que repensar, lo que realmente quieres.

¿No puedes reemplazar esto con una colección plantatizada en Foo / Bar?, Algo como esto

class Collection<T> {
protected:
    vector<shared_ptr<T> > d_foos;
};

typedef Collection<Foo> FooCollection;
typedef Collection<Bar> BarCollection;

¿Tienes una necesidad especial de tener? BarCollection derivado de FooCollection? Porque generalmente un BarCollection no es a FooCollection, por lo general, muchas de las cosas que se pueden hacer con un FooCollection no debe hacerse con un BarCollection. Por ejemplo:

BarCollection *bc = new BarCollection();
FooCollection *fc = bc; // They are derived from each other to be able to do this
fc->addFoo(Foo());      // Of course we can add a Foo to a FooCollection

Ahora hemos agregado un Foo Objetar a lo que se supone que es un BarCollection. Si el BarCollection intenta acceder a este elemento recién agregado y espera que sea un Bar, todo tipo de cosas feas sucederán.

Por lo general, desea evitar esto y no tener sus clases de recolección derivadas entre sí. Ver también preguntas sobre contenedores de fundición de tipos derivados para obtener más respuestas sobre este tema ...

En primer lugar, hablemos de shared_ptr. Sabes acerca de: boost::detail::dynamic_cast_tag ?

shared_ptr<Foo> fooPtr(new Bar());
shared_ptr<Bar> barPtr(fooPtr, boost::detail::dynamic_cast_tag());

Esta es una forma muy útil. Debajo de la portada simplemente realiza un dynamic_cast, nada elegante allí, pero una notación más fácil. El contrato es el mismo que el clásico: si el objeto señalado no es en realidad es un Bar (o derivado de él), luego obtienes un puntero nulo.

Volver a su pregunta: Código malo.

BarCollection no es un FooCollection, como se mencionó, por lo tanto, está en problemas porque puede introducir otros elementos en el vector de punteros que Bar unos.

Sin embargo, no extenderé eso, porque esto está más allá de la pregunta en cuestión, y creo que nosotros (como intentando responder) deberíamos restringirnos de eso.

No puede aprobar una referencia, pero puede pasar un View.

Básicamente, un View es un nuevo objeto que actúa como un Proxy al viejo. Es relativamente fácil de usar Boost.iterators del ejemplo.

class VectorView
{
  typedef std::vector< std::shared_ptr<Foo> > base_type;

public:
  typedef Bar value_type;
  // all the cluttering

  class iterator: boost::iterator::iterator_adaptor<
    iterator,
    typename base_type::iterator,
    std::shared_ptr<Bar>
  >
  {
    typename iterator_adaptor::reference dereference() const
    {
      // If you have a heart weakness, you'd better stop here...
      return reinterpret_cast< std::shared_ptr<Bar> >(this->base_reference());
    }
  };

  // idem for const_iterator

  // On to the method forwarding
  iterator begin() { return iterator(m_reference.begin()); }

private:
  base_type& m_reference;
}; // class VectorView

El verdadero problema aquí es, por supuesto, el reference un poco. Conseguir un NEW shared_ptr El objeto es fácil y deje realizar un dynamic_cast según sea necesario. Conseguir un reference hacia ORIGINAL shared_ptr Pero interpretado como el tipo requerido ... realmente no es lo que me gusta ver en el código.

Nota:
Puede haber una manera de hacerlo mejor que eso usando Boost.fusion transform_view clase, pero no pude resolverlo.

En particular, usando transform_view puedo conseguir shared_ptr<Bar> Pero no puedo conseguir un shared_ptr<Bar>& Cuando desreferencia a mi iterador, lo cual es molesto dado que el único uso de devolver una referencia a los subyacentes vector (y no un const_reference) es realmente modificar la estructura del vector y los objetos que contiene.

Nota 2:
Considere la refactorización. Ha habido excelentes sugerencias allí.

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