Domanda

Esiste comunque una sorta di membro statico virtuale in C++?

Per esempio:

class BaseClass {
    public:
        BaseClass(const string& name) : _name(name) {}
        string GetName() const { return _name; }
        virtual void UseClass() = 0;
    private:
        const string _name;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() : BaseClass("DerivedClass") {}
        virtual void UseClass() { /* do something */ }
};

So che questo esempio è banale, ma se ho un vettore di dati complessi che sarà sempre lo stesso per tutte le classi derivate ma è necessario accedervi dai metodi della classe base?

class BaseClass {
    public:
        BaseClass() {}
        virtual string GetName() const = 0;
        virtual void UseClass() = 0;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() {}
        virtual string GetName() const { return _name; }
        virtual void UseClass() { /* do something */ }
    private:
        static const string _name;
};

string DerivedClass::_name = "DerivedClass";

Questa soluzione non mi soddisfa perché ho bisogno di reimplementare il membro _name e la sua funzione di accesso GetName() in ogni classe.Nel mio caso ho diversi membri che seguono il comportamento _name e i decimi delle classi derivate.

Qualche idea?

È stato utile?

Soluzione

Ecco una soluzione:

struct BaseData
{
  const string my_word;
  const int my_number;
};

class Base
{
public:
    Base(const BaseData* apBaseData)
    {
        mpBaseData = apBaseData;
    }
    const string getMyWord()
    {
        return mpBaseData->my_word;
    }
    int getMyNumber()
    {
        return mpBaseData->my_number;
    }
private:
    const BaseData* mpBaseData;
};

class Derived : public Base
{
public:
    Derived() : Base(&sBaseData)
    {
    }
private:
    static BaseData sBaseData;
}

BaseData Derived::BaseData = { "Foo", 42 };

Altri suggerimenti

Sembra che la risposta sia nella domanda: il metodo che hai suggerito sembra essere la giusta direzione da seguire, tranne per il fatto che se hai un numero elevato di membri condivisi potresti volerli riunire in una struttura o classe e superarli come l'argomento al costruttore della classe base.

Se insisti affinché i membri "condivisi" vengano implementati come membri statici della classe derivata, potresti essere in grado di generare automaticamente il codice delle classi derivate.XSLT è un ottimo strumento per generare automaticamente classi semplici.

In generale, l'esempio non mostra la necessità di membri "statici virtuali", perché per scopi come questi in realtà non hai bisogno dell'ereditarietà - invece dovresti usare la classe base e farle accettare i valori appropriati nel costruttore - forse creando una singola istanza degli argomenti per ogni "sottotipo" e passandovi un puntatore per evitare la duplicazione dei dati condivisi.Un altro approccio simile consiste nell'utilizzare i modelli e passare come argomento del modello una classe che fornisce tutti i valori rilevanti (questo è comunemente indicato come modello "Policy").

Per concludere, ai fini dell'esempio originale, non sono necessari tali membri "virtuali statici".Se ritieni ancora che siano necessari per il codice che stai scrivendo, prova a elaborarlo e ad aggiungere più contesto.

Esempio di quanto ho descritto sopra:

class BaseClass {
    public:
        BaseClass(const Descriptor& desc) : _desc(desc) {}
        string GetName() const { return _desc.name; }
        int GetId() const { return _desc.Id; }
        X GetX() connst { return _desc.X; }
        virtual void UseClass() = 0;
    private:
        const Descriptor _desc;
};


class DerivedClass : public BaseClass {
    public:
        DerivedClass() : BaseClass(Descriptor("abc", 1,...)) {}
        virtual void UseClass() { /* do something */ }
};

class DerDerClass : public BaseClass {
    public:
        DerivedClass() : BaseClass("Wowzer", 843,...) {}
        virtual void UseClass() { /* do something */ }
};

Vorrei approfondire questa soluzione e magari fornire una soluzione al problema della deinizializzazione:

Con una piccola modifica è possibile implementare il progetto sopra descritto senza necessariamente creare una nuova istanza del "descrittore" per ogni istanza di una classe derivata.

Puoi creare un oggetto singleton, DescriptorMap, che conterrà la singola istanza di ciascun descrittore e utilizzarlo durante la costruzione degli oggetti derivati ​​in questo modo:

enum InstanceType {
    Yellow,
    Big,
    BananaHammoc
}

