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;
}
Pourquoi avez-vous ce nombre de niveaux indirection par rapport à l'égalité? –
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