Membres virtuels statiques C ++?

Est-il possible en C ++ d’avoir une fonction membre à la fois static et virtual ? Apparemment, il n’y a pas de moyen simple de le faire ( static virtual member(); est une erreur de compilation), mais existe-t-il au moins un moyen d’obtenir le même effet?

C’EST À DIRE:

 struct Object { struct TypeInformation; static virtual const TypeInformation &GetTypeInformation() const; }; struct SomeObject : public Object { static virtual const TypeInformation &GetTypeInformation() const; }; 

Il est logique d’utiliser GetTypeInformation() fois sur une instance ( object->GetTypeInformation() ) et sur une classe ( SomeObject::GetTypeInformation() ), ce qui peut être utile pour les comparaisons et essentiel pour les modèles.

La seule façon de penser consiste à écrire deux fonctions / une fonction et une constante, par classe, ou à utiliser des macros.

D’autres solutions?

Non, il n’y a pas moyen de le faire, car que se passerait-il lorsque vous avez appelé Object::GetTypeInformation() ? Il ne peut pas savoir quelle version de classe dérivée appeler car aucun object n’y est associé.

Vous devrez en faire une fonction virtuelle non statique pour fonctionner correctement. Si vous souhaitez également pouvoir appeler la version d’une classe dérivée spécifique sans instance virtuelle, vous devrez également fournir une deuxième version non virtuelle statique redondante.

Beaucoup disent que ce n’est pas possible, je ferais un pas de plus et je dirais que cela n’a pas de sens.

Un membre statique est quelque chose qui ne se rapporte à aucune instance, seulement à la classe.

Un membre virtuel est quelque chose qui ne concerne pas directement une classe, mais uniquement une instance.

Ainsi, un membre virtuel statique serait quelque chose qui ne se rapporte à aucune instance ou à aucune classe.

J’ai rencontré ce problème l’autre jour: j’avais quelques classes pleines de méthodes statiques mais je voulais utiliser l’inheritance et les méthodes virtuelles et réduire la répétition du code. Ma solution était:

Au lieu d’utiliser des méthodes statiques, utilisez un singleton avec des méthodes virtuelles.

En d’autres termes, chaque classe doit contenir une méthode statique que vous appelez pour obtenir un pointeur sur une seule instance partagée de la classe. Vous pouvez rendre les véritables constructeurs privés ou protégés afin que le code externe ne puisse pas en abuser en créant des instances supplémentaires.

En pratique, l’utilisation d’un singleton ressemble beaucoup à l’utilisation de méthodes statiques, sauf que vous pouvez tirer parti des méthodes d’inheritance et virtuelles.

C’est possible!

Mais qu’est-ce qui est possible? Les gens veulent souvent une sorte de “fonction virtuelle statique” en raison de la duplication du code nécessaire pour pouvoir appeler la même fonction via un appel statique “SomeDerivedClass :: myfunction ()” et un appel polymorphe “base_class_pointer-> myfunction ()”. La méthode “légale” pour permettre une telle fonctionnalité est la duplication des définitions de fonction:

 class Object { public: static ssortingng getTypeInformationStatic() { return "base class";} virtual ssortingng getTypeInformation() { return getTypeInformationStatic(); } }; class Foo: public Object { public: static ssortingng getTypeInformationStatic() { return "derived class";} virtual ssortingng getTypeInformation() { return getTypeInformationStatic(); } }; 

