Membri virtuali statici C ++?


140

In C ++ è possibile avere una funzione membro che è sia statice che virtual? Apparentemente, non esiste un modo semplice per farlo ( static virtual member();è un errore di compilazione), ma c'è almeno un modo per ottenere lo stesso effetto?

IE:

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

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

Ha senso usare GetTypeInformation()sia su un'istanza ( object->GetTypeInformation()) che su una classe ( SomeObject::GetTypeInformation()), che può essere utile per i confronti e vitale per i template.

L'unico modo in cui riesco a pensare è scrivere due funzioni / una funzione e una costante, per classe, o usare macro.

Qualche altra soluzione?


12
Solo un commento a margine: i metodi statici non vengono eseguiti in nessuna istanza, il che significa che non hanno implicito questo puntatore. Detto questo, la constfirma in un metodo contrassegna il thispuntatore implicito come costante e non può essere applicato ai metodi statici poiché mancano del parametro implicito.
David Rodríguez - dribeas,

2
@cvb: riconsidererei seriamente di sostituire il tuo esempio con il codice che non implica la riflessione. A questo punto, adesso stai unendo due problemi separati (anche se correlati). Sì, e so che sono passati 5 anni e mezzo da quando l'hai chiesto.
einpoklum,

Una delle funzionalità implicitamente richieste qui è che il compilatore controlli che ciascun oggetto in una gerarchia implementa un'interfaccia specifica (in cui uno o più metodi sono statici). Fondamentalmente, un puro controllo virtuale per il metodo statico ha molto senso, poiché se si dimentica di aggiungere il metodo statico, il compilatore dovrebbe errori. virtuale non è la parola chiave qui, è più astratto che sembra essere una specie di sinonimi in C ++, tranne per questo caso specifico. Purtroppo al momento non è possibile farlo con C ++.
xryl669,

Risposte:


75

No, non c'è modo di farlo, dal momento che cosa succederebbe quando chiamassi Object::GetTypeInformation()? Non può sapere quale versione della classe derivata chiamare poiché non è associato alcun oggetto.

Dovrai renderlo una funzione virtuale non statica per funzionare correttamente; se vuoi anche essere in grado di chiamare la versione di una specifica classe derivata non virtualmente senza un'istanza di oggetto, dovrai fornire anche una seconda versione statica non virtuale ridondante.


8
Se si pensa di classe statica (o classi di membri statici) come Singleton, tutto diventa evidente - nel vostro caso è sufficiente oggetto :: GetTypeInformation dovrebbe essere chiamato - allo stesso modo di una chiamata al metodo virtuale regolare classe base dell'istanza . (Naturalmente, se C ++ supportava i metodi statici virtuali)
Spook il

13
Questa è una discussione completamente speciosa. Se usi la classe invece di un oggetto, userebbe naturalmente la versione di quella classe, invece di fare virtual-dispatch. Niente di nuovo lì.
Deduplicatore

54

Molti dicono che non è possibile, farei un ulteriore passo avanti e direi che non è significativo.

Un membro statico è qualcosa che non riguarda alcuna istanza, ma solo la classe.

Un membro virtuale è qualcosa che non si collega direttamente a nessuna classe, ma solo a un'istanza.

Quindi un membro virtuale statico sarebbe qualcosa che non si riferisce a nessuna istanza o classe.


42
È perfettamente significativo nei linguaggi in cui le classi sono valori di prima classe, ad esempio Delphi lo possiede e ha anche metodi "statici virtuali".
Pavel Minaev,

4
Esattamente. Una "funzione virtuale" è (per definizione) una funzione che è collegata dinamicamente , cioè viene scelta in fase di esecuzione in base al tipo dinamico di un determinato oggetto. Quindi, nessun oggetto = nessuna chiamata virtuale.
Kos,

7
Penso anche che i virtual statici siano significativi. Sarebbe possibile definire le classi di interfaccia e includere metodi statici che devono essere implementati nella classe derivata.
bkausbk,

34
Non è così significativo per un static virtualmetodo, ma un metodo static puro virtual è molto significativo in un'interfaccia.
Bret Kuhns,

4
È perfettamente significativo avere un static const string MyClassSillyAdditionalName.
einpoklum,

