#c #inheritance #polymorphism #subclass #downcast
#c #наследование #полиморфизм #подкласс #понижение
Вопрос:
Мне нужно проанализировать исходный код. Я определил 3 разных типа токенов: символы (операторы, ключевые слова), символы (целые числа, строки и т. Д.) И идентификаторы.
У меня уже есть следующий дизайн с базовым классом, который отслеживает тип подкласса, чтобы его можно было понизить с помощью указателя базового класса :
class Token
{
type_e type; // E_SYMBOL, E_LITTERAL, E_TOKEN
};
class Symbol : public Token
{
const symbol_e symbol;
};
class Litteral : public Token
{
const Value value;
};
class Identifier : public Token
{
const std::string name;
};
Мне нужно, чтобы эти классы хранились в одном массиве токенов, поэтому мне нужно, чтобы у них был общий базовый класс. Затем я использую их следующим образом :
if( cur->type == E_SYMBOL amp;amp; static_cast< const Symbol * >( cur )->symbol == E_LPARENT )
{
// ...
}
Я мог бы создать виртуальные функции isSymbol, isLitteral, isIdentifer, которые каждый подкласс переопределял бы, но мне все равно пришлось бы понизить указатель базового класса до указателя подкласса, чтобы я мог получить доступ к конкретным данным подкласса.
Люди говорят, что даункастинг означает, что интерфейс, скорее всего, имеет недостатки, и это делает синтаксис очень тяжелым, поэтому я хотел бы найти другой способ, но не могу. Некоторые люди предложили шаблон visitor, но я боюсь, что это бесполезно усложнило бы код, и я даже не понимаю, как я мог бы использовать шаблон visitor для решения этой проблемы.
Кто-нибудь может помочь? Спасибо 🙂
Комментарии:
1.
Token
boost::variant
Может быть, сделать?2. Вам не нужен общий базовый класс для хранения разных типов в одном массиве. Например, тип варианта, упомянутый выше.
3. @Virus721: Boost — чистейший из чистейших C . У вас не может быть более чистого C , чем boost. Ну, серьезно, с одной стороны, boost — это своего рода промежуточная область для стандартной библиотеки (точно так же, как STL был много лет назад). А для другого вы можете легко выбрать определенные биты из boost; они в основном независимы.
4. @Virus721: изобретать дискриминационное объединение было бы такой же пустой тратой времени.
5. @Virus721 Нет, потому что вы не предоставили никакой полезной информации о своей проблеме.
Ответ №1:
У вас есть три варианта. Каждое решение имеет свои преимущества и недостатки.
- Поместите логику в классы токенов, чтобы вызывающему коду не нужно было знать, с каким типом токена он имеет дело.
Это было бы «самым чистым объектно-ориентированным» решением. Недостатком является то, что логика имеет тенденцию распространяться между базовым классом и подклассами, что затрудняет ее понимание. Это также может привести к тому, что классы станут довольно большими. Но у компиляторов / интерпретаторов обычно не так много действий, чтобы это стало проблемой.
- Используйте шаблон посетителя.
То есть имеет интерфейс
TokenVisitor
сvisit
перегруженным методом для подтипов токенов иaccept(TokenVisitoramp;)
метод, дляToken
которого каждый подкласс переопределял бы для вызова соответствующей перегрузкиvisit
.Теперь вам нужно знать полный набор типов токенов в интерфейсе, но это позволяет сохранить классы достаточно маленькими и сгруппированными по действиям, логике которых обычно легче следовать.
- Используйте различимый союз, например Boost.Variant.
Это вообще не объектно-ориентированный. Это приведет к повсеместному переключению типа и, вероятно, будет выглядеть некрасиво. Но поскольку логика состоит из всех элементов, ей часто легче следовать, особенно для тех, кто не понимает идею, лежащую в основе кода.