Que se passe-t-il si la classe de base possède un grand nombre de fonctions statiques et que la classe dérivée doit remplacer chacune d’elles et que l’on a oublié de fournir une définition de duplication pour la fonction virtuelle. Bon, nous aurons une erreur étrange lors de l’ exécution, ce qui est difficile à retrouver. Parce que la duplication du code est une mauvaise chose. Ce qui suit essaie de résoudre ce problème (et je veux dire à l’avance qu’il est complètement sûr de type et qu’il ne contient pas de magie noire comme typeid ou dynamic_cast 🙂

Donc, nous voulons fournir une seule définition de getTypeInformation () par classe dérivée et il est évident que ce doit être une définition de la fonction statique car il est impossible d’appeler “SomeDerivedClass :: getTypeInformation ()” si getTypeInformation () est virtuel. Comment pouvons-nous appeler la fonction statique de la classe dérivée via un pointeur vers la classe de base? Ce n’est pas possible avec vtable car vtable stocke les pointeurs uniquement sur les fonctions virtuelles et comme nous avons décidé de ne pas utiliser les fonctions virtuelles, nous ne pouvons pas modifier la vtable dans notre intérêt. Ensuite, pour pouvoir accéder à une fonction statique pour une classe dérivée via un pointeur sur une classe de base, nous devons stocker en quelque sorte le type d’un object dans sa classe de base. Une approche consiste à rendre la classe de base modélisée en utilisant “le modèle de modèle curieusement récurrent” mais ce n’est pas approprié ici et nous utiliserons une technique appelée “effacement de type”:

 class TypeKeeper { public: virtual ssortingng getTypeInformation() = 0; }; template class TypeKeeperImpl: public TypeKeeper { public: virtual ssortingng getTypeInformation() { return T::getTypeInformationStatic(); } }; 

Maintenant, nous pouvons stocker le type d’un object dans la classe de base “Object” avec une variable “keeper”:

 class Object { public: Object(){} boost::scoped_ptr keeper; //not virtual ssortingng getTypeInformation() const { return keeper? keeper->getTypeInformation(): ssortingng("base class"); } }; 

Dans une classe dérivée, le gestionnaire doit être initialisé pendant la construction:

 class Foo: public Object { public: Foo() { keeper.reset(new TypeKeeperImpl()); } //note the name of the function static ssortingng getTypeInformationStatic() { return "class for proving static virtual functions concept"; } }; 

Ajoutons du sucre syntaxique:

 template void override_static_functions(T* t) { t->keeper.reset(new TypeKeeperImpl()); } #define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this) 

Maintenant, les déclarations de descendants ressemblent à:

 class Foo: public Object { public: Foo() { OVERRIDE_STATIC_FUNCTIONS; } static ssortingng getTypeInformationStatic() { return "class for proving static virtual functions concept"; } }; class Bar: public Foo { public: Bar() { OVERRIDE_STATIC_FUNCTIONS; } static ssortingng getTypeInformationStatic() { return "another class for the same reason"; } }; 

usage:

 Object* obj = new Foo(); cout << obj->getTypeInformation() << endl; //calls Foo::getTypeInformationStatic() obj = new Bar(); cout << obj->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic() Foo* foo = new Bar(); cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic() Foo::getTypeInformation(); //compile-time error Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic() Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic() 

Avantages:

  1. moins de duplication de code (mais nous devons appeler OVERRIDE_STATIC_FUNCTIONS dans chaque constructeur)

Désavantages:

  1. OVERRIDE_STATIC_FUNCTIONS dans chaque constructeur
  2. frais généraux de mémoire et de performances
  3. complexité accrue

Questions ouvertes:

1) il y a différents noms pour les fonctions statiques et virtuelles comment résoudre l'ambiguïté ici?

 class Foo { public: static void f(bool f=true) { cout << "static";} virtual void f() { cout << "virtual";} }; //somewhere Foo::f(); //calls static f(), no ambiguity ptr_to_foo->f(); //ambiguity 

2) comment appeler implicitement OVERRIDE_STATIC_FUNCTIONS à l'intérieur de chaque constructeur?

C’est possible. Faire deux fonctions: statique et virtuelle

 struct Object{ struct TypeInformation; static const TypeInformation &GetTypeInformationStatic() const { return GetTypeInformationMain1(); } virtual const TypeInformation &GetTypeInformation() const { return GetTypeInformationMain1(); } protected: static const TypeInformation &GetTypeInformationMain1(); // Main function }; struct SomeObject : public Object { static const TypeInformation &GetTypeInformationStatic() const { return GetTypeInformationMain2(); } virtual const TypeInformation &GetTypeInformation() const { return GetTypeInformationMain2(); } protected: static const TypeInformation &GetTypeInformationMain2(); // Main function }; 

Bien que Alsk ait déjà donné une réponse assez détaillée, j’aimerais append une alternative, car je pense que sa mise en œuvre améliorée est trop compliquée.

Nous commençons par une classe de base abstraite, qui fournit l’interface pour tous les types d’object:

 class Object { public: virtual char* GetClassName() = 0; }; 

Maintenant, nous avons besoin d’une implémentation réelle. Mais pour éviter d’avoir à écrire à la fois les méthodes statique et virtuelle, nos classes d’objects réels hériteront des méthodes virtuelles. Cela ne fonctionne évidemment que si la classe de base sait comment accéder à la fonction membre statique. Nous devons donc utiliser un modèle et lui transmettre le nom de la classe d’objects:

 template class ObjectImpl : public Object { public: virtual char* GetClassName() { return ObjectType::GetClassNameStatic(); } }; 

Enfin, nous devons implémenter nos objects réels. Ici, il suffit d’implémenter la fonction membre statique, les fonctions membres virtuelles seront héritées de la classe modèle ObjectImpl, instanciée avec le nom de la classe dérivée, de sorte qu’elle accédera aux membres statiques.

 class MyObject : public ObjectImpl { public: static char* GetClassNameStatic() { return "MyObject"; } }; class YourObject : public ObjectImpl { public: static char* GetClassNameStatic() { return "YourObject"; } }; 

Ajoutons du code pour tester:

 char* GetObjectClassName(Object* object) { return object->GetClassName(); } int main() { MyObject myObject; YourObject yourObject; printf("%s\n", MyObject::GetClassNameStatic()); printf("%s\n", myObject.GetClassName()); printf("%s\n", GetObjectClassName(&myObject)); printf("%s\n", YourObject::GetClassNameStatic()); printf("%s\n", yourObject.GetClassName()); printf("%s\n", GetObjectClassName(&yourObject)); return 0; } 

Non, ce n’est pas possible, car les fonctions membres statiques ne possèdent pas this pointeur. Et les membres statiques (à la fois les fonctions et les variables) ne sont pas vraiment des membres de classe. Ils se trouvent juste être ClassName::member par ClassName::member , et adhèrent aux spécificateurs d’access aux classes. Leur stockage est défini quelque part en dehors de la classe; le stockage n’est pas créé chaque fois que vous instanciez un object de la classe. Les pointeurs vers les membres de la classe sont spéciaux en sémantique et en syntaxe. Un pointeur sur un membre statique est un pointeur normal à tous égards.

les fonctions virtuelles dans une classe ont besoin de this pointeur et sont très couplées à la classe, elles ne peuvent donc pas être statiques.

Eh bien, une réponse assez tardive, mais c’est possible en utilisant le modèle de modèle curieusement récurrent. Cet article de wikipedia contient les informations dont vous avez besoin et l’exemple qui vous est demandé est celui du polymorphism statique.

Non, la fonction de membre statique ne peut pas être virtuelle. Le concept virtuel étant résolu à l’exécution à l’aide de vptr et vptr étant un membre non statique de class.due, cette fonction membre statique ne peut pas accéder à vptr si un membre statique peut ne soyez pas virtuel.

Je pense que ce que vous essayez de faire peut être fait avec des modèles. J’essaie de lire entre les lignes ici. Ce que vous essayez de faire, c’est d’appeler une méthode à partir d’un code, où elle appelle une version dérivée, mais l’appelant ne spécifie pas quelle classe. Exemple:

 class Foo { public: void M() {...} }; class Bar : public Foo { public: void M() {...} }; void Try() { xxx::M(); } int main() { Try(); } 

Vous voulez que Try () appelle la version Bar de M sans spécifier Bar. La façon dont vous faites cela pour la statique consiste à utiliser un modèle. Alors changez comme ça:

 class Foo { public: void M() {...} }; class Bar : public Foo { public: void M() {...} }; template  void Try() { T::M(); } int main() { Try(); } 

Non, ce n’est pas possible, car les membres statiques sont liés au moment de la compilation, tandis que les membres virtuels sont liés au moment de l’exécution.

Premièrement, les réponses sont correctes: ce que demande le PO est une contradiction dans les termes: les méthodes virtuelles dépendent du type d’exécution d’une instance; Les fonctions statiques ne dépendent pas spécifiquement d’une instance, mais uniquement d’un type. Cela dit, il est logique que les fonctions statiques renvoient quelque chose de spécifique à un type. Par exemple, j’avais une famille de classes MouseTool pour le pattern State et j’ai commencé à avoir une fonction statique renvoyant le modificateur de clavier qui l’accompagnait; J’ai utilisé ces fonctions statiques dans la fonction usine qui a créé l’instance MouseTool correcte. Cette fonction vérifiait l’état de la souris contre MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier (), etc. Bien sûr, plus tard, je voulais vérifier si l’état était correct, donc je voulais écrire quelque chose comme “if (keyboardModifier == dynamic_type (* state) :: keyboardModifier ())” (pas la vraie syntaxe C ++). .

Donc, si vous le souhaitez, vous voudrez peut-être modifier votre solution. Néanmoins, je comprends le désir d’avoir des méthodes statiques, puis de les appeler dynamicment en fonction du type dynamic d’une instance. Je pense que le Pattern visiteur peut vous donner ce que vous voulez. Cela vous donne ce que vous voulez. C’est un peu de code supplémentaire, mais cela pourrait être utile pour les autres visiteurs.

Voir: http://en.wikipedia.org/wiki/Visitor_pattern pour le fond.

 struct ObjectVisitor; struct Object { struct TypeInformation; static TypeInformation GetTypeInformation(); virtual void accept(ObjectVisitor& v); }; struct SomeObject : public Object { static TypeInformation GetTypeInformation(); virtual void accept(ObjectVisitor& v) const; }; struct AnotherObject : public Object { static TypeInformation GetTypeInformation(); virtual void accept(ObjectVisitor& v) const; }; 

Ensuite, pour chaque object concret:

 void SomeObject::accept(ObjectVisitor& v) const { v.visit(*this); // The comstackr statically picks the visit method based on *this being a const SomeObject&. } void AnotherObject::accept(ObjectVisitor& v) const { v.visit(*this); // Here *this is a const AnotherObject& at comstack time. } 

puis définissez le visiteur de base:

 struct ObjectVisitor { virtual ~ObjectVisitor() {} virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like. virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like. // More virtual void visit() methods for each Object class. }; 

Ensuite, le visiteur concret qui sélectionne la fonction statique appropriée:

 struct ObjectVisitorGetTypeInfo { Object::TypeInformation result; virtual void visit(const SomeObject& o) { result = SomeObject::GetTypeInformation(); } virtual void visit(const AnotherObject& o) { result = AnotherObject::GetTypeInformation(); } // Again, an implementation for each concrete Object. }; 

enfin, utilisez-le:

 void printInfo(Object& o) { ObjectVisitorGetTypeInfo getTypeInfo; Object::TypeInformation info = o.accept(getTypeInfo).result; std::cout << info << std::endl; } 

Remarques:

  • La constance laissée comme exercice.
  • Vous avez renvoyé une référence d'un fichier statique. À moins d'avoir un singleton, c'est discutable.

Si vous voulez éviter les erreurs de copier-coller où l'une de vos méthodes de visite appelle la mauvaise fonction statique, vous pouvez utiliser une fonction d'assistance basée sur un modèle (qui ne peut pas être virtuelle) avec votre visiteur avec un modèle comme celui-ci:

 struct ObjectVisitorGetTypeInfo { Object::TypeInformation result; virtual void visit(const SomeObject& o) { doVisit(o); } virtual void visit(const AnotherObject& o) { doVisit(o); } // Again, an implementation for each concrete Object. private: template  void doVisit(const T& o) { result = T::GetTypeInformation(); } }; 

Peut-être que vous pouvez essayer ma solution ci-dessous:

 class Base { public: Base(void); virtual ~Base(void); public: virtual void MyVirtualFun(void) = 0; static void MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); } private: static Base* mSelf; }; Base::mSelf = NULL; Base::Base(void) { mSelf = this; } Base::~Base(void) { // please never delete mSelf or reset the Value of mSelf in any deconstructors } class DerivedClass : public Base { public: DerivedClass(void) : Base() {} ~DerivedClass(void){} public: virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"< 

Comme d’autres l’ont dit, il existe deux informations importantes:

  1. il n’y a pas this pointeur lors d’un appel de fonction statique et
  2. le pointeur this pointe sur la structure où la table virtuelle, ou thunk, est utilisée pour rechercher la méthode d’exécution à appeler.

Une fonction statique est déterminée au moment de la compilation.

J’ai montré cet exemple de code dans les membres statiques C ++ en classe ; il montre que vous pouvez appeler une méthode statique avec un pointeur nul:

 struct Foo { static int boo() { return 2; } }; int _tmain(int argc, _TCHAR* argv[]) { Foo* pFoo = NULL; int b = pFoo->boo(); // b will now have the value 2 return 0; }