Как я должен различать подклассы
У меня есть класс токенов, который выглядит примерно так:
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
}
Это также довольно глупо. Особенно, когда мне нужно связать их вместе в большое, вложенное, если.
Так что бы вы использовали? Есть ли другое решение этой проблемы?
Примечание: я знаю, что мне все равно придется разыграть их, чтобы получить их соответствующие данные, но
- Я не буду кастовать их до тех пор, пока я не использую их функцию, так что я чувствую себя чище и
- Я проверяю их тип гораздо чаще, чем использую их данные.
Примечание 2: в приведенном выше коде не указано, что эти токены также являются связанным списком. Это затрудняет создание шаблонов (Token<int>
может указывать на Token<string>
, так далее). Вот почему мне нужен класс Token как родительский для начала.
5 ответов
Просто используйте виртуальные функции вместо того, чтобы делать то, что вы хотите. Вместо этого:
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
}
}
Шаблон посетителя, действительно.
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);
Контроль будет предоставлен Посетителю, который затем сможет выполнить соответствующие действия. Если вам нужна переменная локально и правильного типа - тогда, однако, вы вряд ли обойдете ее. Но я думаю, что передача управления к конкретным реализациям с использованием виртуальных функций часто является хорошим способом ее решения.
Могу ли я предложить использовать Boost::Variant, который представляет собой объединение нескольких типов (объект варианта типа может содержать любой объект типа Ti ( 1 <= i <= n)).
Используя это, вам не придется использовать наследование.
Смотрите там для получения дополнительной информации.
Это неприятно, хотя я бы с большей вероятностью согласился с версией использования RTTI.
Разве не были новые компиляторы C++ (я в последний раз пробовал в VC 6.0, когда он не был реально поддержан), предполагал оператор typeid, чтобы вам не понадобилось полное динамическое приведение?
По сути, я должен иметь каждый подкласс, определенный в классе Token
Вы можете объяснить, почему?
Действительно ли это необходимо, чтобы бросить? Полиморфные функции могут быть использованы.
Или, может быть, вы можете иметь шаблонный класс Token (с поведением по умолчанию для некоторых) и специализироваться на остальных.