class DescriptorsMap{
    public:
        static Descriptor* GetDescriptor(InstanceType type) {
            if ( _instance.Get() == null) {
                _instance.reset(new DescriptorsMap());
            }
            return _instance.Get()-> _descriptors[type];
        }
    private:
        DescriptorsMap() {
            descriptors[Yellow] = new Descriptor("Yellow", 42, ...);
            descriptors[Big] = new Descriptor("InJapan", 17, ...)
            ...
        }

        ~DescriptorsMap() {
            /*Delete all the descriptors from the map*/
        }

        static autoptr<DescriptorsMap> _instance;
        map<InstanceType, Descriptor*> _descriptors;
}

Ora possiamo fare questo:

class DerivedClass : public BaseClass {
    public:
        DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.BananaHammoc)) {}
        virtual void UseClass() { /* do something */ }
};

class DerDerClass : public BaseClass {
    public:
        DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.Yellow)) {}
        virtual void UseClass() { /* do something */ }
};

Alla fine dell'esecuzione, quando il runtime C esegue le deinizializzazioni, chiama anche il distruttore di oggetti statici, incluso il nostro autoptr, che cancella la nostra istanza di DescriptorsMap.

Quindi ora abbiamo una singola istanza di ciascun descrittore che viene anch'essa eliminata alla fine dell'esecuzione.

Si noti che se l'unico scopo della classe derivata è fornire i dati "descrittori" rilevanti (ad es.invece di implementare funzioni virtuali), dovresti accontentarti di rendere la classe base non astratta e creare ogni volta un'istanza con il descrittore appropriato.

Sono d'accordo con il suggerimento di Hershi di utilizzare un modello come "classe base".Da quello che stai descrivendo, sembra più un utilizzo dei modelli piuttosto che una sottoclasse.

Potresti creare un modello come segue (non ho provato a compilarlo):


template <typename T>
class Object
{
public:

  Object( const T& newObject ) : yourObject(newObject) {} ;
  T GetObject() const { return yourObject } ;
  void SetObject( const T& newObject ) { yourObject = newObject } ;

protected:

  const T yourObject ;
} ;

class SomeClassOne
{
public:

  SomeClassOne( const std::vector& someData )
  {
    yourData.SetObject( someData ) ;
  }

private:

  Object<std::vector<int>> yourData ;
} ;

Ciò ti consentirà di utilizzare i metodi della classe modello per modificare i dati secondo necessità dalle classi personalizzate che utilizzano i dati e condividono i vari aspetti della classe modello.

Se intendi utilizzare l'ereditarietà, potresti dover ricorrere alle "gioie" di utilizzare un puntatore void* nella tua BaseClass e gestire il casting, ecc.

Tuttavia, in base alla tua spiegazione, sembra che tu abbia bisogno di modelli e non di ereditarietà.

@Hershi:il problema con questo approccio è che ogni istanza di ciascuna classe derivata ha una copia dei dati, il che può essere in qualche modo costoso.

Forse potresti provare qualcosa del genere (sto scherzando senza un esempio di compilazione, ma l'idea dovrebbe essere chiara).


#include <iostream>
#include <string>
using namespace std;

struct DerivedData
{
  DerivedData(const string & word, const int number) :
    my_word(word), my_number(number) {}
  const string my_word;
  const int my_number;
};

class Base {
public:
  Base() : m_data(0) {}
  string getWord() const { return m_data->my_word; }
  int getNumber() const { return m_data->my_number; }
protected:
  DerivedData * m_data;
};


class Derived : public Base {
public:
  Derived() : Base() {
    if(Derived::s_data == 0) {
      Derived::s_data = new DerivedData("abc", 1);
    }
    m_data = s_data;
  }
private:
  static DerivedData * s_data;
};


DerivedData * Derived::s_data = 0; 

int main()
{
  Base * p_b = new Derived();
  cout getWord() << endl;
}

Per quanto riguarda la domanda successiva sull'eliminazione dell'oggetto statico:l'unica soluzione che mi viene in mente è usare un puntatore intelligente, qualcosa come Potenzia il puntatore condiviso.

Sembra che tu stia cercando di evitare di dover duplicare il codice nelle classi foglia, quindi perché non derivare semplicemente una classe base intermedia dalla classe base.questa classe intermedia può contenere dati statici e far derivare tutte le classi foglia dalla classe base intermedia.Ciò presuppone che si desideri un dato statico conservato su tutte le classi derivate, come sembra così dal tuo esempio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top