23

L'altro giorno ho riscontrato questo problema: avevo alcune classi piene di metodi statici ma volevo usare l'ereditarietà e i metodi virtuali e ridurre la ripetizione del codice. La mia soluzione era:

Invece di utilizzare metodi statici, utilizzare un singleton con metodi virtuali.

In altre parole, ogni classe dovrebbe contenere un metodo statico chiamato per ottenere un puntatore a una singola istanza condivisa della classe. È possibile rendere privati ​​o protetti i veri costruttori in modo che il codice esterno non possa utilizzarlo in modo errato creando istanze aggiuntive.

In pratica, l'uso di un singleton è molto simile all'utilizzo di metodi statici, tranne per il fatto che è possibile sfruttare l'ereditarietà e i metodi virtuali.


Questo mi costerà delle prestazioni, a meno che il compilatore non sia certo che: 1. In realtà è un singleton e 2. Nulla eredita da esso, non penso che possa ottimizzare tutto il sovraccarico.
einpoklum,

Se la performance di questo genere di cose ti preoccupa, allora C # è probabilmente la lingua sbagliata per te.
Nate CK,

3
Ah, buon punto. Ovviamente è da un po 'che non ci penso, da quando l'ho scritto nel 2009. Vorrei dirlo in un altro modo: se questo tipo di performance ti preoccupa, forse dovresti evitare l'uso dell'eredità del tutto. Il poster chiedeva specificamente metodi virtuali, quindi è strano che tu venga qui per lamentarti del sovraccarico di metodi virtuali.
Nate CK,

15

È possibile!

Ma cosa è esattamente possibile, restringiamo. Le persone spesso desiderano un qualche tipo di "funzione virtuale statica" a causa della duplicazione del codice necessario per poter chiamare la stessa funzione tramite la chiamata statica "SomeDerivedClass :: myfunction ()" e la chiamata polimorfica "base_class_pointer-> myfunction ()". Il metodo "legale" per consentire tale funzionalità è la duplicazione delle definizioni delle funzioni:

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(); }
};

Che cosa succede se la classe base ha un gran numero di funzioni statiche e la classe derivata deve sovrascrivere ognuna di esse e si dimentica di fornire una definizione duplicata per la funzione virtuale. Bene, avremo alcuni strani errori durante il runtime che è difficile da rintracciare. Causa la duplicazione del codice è una cosa negativa. Quanto segue prova a risolvere questo problema (e voglio dire in anticipo che è completamente sicuro per i tipi e non contiene alcuna magia nera come quella di typeid o dynamic_cast :)

Quindi, vogliamo fornire solo una definizione di getTypeInformation () per classe derivata ed è ovvio che deve essere una definizione di staticfunzione perché non è possibile chiamare "SomeDerivedClass :: getTypeInformation ()" se getTypeInformation () è virtuale. Come possiamo chiamare la funzione statica della classe derivata tramite il puntatore alla classe base? Non è possibile con vtable perché vtable archivia i puntatori solo a funzioni virtuali e poiché abbiamo deciso di non utilizzare le funzioni virtuali, non possiamo modificare vtable a nostro vantaggio. Quindi, per poter accedere alla funzione statica per la classe derivata tramite il puntatore alla classe base, dobbiamo in qualche modo memorizzare il tipo di un oggetto all'interno della sua classe base. Un approccio consiste nel rendere la classe base modellata utilizzando "modello di modello curiosamente ricorrente", ma qui non è appropriato e useremo una tecnica chiamata "cancellazione del tipo":

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

Ora possiamo memorizzare il tipo di un oggetto all'interno della classe base "Object" con una variabile "keeper":

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

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

};

In una classe derivata il custode deve essere inizializzato durante la costruzione:

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

Aggiungiamo lo zucchero sintattico:

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

Ora le dichiarazioni dei discendenti sembrano:

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

utilizzo:

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()

vantaggi:

  1. meno duplicazione del codice (ma dobbiamo chiamare OVERRIDE_STATIC_FUNCTIONS in ogni costruttore)

svantaggi:

  1. OVERRIDE_STATIC_FUNCTIONS in ogni costruttore
  2. sovraccarico di memoria e prestazioni
  3. maggiore complessità

