C++ membres virtuels statiques?

est-il possible en C++ d'avoir une fonction de membre qui est à 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 est-il au moins un moyen d'obtenir le même effet?

I. E:

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() à la fois sur une instance ( object->GetTypeInformation() ) et sur une classe ( SomeObject::GetTypeInformation() ), ce qui peut être utile pour les comparaisons et vital pour les gabarits.

les seules façons dont je peux penser implique d'écrire deux fonctions / une fonction et une constante, par classe, ou utiliser des macros.

D'autres solutions?

127
demandé sur Null 2009-11-30 18:26:46

15 réponses

Non, il n'y a aucun moyen de le faire, car que se passerait-il si vous appeliez Object::GetTypeInformation() ? Il ne peut pas savoir quelle version de classe dérivée appeler puisqu'il n'y a aucun objet associé avec elle.

vous devrez en faire une fonction virtuelle non statique pour fonctionner correctement; si vous voulez également être en mesure d'appeler la version d'une classe dérivée spécifique non-virtuellement sans une instance objet, vous devrez fournir une deuxième version non virtuelle statique redunduant ainsi.

71
répondu Adam Rosenfield 2009-11-30 15:30:23

beaucoup disent que ce n'est pas possible, j'irais un peu plus loin 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 se rapporte pas directement à une classe, mais seulement à une instance.

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

53
répondu Rasmus Kaj 2009-11-30 15:51:35

j'ai rencontré ce problème l'autre jour: j'avais des classes pleines de méthodes statiques mais je voulais utiliser l'héritage 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 vers un unique, partagé instance de la classe. Vous pouvez faire du vrai les constructeurs privés ou protégés de sorte que le code extérieur ne peut pas l'utiliser à mauvais escient en créant des instances supplémentaires.

dans la pratique, utiliser un singleton est un peu comme utiliser des méthodes statiques, sauf que vous pouvez profiter de l'héritage et des méthodes virtuelles.

16
répondu Nate C-K 2012-08-22 16:44:48

c'est possible!

mais ce qui est exactement possible, nous allons réduire. 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 par appel statique" SomeDerivedClass::myfunction () "et appel polymorphe"base_class_pointer->myfunction ()". La méthode "légale" permettant une telle fonctionnalité est la duplication des définitions de fonction:

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

Que si la base de la classe a un grand nombre de fonctions statiques et la classe dérivée doit surpasser chacune d'elles et on a oublié de fournir une définition de duplication pour la fonction virtuelle. Bien, nous allons avoir une erreur étrange pendant runtime qui est difficile à retrouver. Parce que la duplication du code est une mauvaise chose. Ce qui suit tente de résoudre ce problème (et je tiens à dire à l'avance qu'il est complètement sans type et ne contient pas de magie noire comme typeid ou dynamic_cast :)

ainsi, nous voulons fournir une seule définition de getTypeInformation() par classe dérivée et il est évident qu'il doit s'agir d'une définition de fonction statique parce qu'il n'est pas possible d'appeler" Somerderivedclass::getTypeInformation () " si getTypeInformation() est virtuel. Comment Pouvons-nous appeler la fonction statique de la classe dérivée par pointer vers la classe de base? Il n'est pas possible avec vtable parce que vtable stocke des pointeurs seulement aux fonctions virtuelles et depuis nous avons décidé de ne pas utiliser de fonctions virtuelles, nous ne pouvons pas modifier vtable à notre avantage. Ensuite, pour pouvoir accéder à la fonction statique pour la classe dérivée à travers pointeur vers la classe de base, nous devons stocker d'une manière ou d'une autre le type d'un objet dans sa classe de base. Une approche consiste à classifier les bases selon un modèle en utilisant "curieusement un modèle de modèle récurrent", mais ce n'est pas approprié ici et nous utiliserons une technique appelée "effacement de type":

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

maintenant nous pouvons stocker le type d'objet dans la classe de base "objet" avec une variable "keeper":

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

dans un Class keeper dérivé doit être initialisé pendant la construction:

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

ajoutons du sucre syntaxique:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#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 string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

utilisation:

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 du code (mais nous avoir appeler OVERRIDE_STATIC_FUNCTIONS in every le constructeur)

désavantages:

  1. OVERRIDE_STATIC_FUNCTIONS in every constructeur
  2. mémoire et performance frais généraux
  3. complexité accrue

questions en suspens:

1) Il existe 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?

12
répondu Alsk 2014-10-10 13:43:35

