Ayant passé un certain temps à développer en C #, j'ai remarqué que si vous déclarez une classe abstraite dans le but de l'utiliser comme interface, vous ne pouvez pas instancier un vecteur de cette classe abstraite pour stocker des instances des classes enfants.
#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;
};
La ligne déclarant le vecteur de classe abstraite provoque cette erreur dans MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Je vois une solution de contournement évidente, qui consiste à remplacer IFunnyInterface par ce qui suit:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Est-ce une solution de contournement acceptable en matière de C ++? Sinon, y a-t-il une bibliothèque tierce comme boost qui pourrait m'aider à contourner cela?
Merci d'avoir lu ceci !
Anthony
la source
Vous ne pouvez pas créer un vecteur d'un type de classe abstraite car vous ne pouvez pas créer d'instances d'une classe abstraite et des conteneurs de bibliothèque standard C ++ comme std :: vector stockent des valeurs (c'est-à-dire des instances). Si vous souhaitez faire cela, vous devrez créer un vecteur de pointeurs vers le type de classe abstraite.
Votre solution de contournement ne fonctionnerait pas car les fonctions virtuelles (c'est pourquoi vous voulez la classe abstraite en premier lieu) ne fonctionnent que lorsqu'elles sont appelées via des pointeurs ou des références. Vous ne pouvez pas non plus créer de vecteurs de références, c'est donc une deuxième raison pour laquelle vous devez utiliser un vecteur de pointeurs.
Vous devez savoir que C ++ et C # ont très peu de points communs. Si vous avez l'intention d'apprendre le C ++, vous devriez le considérer comme partant de zéro et lire un bon didacticiel C ++ dédié tel que Accelerated C ++ de Koenig et Moo.
la source
Dans ce cas, nous ne pouvons même pas utiliser ce code:
std::vector <IFunnyInterface*> funnyItems;
ou
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Parce qu'il n'y a pas de relation IS A entre FunnyImpl et IFunnyInterface et qu'il n'y a pas de conversion implicite entre FUnnyImpl et IFunnyInterface en raison de l'héritage privé.
Vous devez mettre à jour votre code comme suit:
class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: public IFunnyInterface { public: virtual void IamFunny() { cout << "<INSERT JOKE HERE>"; } };
la source
L'alternative traditionnelle est d'utiliser un
vector
des pointeurs, comme déjà noté.Pour ceux qui apprécient,
Boost
est livré avec une bibliothèque très intéressante:Pointer Containers
qui est parfaitement adaptée à la tâche et vous libère des différents problèmes qu'impliquent les pointeurs:Notez que c'est nettement mieux qu'un
vector
des pointeurs intelligents, à la fois en termes de performances et d'interface.Maintenant, il existe une 3ème alternative, qui est de changer votre hiérarchie. Pour une meilleure isolation de l'utilisateur, j'ai vu plusieurs fois le motif suivant utilisé:
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 { .. };
C'est assez simple et une variante de l'
Pimpl
idiome enrichie par unStrategy
modèle.Cela ne fonctionne, bien sûr, que dans le cas où vous ne souhaitez pas manipuler directement les "vrais" objets, et implique une copie profonde. Donc, ce n'est peut-être pas ce que vous souhaitez.
la source
Parce que pour redimensionner un vecteur, vous devez utiliser le constructeur par défaut et la taille de la classe, ce qui à son tour nécessite qu'il soit concret.
Vous pouvez utiliser un pointeur comme autre suggéré.
la source
std :: vector essaiera d'allouer de la mémoire pour contenir votre type. Si votre classe est purement virtuelle, le vecteur ne peut pas connaître la taille de la classe qu'il devra allouer.
Je pense qu'avec votre solution de contournement, vous serez en mesure de compiler un
vector<IFunnyInterface>
mais vous ne pourrez pas manipuler FunnyImpl à l'intérieur. Par exemple, si IFunnyInterface (classe abstraite) est de taille 20 (je ne sais pas vraiment) et FunnyImpl est de taille 30 car il a plus de membres et de code, vous finirez par essayer de mettre 30 dans votre vecteur de 20La solution serait d'allouer de la mémoire sur le tas avec "nouveau" et de stocker les pointeurs dans
vector<IFunnyInterface*>
la source
Je pense que la cause première de cette triste limitation est le fait que les constructeurs ne peuvent pas virtuels. Le compilateur ne peut pas générer de code qui copie l'objet sans connaître son heure au moment de la compilation.
la source