Questioni aperte:

1) ci sono nomi diversi per funzioni statiche e virtuali come risolvere l'ambiguità qui?

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) come chiamare implicitamente OVERRIDE_STATIC_FUNCTIONS all'interno di ogni costruttore?


+1 per sforzo, anche se non sono sicuro che sia più elegante della semplice delega della funzionalità a un singleton con metodi virtuali.
einpoklum,

1
@einpoklum, posso pensare a una situazione in cui questa può essere preferibile. Supponiamo di avere un sacco di codice client che chiama già metodi statici. Il passaggio da metodi statici a singleton con metodi virtuali richiederebbe modifiche nel codice client mentre la soluzione presentata sopra non è invasiva.
Alsk,

La parola chiave "virtuale" non è richiesta per "Foo :: getTypeInformation" e "TypeKeeperImpl :: getTypeInformation".
bartolo-otrit,

12

Mentre Alsk ha già dato una risposta abbastanza dettagliata, vorrei aggiungere un'alternativa, poiché penso che la sua implementazione migliorata sia eccessivamente complicata.

Iniziamo con una classe base astratta, che fornisce l'interfaccia per tutti i tipi di oggetto:

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

Ora abbiamo bisogno di un'implementazione effettiva. Ma per evitare di dover scrivere sia i metodi statici sia quelli virtuali, avremo le nostre classi di oggetti reali ereditate dai metodi virtuali. Questo ovviamente funziona solo se la classe base sa come accedere alla funzione membro statico. Quindi dobbiamo usare un modello e passargli il nome della classe di oggetti reali:

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

Infine, dobbiamo implementare i nostri oggetti reali. Qui abbiamo solo bisogno di implementare la funzione membro statico, le funzioni membro virtuale saranno ereditate dalla classe modello ObjectImpl, istanziato con il nome della classe derivata, quindi accederà ai suoi membri statici.

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

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

Aggiungiamo un po 'di codice per testare:

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;
}

Addendum (12 gennaio 2019):

Invece di usare la funzione GetClassNameStatic (), puoi anche definire il nome della classe come membro statico, anche "inline", che IIRC funziona dal C ++ 11 (non farti spaventare da tutti i modificatori :)):

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

11

È possibile. Crea due funzioni: statica e virtuale

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
};

4
Inoltre, i metodi statici non possono essere const. Non ha senso, in quale caso non cambieranno?
David Rodríguez - dribeas,

1
Questo è principalmente solo la duplicazione del codice. L'idea è che le sottoclassi debbano solo avere il membro const statico, non avere il codice per accedervi.
einpoklum,

8

No, questo non è possibile, poiché le funzioni dei membri statici mancano di un thispuntatore. E i membri statici (sia le funzioni che le variabili) non sono in realtà membri della classe di per sé. Capita solo di essere invocati da ClassName::membere aderiscono agli identificatori di accesso alla classe. La loro memorizzazione è definita da qualche parte al di fuori della classe; l'archiviazione non viene creata ogni volta che è stata creata un'istanza di un oggetto della classe. I puntatori ai membri della classe sono speciali in semantica e sintassi. Un puntatore a un membro statico è un normale puntatore sotto tutti gli aspetti.

le funzioni virtuali in una classe richiedono il thispuntatore ed è molto accoppiato alla classe, quindi non possono essere statiche.


1
Solo le funzioni non statiche richiedono un this puntatore. le funzioni statiche non sono specifiche di un'istanza e non ne avrebbero bisogno. Quindi - questa non è una ragione per cui i membri statici virtuali sono impossibili.
einpoklum,

7

Bene, una risposta piuttosto tardiva, ma è possibile usare il modello di modello curiosamente ricorrente. Questo articolo di Wikipedia contiene le informazioni di cui hai bisogno e anche l'esempio del polimorfismo statico è quello che ti viene chiesto.


3

Penso che ciò che stai cercando di fare possa essere fatto attraverso i template. Sto cercando di leggere tra le righe qui. Quello che stai cercando di fare è chiamare un metodo da un codice, in cui chiama una versione derivata ma il chiamante non specifica quale classe. Esempio:

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

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

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

