Frage

Gibt es überhaupt eine Art virtuelles statisches Mitglied in C++?

Zum Beispiel:

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

Ich weiß, dass dieses Beispiel trivial ist, aber wenn ich einen Vektor komplexer Daten habe, der für alle abgeleiteten Klassen immer gleich ist, auf den aber über Basisklassenmethoden zugegriffen werden muss?

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

Diese Lösung befriedigt mich nicht, da ich das Mitglied _name und seinen Accessor GetName() in jeder Klasse neu implementieren muss.In meinem Fall habe ich mehrere Mitglieder, die dem Verhalten von _name folgen, und Zehntel abgeleiteter Klassen.

Irgendeine Idee?

War es hilfreich?

Lösung

Hier ist eine Lösung:

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

Andere Tipps

Es scheint, als ob die Antwort in der Frage liegt – die von Ihnen vorgeschlagene Methode scheint die richtige Richtung zu sein, außer dass Sie, wenn Sie eine große Anzahl dieser gemeinsam genutzten Mitglieder haben, diese möglicherweise in einer Struktur oder Klasse zusammenfassen und diese als übergeben möchten das Argument für den Konstruktor der Basisklasse.

Wenn Sie darauf bestehen, dass die „gemeinsam genutzten“ Mitglieder als statische Mitglieder der abgeleiteten Klasse implementiert werden, können Sie möglicherweise den Code der abgeleiteten Klassen automatisch generieren.XSLT ist ein großartiges Tool zum automatischen Generieren einfacher Klassen.

Im Allgemeinen zeigt das Beispiel keinen Bedarf an „virtuellen statischen“ Mitgliedern, da Sie für Zwecke wie diese eigentlich keine Vererbung benötigen – stattdessen sollten Sie die Basisklasse verwenden und dafür sorgen, dass sie die entsprechenden Werte im Konstruktor akzeptiert – vielleicht Erstellen einer einzelnen Instanz der Argumente für jeden „Untertyp“ und Übergeben eines Zeigers darauf, um eine Duplizierung der gemeinsam genutzten Daten zu vermeiden.Ein anderer ähnlicher Ansatz besteht darin, Vorlagen zu verwenden und als Vorlagenargument eine Klasse zu übergeben, die alle relevanten Werte bereitstellt (dies wird allgemein als „Richtlinien“-Muster bezeichnet).

Zusammenfassend lässt sich sagen, dass für die Zwecke des ursprünglichen Beispiels solche „virtuellen statischen“ Mitglieder nicht erforderlich sind.Wenn Sie immer noch der Meinung sind, dass sie für den von Ihnen geschriebenen Code benötigt werden, versuchen Sie bitte, dies näher zu erläutern und mehr Kontext hinzuzufügen.

Beispiel für das, was ich oben beschrieben habe:

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

Ich möchte diese Lösung näher erläutern und vielleicht eine Lösung für das Deinitialisierungsproblem geben:

Mit einer kleinen Änderung können Sie das oben beschriebene Design implementieren, ohne unbedingt für jede Instanz einer abgeleiteten Klasse eine neue Instanz des „Deskriptors“ zu erstellen.

Sie können ein Singleton-Objekt, DescriptorMap, erstellen, das die einzelne Instanz jedes Deskriptors enthält, und es beim Erstellen der abgeleiteten Objekte wie folgt verwenden:

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

Jetzt können wir Folgendes tun:

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

Am Ende der Ausführung, wenn die C-Laufzeit Uninitialisierungen durchführt, ruft sie auch den Destruktor statischer Objekte auf, einschließlich unseres Autoptr, der unsere Instanz der DescriptorsMap löscht.

Jetzt haben wir also eine einzelne Instanz jedes Deskriptors, die am Ende der Ausführung ebenfalls gelöscht wird.

Beachten Sie, dass der einzige Zweck der abgeleiteten Klasse darin besteht, die relevanten „Deskriptor“-Daten bereitzustellen (d. h.(im Gegensatz zur Implementierung virtueller Funktionen), dann sollten Sie sich damit begnügen, die Basisklasse nicht abstrakt zu machen und jedes Mal einfach eine Instanz mit dem entsprechenden Deskriptor zu erstellen.

Ich stimme Hershis Vorschlag zu, eine Vorlage als „Basisklasse“ zu verwenden.Nach dem, was Sie beschreiben, klingt es eher nach einer Verwendung für Vorlagen als nach einer Unterklasse.

Sie könnten eine Vorlage wie folgt erstellen (habe noch nicht versucht, diese zu kompilieren):


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

Auf diese Weise können Sie die Methoden der Vorlagenklasse verwenden, um die Daten nach Bedarf innerhalb Ihrer benutzerdefinierten Klassen zu ändern, die die Daten verwenden und die verschiedenen Aspekte der Vorlagenklasse gemeinsam nutzen.

Wenn Sie Vererbung verwenden möchten, müssen Sie möglicherweise auf die „Freuden“ zurückgreifen, die mit der Verwendung eines void*-Zeigers in Ihrer BaseClass und der Umwandlung usw. einhergehen.

Aufgrund Ihrer Erklärung scheinen Sie jedoch Vorlagen und keine Vererbung zu benötigen.

@Hershi:Das Problem bei diesem Ansatz besteht darin, dass jede Instanz jeder abgeleiteten Klasse eine Kopie der Daten hat, was in gewisser Weise teuer sein kann.

Vielleicht könnten Sie so etwas versuchen (ich bin ohne ein Kompilierungsbeispiel verrückt, aber die Idee sollte klar sein).


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

Bezüglich der Folgefrage zum Löschen des statischen Objekts:Die einzige Lösung, die mir in den Sinn kommt, ist die Verwendung eines intelligenten Zeigers, so etwas wie der Erhöhen Sie den gemeinsamen Zeiger.

Es hört sich so an, als würden Sie vermeiden, den Code in den Blattklassen duplizieren zu müssen. Warum also nicht einfach eine Zwischenbasisklasse von der Basisklasse ableiten?Diese Zwischenklasse kann die statischen Daten enthalten und alle Ihre Blattklassen von der Zwischenbasisklasse ableiten.Dies setzt voraus, dass ein statisches Datenelement über alle abgeleiteten Klassen hinweg gespeichert werden soll, was in Ihrem Beispiel so erscheint.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top