Question

Existe-t-il de toute façon une sorte de membre statique virtuel en C++ ?

Par exemple:

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

Je sais que cet exemple est trivial, mais si j'ai un vecteur de données complexes qui sera toujours le même pour toutes les classes dérivées mais qui doit être accessible à partir des méthodes de classe de 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";

Cette solution ne me satisfait pas car j'ai besoin de réimplémenter le membre _name et son accesseur GetName() dans chaque classe.Dans mon cas, j'ai plusieurs membres qui suivent le comportement de _name et les dixièmes de classes dérivées.

Une idée?

Était-ce utile?

La solution

Voici une solution :

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

Autres conseils

Il semble que la réponse soit dans la question - la méthode que vous avez suggérée semble être la bonne direction, sauf que si vous avez un grand nombre de ces membres partagés, vous souhaiterez peut-être les rassembler dans une structure ou une classe et les dépasser comme l'argument du constructeur de la classe de base.

Si vous insistez pour que les membres « partagés » soient implémentés en tant que membres statiques de la classe dérivée, vous pourrez peut-être générer automatiquement le code des classes dérivées.XSLT est un excellent outil pour générer automatiquement des classes simples.

En général, l'exemple ne montre pas la nécessité de membres "statiques virtuels", car à des fins comme celles-ci, vous n'avez pas réellement besoin d'héritage - vous devez plutôt utiliser la classe de base et lui faire accepter les valeurs appropriées dans le constructeur - peut-être créer une seule instance des arguments pour chaque "sous-type" et lui passer un pointeur pour éviter la duplication des données partagées.Une autre approche similaire consiste à utiliser des modèles et à transmettre comme argument de modèle une classe qui fournit toutes les valeurs pertinentes (c'est ce que l'on appelle communément le modèle « Politique »).

Pour conclure - pour les besoins de l'exemple original, de tels membres "statiques virtuels" ne sont pas nécessaires.Si vous pensez toujours qu'ils sont nécessaires pour le code que vous écrivez, essayez d'élaborer et d'ajouter plus de contexte.

Exemple de ce que j'ai décrit ci-dessus :

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

J'aimerais développer cette solution, et peut-être donner une solution au problème de désinitialisation :

Avec un petit changement, vous pouvez implémenter la conception décrite ci-dessus sans nécessairement créer une nouvelle instance du « descripteur » pour chaque instance d'une classe dérivée.

Vous pouvez créer un objet singleton, DescriptorMap, qui contiendra l'instance unique de chaque descripteur et l'utiliser lors de la construction des objets dérivés comme ceci :

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

Maintenant, nous pouvons faire ceci :

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

En fin d'exécution, lorsque le runtime C effectue des désinitialisations, il appelle également le destructeur d'objets statiques, dont notre autoptr, qui supprime notre instance du DescriptorsMap.

Nous avons donc maintenant une seule instance de chaque descripteur qui est également supprimée à la fin de l'exécution.

Notez que si le seul objectif de la classe dérivée est de fournir les données de « descripteur » pertinentes (c'est-à-direau lieu d'implémenter des fonctions virtuelles), vous devez alors vous contenter de rendre la classe de base non abstraite et de simplement créer une instance avec le descripteur approprié à chaque fois.

Je suis d'accord avec la suggestion de Hershi d'utiliser un modèle comme "classe de base".D'après ce que vous décrivez, cela ressemble plus à une utilisation de modèles qu'à un sous-classement.

Vous pouvez créer un modèle comme suit (je n'ai pas essayé de le compiler) :


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

Cela vous permettra d'utiliser les méthodes de classe modèle pour modifier les données selon vos besoins à partir de vos classes personnalisées qui utilisent les données et partagent les différents aspects de la classe modèle.

Si vous avez l'intention d'utiliser l'héritage, vous devrez peut-être recourir aux "joies" de l'utilisation d'un pointeur void* dans votre BaseClass et de la gestion du casting, etc.

Cependant, d'après votre explication, il semble que vous ayez besoin de modèles et non d'héritage.

@Hershi :le problème avec cette approche est que chaque instance de chaque classe dérivée possède une copie des données, ce qui peut être coûteux d'une manière ou d'une autre.

Peut-être pourriez-vous essayer quelque chose comme ça (je crache sans exemple de compilation, mais l'idée devrait être claire).


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

Concernant la question complémentaire sur la suppression de l'objet statique :la seule solution qui me vient à l'esprit est d'utiliser un pointeur intelligent, quelque chose comme le Boostez le pointeur partagé.

Il semble que vous essayiez d'éviter d'avoir à dupliquer le code au niveau des classes feuilles, alors pourquoi ne pas simplement dériver une classe de base intermédiaire à partir de la classe de base.cette classe intermédiaire peut contenir les données statiques et faire en sorte que toutes vos classes feuilles dérivent de la classe de base intermédiaire.Cela présuppose qu'une donnée statique conservée sur toutes les classes dérivées soit souhaitée, ce qui semble être le cas d'après votre exemple.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top