Pourquoi ne pouvons-nous pas déclarer un std :: vector ?

Ayant passé pas mal de 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 les instances des classes enfants.

#pragma once #include  #include  using namespace std; class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: IFunnyInterface { public: virtual void IamFunny() { cout << ""; } }; class FunnyContainer { private: std::vector  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 é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 pour C ++? Sinon, existe-t-il une bibliothèque tierce comme boost qui pourrait m’aider à contourner ce problème?

Merci d’avoir lu ceci !

Anthony

Vous ne pouvez pas instancier des classes abstraites, donc un vecteur de classes abstraites ne peut pas fonctionner.

Vous pouvez cependant utiliser un vecteur de pointeurs pour les classes abstraites:

 std::vector ifVec; 

Cela vous permet également d’utiliser un comportement polymorphe – même si la classe n’était pas abstraite, le stockage par valeur poserait le problème du découpage d’objects .

Vous ne pouvez pas créer un vecteur d’un type de classe abstrait car vous ne pouvez pas créer d’instances d’une classe abstraite et de conteneurs de bibliothèque standard C ++ tels que des valeurs de magasin std :: vector (c’est-à-dire des instances). Si vous voulez faire cela, vous devrez créer un vecteur de pointeurs vers le type de classe abstrait.

Votre workround ne fonctionnerait pas car les fonctions virtuelles (c’est pourquoi vous voulez que la classe abstraite en premier lieu) ne fonctionnent que si 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 devriez réaliser que C ++ et C # ont très peu en commun. Si vous avez l’intention d’apprendre C ++, vous devriez penser à partir de zéro, et lire un bon didacticiel C ++ dédié, tel que Accelerated C ++ de Koenig et Moo.

L’alternative traditionnelle est d’utiliser un vector de pointeurs, comme déjà noté.

Pour ceux qui apprécient, Boost est livré avec une bibliothèque très intéressante: Pointer Containers qui convient parfaitement à la tâche et vous libère des différents problèmes que posent les pointeurs:

  • gestion à vie
  • double déréférencement des iterators

Notez que ceci est nettement meilleur qu’un vector de pointeurs intelligents, à la fois en termes de performances et d’interface.

Maintenant, il existe une troisième alternative, qui consiste à changer de hiérarchie. Pour une meilleure isolation de l’utilisateur, j’ai vu plusieurs fois le schéma suivant:

 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’idiome Pimpl enrichie par un modèle de Strategy .

Cela fonctionne bien sûr uniquement dans le cas où vous ne souhaitez pas manipuler directement les “vrais” objects et implique une copie profonde. Donc, ce n’est peut-être pas ce que vous souhaitez.

Dans ce cas, nous ne pouvons même pas utiliser ce code:

 std::vector  funnyItems; 

ou

 std::vector  > funnyItems; 

Parce qu’il n’y a pas de relation IS Une relation entre FunnyImpl et IFunnyInterface et qu’il n’y a pas de conversion implicite entre FUnnyImpl et IFunnyInterface en raison de l’inheritance 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 << ""; } }; 

Parce que pour redimensionner un vecteur, vous devez utiliser le constructeur par défaut et la taille de la classe, ce qui nécessite à son tour d’être concret.

Vous pouvez utiliser un pointeur comme d’autres suggestions.

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 à allouer.

Je pense qu’avec votre solution de contournement, vous pourrez comstackr un vector 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 que FunnyImpl est de taille 30 car il a plus de membres et de code, vous finirez par essayer d’adapter 30 à votre vecteur de 20.

La solution serait d’allouer de la mémoire sur le tas avec “new” et de stocker les pointeurs dans le vector

Je pense que la cause profonde de cette sortingste limite est le fait que les constructeurs ne peuvent pas être virtuels. Ainsi, le compilateur ne peut pas générer de code qui copie l’object sans connaître son heure de compilation.