2010-04-12 8 views
1

tous. Je suis assez nouveau en C++, et j'écris une petite bibliothèque (principalement pour mes propres projets) en C++. Dans le processus de conception d'une hiérarchie de types, j'ai rencontré le problème de la définition de l'opérateur d'affectation.vérification de type C++ au moment de la compilation

J'ai pris l'approche de base qui a finalement été atteint en this article, qui est que pour chaque classe MyClass dans une hiérarchie dérivée d'une classe Base vous définissez deux opérateurs d'affectation comme ceci:

class MyClass: public Base { 
public: 
    MyClass& operator =(MyClass const& rhs); 
    virtual MyClass& operator =(Base const& rhs); 
}; 

// automatically gets defined, so we make it call the virtual function below 
MyClass& MyClass::operator =(MyClass const& rhs); 
{ 
    return (*this = static_cast<Base const&>(rhs)); 
} 

MyClass& MyClass::operator =(Base const& rhs); 
{ 
    assert(typeid(rhs) == typeid(*this)); // assigning to different types is a logical error 
    MyClass const& casted_rhs = dynamic_cast<MyClass const&>(rhs); 
    try { 
     // allocate new variables 
     Base::operator =(rhs); 
    } catch(...) { 
     // delete the allocated variables 
     throw; 
    } 
    // assign to member variables 
} 

La partie Je suis préoccupé par est l'affirmation pour l'égalité de type. Depuis que j'écris une bibliothèque, où les affirmations seront probablement compilées sur le résultat final, cela m'a conduit à aller avec un système qui ressemble plus à ceci:

class MyClass: public Base { 
public: 
    operator =(MyClass const& rhs); // etc 
    virtual inline MyClass& operator =(Base const& rhs) 
    { 
     assert(typeid(rhs) == typeid(*this)); 
     return this->set(static_cast<Base const&>(rhs)); 
    } 
private: 
    MyClass& set(Base const& rhs); // same basic thing 
}; 

Mais je me demandais si je pourrait vérifier les types à la compilation. J'ai regardé dans Boost.TypeTraits, et je me suis rapproché en faisant BOOST_MPL_ASSERT((boost::is_same<BOOST_TYPEOF(*this), BOOST_TYPEOF(rhs)>));, mais puisque rhs est déclarée comme une référence à la classe parente et non à la classe dérivée, elle s'est étouffée. Maintenant que j'y pense, mon raisonnement semble idiot - j'espérais que puisque la fonction était inline, elle serait capable de vérifier les paramètres eux-mêmes, mais bien sûr le préprocesseur est toujours exécuté avant le compilateur. Mais je me demandais si quelqu'un savait d'une autre façon que je pourrais appliquer ce genre de vérification à la compilation.

+1

Il n'y a aucun moyen de vérifier les types au moment de la compilation, car c'est tout le point du polymorphisme: le type de choses n'est déterminé qu'à l'exécution. 'Animal * animal = rand()% 2? new Dog(): new Cat(); ' – UncleBens

+2

Bon article lié en passant, merci pour cela :) –

Répondre

7

Vous ne pouvez pas effectuer cette affirmation au moment de la compilation pour la simple raison que les types d'exécution ne seront pas connus avant, bien, l'exécution.

assert(typeid(rhs) == typeid(*this)); 
return this->set(static_cast<Base const&>(rhs)); 

Dans la version non-inline vous aviez dynamic_cast. Je conserverais ceci pour que vous obteniez une erreur bien définie et non un comportement indéfini si votre assertion est violée.

Si vous faites cela, l'assertion est trop restrictive ou inutile. Le dynamic_cast affichera une exception bad_cast dans les versions de débogage et de publication. Voici ce que tu veux. Personnellement, je remettrais en question tout le problème de l'assignation polymorphe. Je voudrais suivre les conseils efficaces de CSE de Scott Meyers et rendre tous vos nœuds non-feuille dans le résumé de la hiérarchie d'héritage. Vous pouvez ensuite protéger les opérateurs d'affectation de classe de base et les rendre non-virtuels. Cela vous permet d'utiliser leur implémentation dans l'opérateur d'affectation de classes dérivées mais empêche les clients de découper des objets. Si une classe de client a seulement une référence de classe de base ou un pointeur, il est douteux que l'on doive essayer de l'assigner à la classe de toute façon. Si le faire, ils devraient être responsables de la coulée et le type de garanties de sécurité.

+3

+1 pour l'opérateur d'assignation' protected' et le constructeur de copie pour les classes non-leaf.Il y a toujours la méthode virtuelle 'clone' pour copier quand même. –

0

Si vous voulez que les choses soient déterminées au moment de la compilation, alors le polymorphisme dynamique (fonctions virtuelles, etc.) n'est pas le bon outil pour vous. En général, je ne vois pas la nécessité de mélanger le polymorphisme dynamique avec des choses comme les «types de valeurs normales» - comme les opérateurs d'affectation. Peut-être que les classes concrètes et non-polymorphes sont ce qui fonctionne le mieux dans votre cas. Mais c'est difficile à dire parce que vous n'avez rien dit de ce que vous essayez de faire avec les classes.

Questions connexes