2017-08-02 2 views
0

J'ai ci-dessous comme «simple» d'un exemple de ce que j'essaie de faire que je pouvais imaginer. J'ai une classe abstraite A qui expose une interface publique au monde avec deux méthodes: operator== et performTasksSpecificToA. Vous pouvez voir que je suis en utilisant la « méthode Modèle modèle », ainsi que le « modèle de modèle curieusement récurrent » afin de garantir que les utilisateurs de A ne ont pas besoin de vous soucier de la mise en œuvre de A, autrement dit AImpl, alors que toujours être en mesure de vérifier l'égalité contre deux instances de AImpl. Voir this answer on SO pour un peu plus d'informations et de contexte sur cette approche.Comment puis-je définir une classe parent pour réduire la duplication de code lors de l'utilisation d'une classe abstraite avec une définition d'opérateur d'égalité?

Supposons maintenant que je souhaite définir une classe B comme suit:

class B 
{ 
    public: 
     virtual ~B() = 0; 

     bool operator(const B& b) const; 
     void performTasksSpecificToB(); 
}; 

Comme vous pouvez le voir, les actions de classe B le même problème que A en termes de définition d'une operator== publique pour comparer les sous-classes. Comment puis-je définir une classe parent, appelons-Letter, afin d'éviter la duplication de code entre A et B?


Voici mon 'exemple simple' qui compile et s'exécute.

#include <iostream> 

class A 
{ 
    public: 
     virtual ~A() = 0; 

     bool operator==(const A& a) const; 
     void performTasksSpecificToA(); 
    private: 
     virtual bool checkEquality_(const A& a) const = 0; 
}; 

template <class T> 
class A_ : public A 
{ 
    protected: 
     bool checkEquality_(const A& a) const override; 
    private: 
     virtual bool checkEquality(const T& t) const = 0; 
}; 

class AImpl : public A_<AImpl> 
{ 
    public: 
     AImpl(int val) : val(val){}; 
     bool checkEquality(const AImpl& anAImpl) const override; 
    private: 
     int val; 
}; 

A::~A(){} 

bool A::operator==(const A& a) const{ 
    return checkEquality_(a); 
} 

template <class T> 
bool A_<T>::checkEquality_(const A& a) const{ 
    const T* other = dynamic_cast<const T*>(&a); 
    if (other != nullptr){ 
     const T& me = static_cast<const T&>(*this); 
     return other->checkEquality(me); 
    } 
    return false; 
} 

bool AImpl::checkEquality(const AImpl& anAImpl) const{ 
    return val == anAImpl.val; 
} 

int main(){ 
    // factory: 
    AImpl* aImpl1 = new AImpl(1); 
    AImpl* aImpl2 = new AImpl(2); 
    AImpl* aImpl3 = new AImpl(1); 

    // client: 
    A& A1 = *aImpl1; 
    A& A2 = *aImpl2; 
    A& A3 = *aImpl3; 


    std::cout << "A1 == A2 -> "; 
    std::cout << (A1 == A2 ? "true" : "false"); 
    std::cout << std::endl; 


    std::cout << "A1 == A3 -> "; 
    std::cout << (A1 == A3 ? "true" : "false"); 
    std::cout << std::endl; 

    delete aImpl1; 
    delete aImpl2; 
    delete aImpl3; 
    return 0; 
} 
+0

Pourquoi avez-vous ce nombre de niveaux indirection par rapport à l'égalité? –

+0

Le premier niveau est pour éviter de faire de 'A' une classe template: j'ai donc déplacé la logique actuelle vers' A_'. Le deuxième niveau, c'est-à-dire l'utilisation de 'A :: checkEquality_', est destiné à faire la distinction entre les méthodes publiquement exposées de A et les parties qui doivent être chevauchées par des sous-classes (selon le modèle de modèle). – wesanyer

Répondre

0

Si vous pouvez autoriser Letter être un modèle, vous pouvez simplement avoir A Hériter d'une base de modèle:

template<class T> 
class Letter 
{ 
public: 
    bool operator==(const Letter<T>& t) const { 
     const T& t1 = static_cast<const T&>(*this); 
     const T& t2 = static_cast<const T&>(t); 
     return t1.checkEquality_(t2); 
    } 
private: 
    virtual bool checkEquality_(const T& a) const = 0; 
}; 

class A : public Letter<A> 
{ 
    public: 
     virtual ~A() = 0; 
     void performTasksSpecificToA(); 
}; 
... 

Si vous avez absolument besoin d'une Letter commune, vous avez probablement ajouter une autre couche de CRTP comme vous l'avez fait avec A_ et A.