bien Qu'Alsk ait déjà donné une réponse assez détaillée, j'aimerais ajouter 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'objets:

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

maintenant nous avons besoin d'une mise en œuvre réelle. Mais pour éviter d'avoir à écrire à la fois les méthodes statiques et les méthodes virtuelles, nous aurons nos classes d'objets actuelles hériter le virtuel méthode. Ce n'est évidemment seulement si la classe de base sait comment accéder à la fonction membre statique. Nous devons donc utiliser un modèle et lui passer le nom de classe des objets réels:

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

Enfin nous avons besoin pour mettre en œuvre notre objet réel(s). Ici, nous n'avons qu'à implémenter la fonction de membre statique, les fonctions de membre virtuel seront héritées de la classe template ObjectImpl, instanciée avec le nom de la classe dérivée, de sorte qu'elle accèdera à ses fonctions statiques. membre.

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

class YourObject : public ObjectImpl<YourObject>
{
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;
}
11
répondu Timo 2015-02-15 09:10:08

c'est possible. Faire deux fonctions: statique et virtuel

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
};
10
répondu Alexey Malistov 2009-12-01 09:27:03

non, ce n'est pas possible, parce que les fonctions statiques des membres n'ont pas de pointeur this . Et les membres statiques (à la fois les fonctions et les variables) ne sont pas vraiment des membres de classe en soi. Il se trouve qu'ils sont simplement invoqués par ClassName::member , et adhèrent aux spécificateurs d'accès de classe. Leur stockage est défini quelque part en dehors de la classe; le stockage n'est pas créé chaque fois que vous instanciez un objet de la classe. Les pointeurs vers les membres de la classe sont spéciaux en sémantique et syntaxe. Un pointeur statique membre est un pointeur normal à tous égards.

fonctions virtuelles dans une classe a besoin du pointeur this , et est très couplé à la classe, donc ils ne peuvent pas être statiques.

8
répondu 2009-11-30 15:33:54

Eh bien, une réponse assez tardive, mais il est possible en utilisant le modèle de gabarit curieusement récurrents. Ce wikipedia article a l'information dont vous avez besoin et aussi l'exemple sous polymorphisme statique est ce que vous êtes demandé pour.

6
répondu tropicana 2013-02-05 00:25:44

non, la fonction de membre statique ne peut pas être virtuelle .depuis concept virtuel est résolu au moment de l'exécution avec l'aide de vptr, et vptr est non membre statique d'une classe.en raison de cette fonction de membre statique ne peut pas acess vptr donc membre statique ne peut pas être virtuel.

3
répondu Prabhat Kumar 2015-06-26 05:30:24

je pense que ce que vous essayez de faire peut être fait à travers des modèles. Je suis en train de lire entre les lignes ici. Ce que vous essayez de faire est d'appeler une méthode à partir d'un code, où il 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 essayer() d'appeler la version barre de M sans spécifier la barre. La façon dont vous faites cela pour la statique est d'utiliser un modèle. Alors changez-le comme ceci:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}
2
répondu zumalifeguard 2012-06-20 01:26:28

non, ce n'est pas possible, puisque les membres statiques sont liés au moment de la compilation, tandis que les membres virtuels sont liés à l'exécution.

0
répondu PaulJWilliams 2009-11-30 15:35:54

tout d'abord, les réponses sont correctes, ce que L'OP demande est une contradiction en termes: les méthodes virtuelles dépendent du type d'exécution d'une instance; les fonctions statiques ne dépendent spécifiquement pas d'une instance -- juste d'un type. Cela dit, il est logique d'avoir des fonctions statiques retourner quelque chose de spécifique à un type. Par exemple, j'avais une famille de classes MouseTool pour le motif D'état et j'ai commencé à avoir chacune une fonction statique retournant le modificateur de clavier qui allait avec cela, j'ai utilisé ces fonctions statiques dans la fonction de l'usine qui a fait la bonne instance MouseTool. Cette fonction vérifiait l'état de la souris contre MouseToolA:: keyboardModifier(), MouseToolB::keyboardModifier (), etc. et puis instancié la. Bien sûr, plus tard, j'ai voulu vérifier si l'État était correct, donc j'ai voulu écrire quelque chose comme "si (keyboardModifier == dynamic_type(*state)::keyboardModifier())" (pas de vraie syntaxe C++), ce qui est ce que cette question demande.