Vuoi che Try () chiami la versione Bar di M senza specificare Bar. Il modo in cui lo fai per la statica è usare un modello. Quindi cambiarlo in questo modo:

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

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

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

int main()
{
    Try<Bar>();
}

1
Se indenti il ​​tuo codice 4 spazi puoi ottenerlo formattato automaticamente. In alternativa, credo che sia possibile utilizzare il segno di spunta posteriore per raggiungere lo stesso scopo in linea.
Chollida,

1
Questo è l'ovvio che mi mancava. Grazie. Tuttavia, i membri pubici sono strani.
allesblinkt,

M () non è una funzione statica. come si chiama T :: M ()?
DDukDDak99

3

No, la funzione di membro statico non può essere virtuale. Poiché il concetto virtuale viene risolto in fase di esecuzione con l'aiuto di vptr e vptr è membro non statico di una classe. A causa di tale funzione di membro statico non è possibile accedere a vptr in modo che un membro statico possa essere virtuale.


2
Solo i metodi virtuali specifici dell'istanza richiedono vtable delle istanze. Potresti avere una vtable statica - una per classe -. E se vuoi che le istanze sappiano, basta puntare dalla vtable dell'istanza anche alla vtable della statica della classe.
einpoklum,

2

Non è possibile, ma è solo perché un'omissione. Non è qualcosa che "non ha senso", come molti sostengono. Per essere chiari, sto parlando di qualcosa del genere:

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.
}

Questo è al 100% qualcosa che potrebbe essere implementato (semplicemente non l'ha fatto), e direi qualcosa di utile.

Considera come funzionano le normali funzioni virtuali. Rimuovi la statics e aggiungi alcune altre cose e abbiamo:

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();
}

Funziona bene e in pratica ciò che accade è che il compilatore crea due tabelle, chiamate VTables, e assegna indici alle funzioni virtuali come questa

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
};

Successivamente ogni classe con funzioni virtuali viene aumentata con un altro campo che punta al suo VTable, quindi il compilatore sostanzialmente li cambia in questo modo:

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

Quindi cosa succede realmente quando chiami b->sayMyName()? Fondamentalmente questo:

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

(Il primo parametro diventa this.)

Ok bene, quindi come funzionerebbe con le funzioni virtuali statiche? Bene, qual è la differenza tra le funzioni membro statiche e non statiche? L'unica differenza è che quest'ultimo ottiene un thispuntatore.

Possiamo fare esattamente lo stesso con le funzioni virtuali statiche: basta rimuovere il thispuntatore.

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

Questo potrebbe quindi supportare entrambe le sintassi:

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

Quindi ignora tutti gli oppositori. Si fa senso. Perché non è supportato allora? Penso che sia perché ha pochissimi benefici e potrebbe anche essere un po 'confuso.

L'unico vantaggio tecnico rispetto a una normale funzione virtuale è che non è necessario passare thisalla funzione, ma non credo che farebbe alcuna differenza misurabile per le prestazioni.

Significa che non hai una funzione statica e non statica separata per i casi in cui hai un'istanza e quando non hai un'istanza, ma potrebbe anche confondere il fatto che sia veramente "virtuale" solo quando usi la chiamata di istanza.


0

No, non è possibile, poiché i membri statici sono associati al momento della compilazione, mentre i membri virtuali sono associati durante l'esecuzione.


0

In primo luogo, le risposte sono corrette sul fatto che ciò che l'OP richiede è una contraddizione in termini: i metodi virtuali dipendono dal tipo di runtime di un'istanza; Le funzioni statiche in particolare non dipendono da un'istanza, ma solo da un tipo. Detto questo, ha senso che le funzioni statiche restituiscano qualcosa di specifico per un tipo. Ad esempio, avevo una famiglia di classi MouseTool per il modello di stato e ho iniziato ad avere ciascuna una funzione statica che restituiva il modificatore di tastiera che lo accompagnava; Ho usato quelle funzioni statiche nella funzione di fabbrica che hanno creato l'istanza corretta di MouseTool. Tale funzione ha verificato lo stato del mouse rispetto a MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier (), ecc., Quindi ha istanziato quello appropriato. Ovviamente in seguito volevo verificare se lo stato era giusto, quindi volevo scrivere qualcosa del tipo "

