Pergunta

Parece que eu tive um mal-entendido fundamental sobre C ++: <

Eu gosto da solução recipiente polimórfico. Obrigado SO, para trazer isso para a minha atenção:)


Assim, temos uma necessidade de criar um objeto do tipo de recipiente relativamente genérico. Acontece também para encapsular alguma lógica de negócios relacionados. No entanto, precisamos armazenar dados essencialmente arbitrárias neste recipiente - tudo a partir de tipos de dados primitivos para classes complexas

.

Assim, seria de saltar imediatamente para a idéia de uma classe de modelo e ser feito com ele. No entanto, tenho notado polimorfismo C ++ e modelos não jogar bem juntos. Sendo que há alguma lógica complexa que vamos ter que trabalhar, eu preferiria ficar com tanto modelos ou polimorfismo, e não tentar lutar C ++, tornando-a fazer as duas coisas.

Finalmente, dado que eu quero fazer uma coisa ou outra, eu preferiria polimorfismo. Acho que é muito mais fácil para representar restrições como "este recipiente contém tipos comparáveis" -. A la java

Trazendo me ao tema da pergunta: No mais abstrato, imagino que eu poderia ter uma interface virtual "Container" pura que tem algo semelhante a "push (* dados void) e pop (dados void *)" ( para o registro, não estou realmente tentando implementar uma pilha).

No entanto, eu realmente não gosto de void * no nível superior, para não mencionar a assinatura vai mudar cada vez que eu quiser adicionar um constrangimento para o tipo de dados de um recipiente de concreto pode trabalhar com ele.

Resumindo: Temos recipientes relativamente complexas que têm várias maneiras de recuperar elementos. Queremos ser capazes de variar as restrições sobre os elementos que podem ir para os recipientes. Elementos devem trabalhar com vários tipos de recipientes (desde que cumpram as restrições desse recipiente particular).

Edit: Gostaria também de mencionar que os próprios recipientes precisam ser polimórfico. Essa é a minha principal razão para não querer usar templated C ++.

Assim - eu deveria largar o meu amor por tipo interfaces Java e ir com modelos? Devo usar void * e tudo estaticamente elenco? Ou devo ir com uma definição de classe vazia "elemento" que declara nada e uso isso como minha classe de nível superior na hierarquia "Elemento"?

Uma das razões pelas quais eu amo estouro de pilha é que muitas das respostas fornecer alguns insights interessantes sobre outras abordagens que eu não tinha nem sequer considerada. Então, obrigado antecipadamente por suas idéias e comentários.

Foi útil?

Solução

Você não pode ter uma classe Container raiz que contém 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();
       }
    }
};

Estes recipientes podem então ser passado ao redor polymorphically:

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();

Isto dá-lhe a interface Container de uma forma genérica de tipo seguro.

Se você quiser adicionar restrições ao tipo de elementos que podem ser contidas, você pode fazer algo como isto:

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
};

Outras dicas

Você pode olhar através de um contêiner padrão de impulso: :. qualquer se você estiver armazenando dados verdadeiramente arbitrárias no recipiente

Parece mais como você preferiria ter algo como um boost :: ptr_container onde qualquer coisa que pode ser armazenado no recipiente tem que derivar de algum tipo base, e o recipiente em si só pode dar-lhe a referência de que o tipo de base.

A coisa simples é definir uma classe base abstrata chamada Container e subclasse-lo para cada tipo de item que você pode desejar armazenar. Em seguida, você pode usar qualquer classe de coleção padrão (std::vector, std::list, etc.) para armazenar ponteiros para Container. Tenha em mente, que desde que você seria armazenar ponteiros, você teria que lidar com sua alocação / desalocação.

No entanto, o fato de que você precisa de uma única coleção para armazenar objetos de tais tipos completamente diferentes é uma indicação de que algo pode estar errado com o projeto de sua aplicação. Pode ser melhor para revisitar a lógica de negócios antes de implementar este contêiner super-genérico.

Polimorfismo e modelos que jogar muito bem juntos, se você usá-los corretamente.

De qualquer forma, eu entendo que você deseja armazenar apenas um tipo de objetos em cada caso recipiente. Se assim for, usar modelos. Isto irá impedi-lo de armazenar o tipo de objeto errado por engano.

Como para interfaces de recipiente: Dependendo do seu design, talvez você vai ser capaz de fazê-los templated, também, e, em seguida, eles vão ter métodos como void push(T* new_element). Pense no que você sabe sobre o objeto quando você deseja adicioná-lo a um recipiente (de um tipo desconhecido). Onde vai o objeto vem em primeiro lugar? Uma função que retorna void*? Sabe que ele vai ser comparável? Pelo menos, se todas as classes de objetos armazenados são definidos no seu código, você pode torná-los todos herdar de um ancestral comum, digamos, Storable e uso Storable* vez de void*.

Agora, se você vê que os objetos serão sempre adicionados a um recipiente por um método como void push(Storable* new_element), então realmente não haverá valor acrescentado no sentido de tornar o recipiente de um modelo. Mas, então, você saberá que deve armazenar Storables.

Em primeiro lugar, de tudo, modelos e polimorfismo são conceitos ortogonais e eles joguem bem juntos. Em seguida, por que você quer uma estrutura de dados específico? E sobre as estruturas STL ou impulso de dados (especificamente ponteiro containter ) não funciona para você.

Dada a sua pergunta, parece que você seria abusar herança em sua situação. É possível criar " restrições " sobre o que vai em seus recipientes, especialmente se você estiver usando modelos. Essas restrições podem ir além do que o seu compilador e vinculador irá dar-lhe. É realmente mais estranho para esse tipo de coisa com a herança e os erros são mais propensos a esquerda para tempo de execução.

Usando o polimorfismo, que são basicamente deixados com uma classe base para o recipiente, e derivado classes para os tipos de dados. Os / as classes derivadas da classe base pode ter tantas funções virtuais como você precisa, em ambas as direções.

Claro, isso significaria que seria necessário para embrulhar os tipos de dados primitivos em classes derivadas também. Se você iria reconsiderar o uso de modelos globais, este é o lugar onde eu iria usar os modelos. Faça uma classe derivada da base, que é um modelo, e o uso que para os tipos primitivos de dados (e outros em que você não precise mais funcionalidade do que é fornecido pelo modelo).

Não se esqueça que você pode tornar sua vida mais fácil, typedefs para cada um dos tipos templated -. Especialmente se você precisar posteriormente para transformar um deles em uma classe

Você também pode querer verificar para fora o impulso Conceito Verificação Library (BCCL) que é projetado para fornecer restrições sobre os parâmetros do modelo de aulas de modelo, seus recipientes neste caso.

E só para reiterar o que já foi dito, eu nunca tive um problema misturando polimorfismo e modelos, e eu tenho feito algumas coisas bastante complexo com eles.

Você não podia ter de desistir de Java-like interfaces e modelos de uso também. sugestão de Josh de um template base genérica Container certamente permitir que você polymorphically passar Containers e seus filhos ao redor, mas além disso, você poderia certamente implementar interfaces como classes abstratas para ser os itens contidos. Não há nenhuma razão você não poderia criar uma classe IComparable abstrato como você sugeriu, de modo que você poderia ter uma função polimórfica da seguinte forma:

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

Este método pode agora tomar qualquer criança de recipiente que contenha qualquer classe que implementa IComparable, por isso seria extremamente flexível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top