Tendo passado algum tempo desenvolvendo em C #, percebi que se você declarar uma classe abstrata com o propósito de usá-la como uma interface, não poderá instanciar um vetor dessa classe abstrata para armazenar instâncias das classes filhas.
#pragma once
#include <iostream>
#include <vector>
using namespace std;
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
class FunnyContainer
{
private:
std::vector <IFunnyInterface> funnyItems;
};
A linha que declara o vetor da classe abstrata causa este erro no MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Vejo uma solução alternativa óbvia, que é substituir IFunnyInterface pelo seguinte:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Esta é uma solução alternativa aceitável em relação ao C ++? Se não, existe alguma biblioteca de terceiros como boost que poderia me ajudar a contornar isso?
Obrigado por ler isso!
Anthony
fonte
Você não pode criar um vetor de um tipo de classe abstrata porque você não pode criar instâncias de uma classe abstrata e recipientes de biblioteca padrão C ++ como std :: vector store values (ou seja, instâncias). Se você quiser fazer isso, terá que criar um vetor de ponteiros para o tipo de classe abstrata.
Seu workround não funcionaria porque as funções virtuais (que é o motivo pelo qual você quer a classe abstrata em primeiro lugar) só funcionam quando chamadas por meio de ponteiros ou referências. Você também não pode criar vetores de referências, então esta é a segunda razão pela qual você deve usar um vetor de ponteiros.
Você deve perceber que C ++ e C # têm muito pouco em comum. Se você pretende aprender C ++, deve pensar nisso como começar do zero e ler um bom tutorial dedicado a C ++, como Accelerated C ++ de Koenig e Moo.
fonte
Neste caso, não podemos usar nem mesmo este código:
std::vector <IFunnyInterface*> funnyItems;
ou
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Porque não há relacionamento IS A entre FunnyImpl e IFunnyInterface e não há conversão implícita entre FUnnyImpl e IFunnyInterface por causa da herança privada.
Você deve atualizar seu código da seguinte maneira:
class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: public IFunnyInterface { public: virtual void IamFunny() { cout << "<INSERT JOKE HERE>"; } };
fonte
A alternativa tradicional é usar um
vector
de ponteiros, como já foi mencionado.Para quem gosta,
Boost
vem com uma biblioteca muito interessante:Pointer Containers
que se adapta perfeitamente à tarefa e te livra dos vários problemas que os ponteiros implicam:Observe que isso é significativamente melhor do que
vector
ponteiros inteligentes, tanto em termos de desempenho quanto de interface.Agora, há uma terceira alternativa, que é mudar sua hierarquia. Para melhor isolamento do usuário, tenho visto várias vezes o seguinte padrão usado:
class IClass; class MyClass { public: typedef enum { Var1, Var2 } Type; explicit MyClass(Type type); int foo(); int bar(); private: IClass* m_impl; }; struct IClass { virtual ~IClass(); virtual int foo(); virtual int bar(); }; class MyClass1: public IClass { .. }; class MyClass2: public IClass { .. };
Isso é bastante direto e uma variação do
Pimpl
idioma enriquecido por umStrategy
padrão.Isso funciona, é claro, apenas no caso em que você não deseja manipular os objetos "verdadeiros" diretamente e envolve cópia profunda. Portanto, pode não ser o que você deseja.
fonte
Porque para redimensionar um vetor, você precisa usar o construtor padrão e o tamanho da classe, que por sua vez exige que seja concreto.
Você pode usar um ponteiro como outra sugestão.
fonte
std :: vector tentará alocar memória para conter seu tipo. Se sua classe for puramente virtual, o vetor não pode saber o tamanho da classe que terá que alocar.
Acho que, com sua solução alternativa, você poderá compilar um,
vector<IFunnyInterface>
mas não poderá manipular o FunnyImpl dentro dele. Por exemplo, se IFunnyInterface (classe abstrata) for de tamanho 20 (não sei direito) e FunnyImpl for de tamanho 30 porque tem mais membros e código, você vai acabar tentando encaixar 30 em seu vetor de 20A solução seria alocar memória no heap com "novos" e armazenar ponteiros em
vector<IFunnyInterface*>
fonte
Acho que a causa raiz dessa limitação realmente triste é o fato de que os construtores não podem ser virtuais. Portanto, o compilador não pode gerar código que copie o objeto sem saber seu tempo no momento da compilação.
fonte