Quindi, se ti ritrovi a desiderare questo, potresti voler riprovare la tua soluzione. Tuttavia, capisco il desiderio di avere metodi statici e quindi li chiamo dinamicamente in base al tipo dinamico di un'istanza. Penso che il modello visitatore possa darti quello che vuoi. Ti dà quello che vuoi. È un po 'di codice aggiuntivo, ma potrebbe essere utile per altri visitatori.

Vedi: http://en.wikipedia.org/wiki/Visitor_pattern per lo sfondo.

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;
};

Quindi per ogni oggetto concreto:

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.
}

e quindi definire il visitatore 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.
};

Quindi il visitatore concreto che seleziona la funzione statica appropriata:

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.
};

infine, usalo:

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

Appunti:

  • Costanza lasciata come esercizio.
  • Hai restituito un riferimento da una statica. A meno che tu non abbia un singleton, è discutibile.

Se vuoi evitare errori di copia e incolla in cui uno dei tuoi metodi di visita chiama la funzione statica errata, puoi utilizzare una funzione di supporto basata su modelli (che non può essere essa stessa virtuale) per il tuo visitatore con un modello come questo:

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();
    }
};

i metodi statici virtuali, se esistessero, non dipenderebbero da nulla in un'istanza, ma l'istanza avrebbe bisogno di conoscerne il tipo per invocarli. Questo può essere risolto da un compilatore (ad esempio utilizzando una struttura di dati singola per classe con puntatori a metodi e membri statici virtuali). Non è certamente una contraddizione in termini.
einpoklum,

Che si tratti o meno di una contraddizione in termini è una questione di semantica. Si potrebbe immaginare il C ++ che consenta di chiamare la statica da un'istanza (ad esempio, Foo foo; ... foo::bar();invece di Foo::bar();). Non è diverso, decltype(foo)::bar();ma sarebbe di nuovo legato staticamente. L'approccio del visitatore sembra un modo ragionevole per ottenere questo comportamento senza solo rendere il metodo statico un metodo const virtuale.
Ben

0

Con c ++ puoi usare l'ereditarietà statica con il metodo crt. Ad esempio, è ampiamente utilizzato sul modello di finestra atl & wtl.

Vedi https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Per essere semplici, hai una classe che è modellata da se stessa come la classe myclass: myancestor pubblico. Da questo punto la classe myancestor ora può chiamare la tua funzione statica T :: YourImpl.


-1

Forse puoi provare la mia soluzione di seguito:

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;
}

Sì, lo so, 4 anni. Spiegare il punteggio per coloro che non vogliono leggere il codice in così tanti dettagli. Base::mSelfsi riferisce all'istanza costruita PIÙ RECENTEMENTE di qualsiasi classe derivata, anche se quell'istanza è stata distrutta . quindi class D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */che NON è ciò che si vuole
Jesse Chisholm,

-3

Come altri hanno già detto, ci sono 2 importanti informazioni:

  1. non c'è thispuntatore quando si effettua una chiamata di funzione statica e
  2. il thispuntatore punta alla struttura in cui la tabella virtuale, o thunk, viene utilizzata per cercare quale metodo di runtime chiamare.

Una funzione statica viene determinata al momento della compilazione.

Ho mostrato questo esempio di codice nei membri statici C ++ in classe ; mostra che puoi chiamare un metodo statico dato un puntatore nullo:

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;
}

6
Tecnicamente, si tratta di un comportamento indefinito. Non puoi deferire un puntatore nullo per qualsiasi motivo. L'unica cosa che puoi fare con un puntatore null è a) assegnargli un altro puntatore eb) confrontarlo con un altro puntatore.
KeithB,

1
Inoltre, puoi solo confrontarlo per l'uguaglianza (o la disuguaglianza_ con un altro puntatore, non per ordinare. Cioè p < null, p >= nullecc. Sono anche tutti indefiniti.
Pavel Minaev

1
@KeithB - ​​Per completezza puoi anche chiamare in modo sicuro delete su un puntatore null.
Steve Rowe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.