2 votes

Comment distinguer les sous-classes

J'ai une classe de jetons qui ressemble à ceci :

class Token
{
 public:
   typedef enum { STRTOK, INTTOK } Type;
   virtual bool IsA(Type) = 0;
}

class IntTok : public Token
{
   int data;
 public:
   bool IsA(Type t) { return (t == INTTOK); }
   int GetData() { return data; }
}

IntTok newToken;
if ( newToken.IsA(Token::INTTOK )
{
  //blah blah
}

Je dois donc définir chaque sous-classe dans la classe Token, ce qui n'est pas si mal car il y a très peu de sous-classes et je ne peux pas imaginer qu'elles changent. Mais c'est quand même moche, maladroit et moins "correct" que d'identifier les sous-classes à l'aide d'un cast dynamique. Cependant :

IntTok newToken;
IntTok* tmpTokenTest = dynamic_cast<IntTok*>(&newToken);
if ( tmpTokenTest != NULL )
{
  //blah blah
}

Est également assez peu pratique. En particulier lorsque je dois les enchaîner dans un grand "if" imbriqué.

Alors, lequel choisiriez-vous ? Existe-t-il une autre solution à ce problème ?

Remarque : Je sais que je devrai de toute façon les lancer pour obtenir leurs données respectives, mais

  1. Je ne les lancerai que juste avant d'utiliser leur fonction, ce qui me semble plus propre et plus efficace.
  2. Je teste leur type bien plus souvent que je n'utilise leurs données.

Note2 : Le code ci-dessus n'indique pas que ces jetons sont également une liste chaînée. Cela rend le templating difficile (un Token<int> peut pointer vers un Token<string> etc). C'est pourquoi j'ai besoin d'une classe Token comme parent pour commencer.

3voto

Adam Rosenfield Points 176408

Il suffit d'utiliser des fonctions virtuelles pour faire ce que vous voulez. Au lieu de cela :

if(newToken.IsA(Token::INTTOK))
{
    // do stuff with ((IntTok*)&newToken)->GetData()
}

Faites ceci :

class Token
{
public:
    ...
    virtual void doTypeDependentStuff() {}  // empty default implementation
}

class IntTok : public Token
{
public:
    ...
    void doTypeDependent()
    {
        // do stuff with data
    }
}

3voto

Modèle de visiteur , en effet.

class TokenVisitor {
public:
    virtual ~TokenVisitor() { }
    virtual void visit(IntTok&) = 0;
    virtual void visit(StrTok&) = 0;
};

class Token {
 public:
   virtual void accept(TokenVisitor &v) = 0;
};

class IntTok : public Token {
   int data;
 public:
   virtual void accept(TokenVisitor &v) {
       v.visit(*this);
   }
   int GetData() { return data; }
};

Il suffit alors de mettre en œuvre l'interface visiteur et d'appeler

token->accept(myVisitor);

Le contrôle sera confié au visiteur, qui pourra alors prendre les mesures appropriées. Si vous avez besoin d'avoir la variable localement et du bon type, alors vous aurez du mal à la down-caster. Mais je pense que conduire le contrôle vers des implémentations spécifiques en utilisant des fonctions virtuelles est souvent une bonne façon de résoudre le problème.

2voto

Benoît Points 10901

Je vous suggère d'utiliser Boost::Variant, qui est en fait l'union de plusieurs types (un objet de type variant peut contenir n'importe quel objet de type Ti ( 1 <= i <= n ) )).

Grâce à cela, vous n'aurez pas besoin d'utiliser l'héritage.

Voir pour plus d'informations.

0voto

dirkgently Points 56879

Il faut donc que chaque sous-classe soit définie dans la classe Token.

Pouvez-vous expliquer pourquoi ?

Est-il vraiment nécessaire de faire un casting ? Les fonctions polymorphes peuvent être utilisées.

Ou alors, on peut avoir une classe de tokens modélisée (avec un comportement par défaut pour certains) et une spécialisation pour les autres.

0voto

Uri Points 50687

C'est une question délicate, mais je serais plus enclin à opter pour la version qui consiste à utiliser RTTI.

Les nouveaux compilateurs C++ (j'ai essayé pour la dernière fois dans VC 6.0 alors qu'il n'était pas vraiment supporté) n'ont-ils pas supposé l'opérateur typeid afin que vous n'ayez pas besoin d'un cast dynamique complet ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X