1
votes

Remplacement des valeurs d'énumération dans la classe dérivée

J'avais une classe A , qui utilisait des énumérations quelque chose comme ceci:

enum DataType
{
    First = 18,
    Second = 19 ...
}

Et consécutivement, certaines valeurs leur étaient assignées qui étaient utilisées dans le code.

Maintenant, je dois écrire une autre classe B qui est très similaire à la classe A , pour laquelle je prévois de faire un class Parent et en dériver à la fois la classe A et la classe B . Mais, je veux réutiliser le enum DataType dans les deux classes.

Le problème étant que ces valeurs enum doivent être différentes pour les deux classe A et classe B .

Pour classe A

enum DataType
{
    First = 1,
    Second = 2 ...
}

Pour classe B

enum DataType
{
    First,
    Second,
    Third
}

Une approche naïve qui me vient à l'esprit est de définir une fonction virtuelle dans les deux classes, avec un cas de commutation code >, donc au lieu d'utiliser l'énumération, je vais devoir appeler la fonction virtuelle et utiliser ce qu'elle renvoie.

Mais, y a-t-il une meilleure façon de le faire? En utilisant une propriété d'héritage dont je ne suis pas au courant?


2 commentaires

@GuillaumeRacicot Pas dans le même sens que remplacer une fonction virtuelle. D'autres noms, vous pouvez simplement "shadow", ce qui affecte les utilisations où le compilateur regarde la classe dérivée, mais pas les utilisations via la classe de base.


"Mais, je veux réutiliser l'énumération DataType dans les deux classes. Le problème étant que ces valeurs d'énumération devraient être différentes" Alors ce n'est pas le même type. Et ne peut pas l'être. Et ça ne devrait pas l'être.


4 Réponses :


1
votes

Vos DataTypes pour A et B sont des types distincts, donc la classe Parent doit être un modèle:

template<typename DataType>
class Parent
{
public:
    // example method
    virtual bool isFirst(DataType value) const
    {
        // do something with value, e. g. 
        return (value == DataType::First);
    }
};

enum class DataTypeA
{
    First = 1,
    Second = 2
};
class A : public Parent<DataTypeA>
{
};

enum class DataTypeB
{
    First = 18,
    Second = 19
};
class B : public Parent<DataTypeB>
{
};

int main()
{
    A a;
    B b;

    std::cout << "a.isFirst(DataTypeA::First): " << (a.isFirst(DataTypeA::First) ? "true" : "false") << std::endl;
    std::cout << "a.isFirst(DataTypeA::Second): " << (a.isFirst(DataTypeA::Second) ? "true" : "false") << std::endl;
    std::cout << "b.isFirst(DataTypeB::First): " << (b.isFirst(DataTypeB::First) ? "true" : "false") << std::endl;
    std::cout << "b.isFirst(DataTypeB::Second): " << (b.isFirst(DataTypeB::Second) ? "true" : "false") << std::endl;

    return 0;
}

Notez que j'ai utilisé la classe enum au lieu de (ancien style) enum pour empêcher se heurter à l'ambiguïté des valeurs d'énumération.


0 commentaires

1
votes

Une manière:

template<int EnumBegin>
struct Parent {
    enum DataType {
        First = EnumBegin,
        Second, // == First + 1
        Third   // == Second + 1
    };
};

struct A : Parent<0> {};
struct B : Parent<10> {};

int main() {
    std::cout << A::Second << '\n'; // Outputs 1.
    std::cout << B::Second << '\n'; // Outputs 11.
}


0 commentaires

2
votes

Une solution simple consiste à définir un enum DataType dans chacun des membres. Cela n'introduit aucune surcharge d'exécution ou de stockage. Mais dans ce cas, le comportement est statique; L'utilisateur a accès à l'énumération uniquement en fonction du type statique. Si les valeurs d'énumération obéissent à un modèle similaire à celui de votre exemple, vous pourrez peut-être générer l'énumération, ou même la classe entière avec un modèle.

Une approche dynamique est ce que vous avez suggéré: utilisez une fonction virtuelle. Cela a un peu de surcharge, mais fournit un polymorphisme d'exécution. Dans ce cas, l'utilisateur peut accéder à l'énumération spécifique au type dynamique sans savoir quel est ce type.

Ces deux approches peuvent même être combinées pour que vous ayez les deux.


0 commentaires

1
votes

J'enverrais simplement le type de la classe dérivée à la fonction de classe parente:

struct A : Parent {
    enum struct DataType {
        First = 1, Second
    };

    //               v---- if virtual, add override
    void stuff1() /* override */ {
        stuff1_impl(*this);
    }
};

struct B : Parent {
    enum struct DataType {
        First = 10, Second
    };

    //               v---- if virtual, add override
    void stuff1() /* override */ {
        stuff1_impl(*this);
    }
};

Puis dans vos classes dérivées:

// parent not templated!
struct Parent {

    // with this pattern, stuff1 could be virtual:
    // virtual void stuff1() = 0;

protected:
    // static because we can access private
    // member through the self object
    template<typename T>
    static void stuff1_impl(T const& self) {
        auto value = /* get the switched on value */;

        switch(value) {
        case T::DataType::First:
            // things for the case1
            break;
        case T::DataType::Second:
            // things for the case1
            break;
        }
    }
};

Ce modèle évite de créer un modèle pour toute la classe de base et vous pouvez toujours utiliser le polymorphisme virtuel. Vous ne modélisez que les parties nécessitant l'énumération dans une section protégée.

Exemple en direct a >


0 commentaires