Как я должен различать подклассы - PullRequest
2 голосов
/ 22 февраля 2009

У меня есть класс токенов, который выглядит примерно так:

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
}

Так что, по сути, я должен иметь каждый подкласс, определенный в классе Token; что не так уж плохо, потому что подклассов очень мало, и я не могу представить, как они меняются. Но, тем не менее, это уродливо, глупо и менее "правильно", чем идентификация подклассов с использованием динамического приведения. Тем не менее:

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

Это тоже довольно глупо. Особенно, когда мне нужно связать их вместе в большое, вложенное, если.

Так что бы вы использовали? Есть ли другое решение этой проблемы?

Примечание: Я знаю, что мне все равно придется разыграть их, чтобы получить их соответствующие данные, но

  1. Я не буду кастовать их до тех пор, пока я не использую их функцию, так что она выглядит чище и
  2. Я проверяю их тип гораздо чаще, чем использую их данные.

Примечание 2: В приведенном выше коде не указано, что эти токены также являются связанным списком. Это затрудняет создание шаблонов (Token<int> может указывать на Token<string> и т. Д.). Вот почему для начала мне нужен родительский класс Token.

Ответы [ 5 ]

3 голосов
/ 22 февраля 2009

Шаблон посетителя , действительно.

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; }
};

Тогда просто реализуйте интерфейс посетителя и позвоните

token->accept(myVisitor);

Контроль будет предоставлен Посетителю, который затем сможет выполнить соответствующие действия. Если вам нужно иметь переменную локально и правильного типа - тогда вы вряд ли сможете обойти ее. Но я думаю, что доведение управления до конкретных реализаций с использованием виртуальных функций часто является хорошим способом решения этой проблемы.

3 голосов
/ 22 февраля 2009

Просто используйте виртуальные функции вместо того, чтобы делать то, что вы хотите. Вместо этого:

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

Сделайте это:

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

class IntTok : public Token
{
public:
    ...
    void doTypeDependent()
    {
        // do stuff with data
    }
}
2 голосов
/ 23 февраля 2009

Могу ли я предложить использовать Boost :: Variant, который представляет собой объединение нескольких типов (объект варианта типа может содержать любой объект типа Ti (1 <= i <= n)). </p>

Используя это, вам не придется использовать наследование.

См. там для получения дополнительной информации.

0 голосов
/ 22 февраля 2009

Это неприятно, хотя я бы с большей вероятностью согласился с версией использования RTTI.

Разве не были новые компиляторы C ++ (в последний раз я пробовал в VC 6.0, когда он не был реально поддержан), предполагал оператор typeid, поэтому вам не понадобится полное динамическое приведение?

0 голосов
/ 22 февраля 2009

Так что, по сути, я должен иметь каждый подкласс, определенный в классе Token

Можете ли вы объяснить, почему?

Неужели это действительно необходимо разыграть? Можно использовать полиморфные функции.

Или, может быть, у вас есть шаблонный класс Token (с поведением по умолчанию для некоторых) и специализированный для остальных.

...