donc, si vous vous trouvez à vouloir cela, vous pourriez vouloir retoucher votre solution. Néanmoins, je comprends le désir d'avoir des méthodes statiques et de les appeler dynamiquement basées sur le type dynamique d'une instance. Je pense que le Modèle Visiteur peut vous donner ce que vous voulez. Il vous donne ce que vous voulez. C'est un peu de code supplémentaire, mais il pourrait être utile pour d'autres visiteurs.

voir: http://en.wikipedia.org/wiki/Visitor_pattern pour background.

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 objet en béton:

void SomeObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // The compiler 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 compile time.
}

et ensuite définir 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.
};

puis le visiteur en béton 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;
}

Notes:

  • Constance left as an exercise.
  • vous avez renvoyé une référence d'un statique. Sauf si vous avez 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'aide Templé (qui ne peut pas être lui-même virtuel) t 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 <typename T>
    void doVisit(const T& o) {
        result = T::GetTypeInformation();
    }
};
0
répondu Ben 2013-08-21 11:55:19

ce n'est pas possible, mais c'est juste par omission. Ce n'est pas quelque chose qui "n'a pas de sens", comme beaucoup de gens semblent à la revendication. Pour être clair, je parle de quelque chose comme ceci:

struct Base {
  static virtual void sayMyName() {
    cout << "Base\n";
  }
};

struct Derived : public Base {
  static void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
  Derived::sayMyName(); // Also would work.
}

C'est 100% quelque chose que pourrait être mis en œuvre (il n'a tout simplement pas), et je dirais quelque chose qui est utile.

examiner comment les fonctions virtuelles normales fonctionnent. Supprimer le static s et ajouter dans certains d'autres choses et nous avons:

struct Base {
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
}

cela fonctionne bien et essentiellement ce qui se passe est que le compilateur fait deux tables, appelées VTables, et assigne des indices aux fonctions virtuelles comme celle-ci

enum Base_Virtual_Functions {
  sayMyName = 0;
  foo = 1;
};

using VTable = void*[];

const VTable Base_VTable = {
  &Base::sayMyName,
  &Base::foo
};

const VTable Derived_VTable = {
  &Derived::sayMyName,
  &Base::foo
};

Ensuite chaque classe avec des fonctions virtuelles est augmentée d'un autre champ qui pointe vers son VTable, donc le compilateur Les change fondamentalement pour être comme ceci:

struct Base {
  VTable* vtable;
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  VTable* vtable;
  void sayMyName() override {
    cout << "Derived\n";
  }
};

alors ce qui se passe réellement quand vous appeler b->sayMyName() ? Fondamentalement ceci:

b->vtable[Base_Virtual_Functions::sayMyName](b);

(le premier paramètre devient this .)

OK très bien, alors comment fonctionnerait-il avec les fonctions virtuelles statiques? Quelle est la différence entre les fonctions statiques et non statiques des membres? La seule différence est que ces derniers reçoivent un pointeur this .

nous pouvons faire exactement la même chose avec les fonctions virtuelles statiques - il suffit de supprimer le pointeur this .

b->vtable[Base_Virtual_Functions::sayMyName]();

cela pourrait alors supporter les deux syntaxes:

b->sayMyName(); // Prints "Base" or "Derived"...
Base::sayMyName(); // Always prints "Base".

ignorez donc tous les impies. ne faire sens. Pourquoi n'est-il pas pris en charge alors? Je pense que c'est parce qu'il a très peu d'avantages et peut-être même un peu déroutant.

le seul avantage technique par rapport à une fonction virtuelle normale est que vous n'avez pas besoin de passer this à la fonction mais je ne pense pas que cela ferait différence mesurable par rapport au rendement.

cela signifie que vous n'avez pas de fonction séparée statique et non-statique pour les cas où vous avez une instance, et quand vous n'avez pas d'instance, mais aussi qu'il pourrait être déroutant que ce ne soit vraiment" virtuel " que lorsque vous utilisez l'appel d'instance.

0
répondu Timmmm 2018-10-05 15:08:16

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!"<<endl; }
};

int main() {
    DerivedClass testCls;
    testCls.MyStaticFun(); //correct way to invoke this kind of static fun
    DerivedClass::MyStaticFun(); //wrong way
    return 0;
}
-1
répondu Jiez 2018-04-17 14:12:30

comme d'autres l'ont dit, il y a 2 informations importantes:

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

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

j'ai montré ce code exemple dans membres statiques C++ dans la classe ; il montre que vous pouvez appeler une méthode statique donné un pointeur null:

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;
}
-3
répondu chollida 2017-04-04 08:18:13