Eu tenho um arquivo: Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
/*etc...*/
e outro arquivo: BaseFactory.h
#include "Base.h"
class BaseFactory
{
public:
BaseFactory(const string &sClassName){msClassName = sClassName;};
Base * Create()
{
if(msClassName == "DerivedA")
{
return new DerivedA();
}
else if(msClassName == "DerivedB")
{
return new DerivedB();
}
else if(/*etc...*/)
{
/*etc...*/
}
};
private:
string msClassName;
};
/*etc.*/
Existe uma maneira de converter de alguma forma essa seqüência de caracteres em um tipo real (classe), para que BaseFactory não precise conhecer todas as classes Derived possíveis e tenha if () para cada uma delas? Posso produzir uma classe dessa string?
Eu acho que isso pode ser feito em c # através de reflexão. Existe algo semelhante em C ++?
c++
inheritance
factory
instantiation
Gal Goldman
fonte
fonte
Respostas:
Não, não há, a menos que você mesmo faça o mapeamento. O C ++ não tem mecanismo para criar objetos cujos tipos são determinados em tempo de execução. Você pode usar um mapa para fazer esse mapeamento, no entanto:
E então você pode fazer
Obtendo uma nova instância. Outra idéia é fazer com que os tipos se registrem:
Você pode optar por criar uma macro para o registro
Tenho certeza de que existem nomes melhores para esses dois. Outra coisa que provavelmente faz sentido usar aqui é
shared_ptr
.Se você tiver um conjunto de tipos não relacionados que não possuem classe base comum, poderá fornecer ao ponteiro da função um tipo de retorno
boost::variant<A, B, C, D, ...>
. Por exemplo, se você tem uma classe Foo, Bar e Baz, fica assim:A
boost::variant
é como uma união. Ele sabe qual tipo é armazenado nele, procurando qual objeto foi usado para inicializar ou atribuir a ele. Dê uma olhada na documentação aqui . Finalmente, o uso de um ponteiro de função bruta também é um pouco antigo. O código C ++ moderno deve ser dissociado de funções / tipos específicos. Você podeBoost.Function
procurar uma maneira melhor. Ficaria assim então (o mapa):std::function
também estará disponível na próxima versão do C ++, inclusivestd::shared_ptr
.fonte
DerivedB::reg
seja realmente inicializado? Meu entendimento é que ele pode não ser construído se nenhuma função ou objeto definido na unidade de traduçãoderivedb.cpp
, conforme 3.6.2.BaseFactory::map_type * BaseFactory::map = NULL;
no meu arquivo cpp. Sem isso, o vinculador reclamou do mapa de símbolos desconhecido.DerivedB::reg
não será inicializado se nenhuma de suas funções ou instâncias estiver definida na unidade de traduçãoderivedb.cpp
. Isso significa que a classe não é registrada até que seja realmente instanciada. Alguém conhece uma solução alternativa para isso?Não, não existe. Minha solução preferida para esse problema é criar um dicionário que mapeie o nome para o método de criação. Classes que desejam ser criadas assim registram um método de criação no dicionário. Isso é discutido em alguns detalhes no livro de padrões do GoF .
fonte
A resposta curta é que você não pode. Veja estas perguntas de SO para saber por que:
fonte
Eu respondi em outra pergunta SO sobre fábricas C ++. Veja lá se uma fábrica flexível é de seu interesse. Eu tento descrever uma maneira antiga do ET ++ para usar macros que funcionou muito bem para mim.
O ET ++ foi um projeto para portar o MacApp antigo para C ++ e X11. No esforço disso, Eric Gamma etc começou a pensar em Design Patterns
fonte
O boost :: functional possui um modelo de fábrica bastante flexível: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
Minha preferência, porém, é gerar classes de wrapper que ocultam o mapeamento e o mecanismo de criação de objetos. O cenário comum que encontro é a necessidade de mapear diferentes classes derivadas de alguma classe base para chaves, onde todas as classes derivadas têm uma assinatura de construtor comum disponível. Aqui está a solução que eu encontrei até agora.
Geralmente sou contra o uso intenso de macro, mas fiz uma exceção aqui. O código acima gera GENERIC_FACTORY_MAX_ARITY + 1 versões de uma classe chamada GenericFactory_N, para cada N entre 0 e GENERIC_FACTORY_MAX_ARITY, inclusive.
Usar os modelos de classe gerados é fácil. Suponha que você queira que uma fábrica crie objetos derivados de BaseClass usando um mapeamento de string. Cada um dos objetos derivados aceita 3 números inteiros como parâmetros do construtor.
O destruidor de classe GenericFactory_N é virtual para permitir o seguinte.
Observe que esta linha da macro do gerador de fábrica genérica
Assume que o arquivo de cabeçalho de fábrica genérico se chama GenericFactory.hpp
fonte
Solução detalhada para registrar os objetos e acessá-los com nomes de cadeias.
common.h
:test1.h
:main.cpp
:Compile e execute (já fez isso com o Eclipse)
Resultado:
fonte
Significado de reflexão como em Java. há algumas informações aqui: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx
De um modo geral, pesquise no Google por "reflexão em c ++"
fonte
Tor Brede Vekterli fornece uma extensão de impulso que fornece exatamente a funcionalidade que você procura. Atualmente, é um pouco estranho para as bibliotecas de impulso atuais, mas consegui fazê-lo funcionar com 1.48_0 depois de alterar seu namespace base.
http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference
Em resposta a quem pergunta por que algo (como reflexão) seria útil para c ++ - eu o uso para interações entre a interface do usuário e um mecanismo - o usuário seleciona uma opção na interface do usuário e o mecanismo pega a sequência de seleção da interface do usuário, e produz um objeto do tipo desejado.
O principal benefício de usar a estrutura aqui (manter uma lista de frutas em algum lugar) é que a função de registro está na definição de cada classe (e requer apenas uma linha de código que chama a função de registro por classe registrada) - em oposição a um arquivo que contém a lista de frutas, que deve ser adicionada manualmente a cada vez que uma nova classe for derivada.
Eu fiz da fábrica um membro estático da minha classe base.
fonte
Esse é o padrão de fábrica. Veja a Wikipedia (e este exemplo). Você não pode criar um tipo per se a partir de uma cadeia de caracteres sem algum truque flagrante. Por que você precisa disso?
fonte