Являются ли поля типов чистым злом? - PullRequest
4 голосов
/ 28 сентября 2010

Как обсуждается в на языке программирования c ++, 3-е издание в разделе 12.2.5, поля типов имеют тенденцию создавать код, который менее универсален, подвержен ошибкам, менее интуитивен и менее удобен, чем эквивалентный код, который использует виртуальные функции и полиморфизм.

В качестве краткого примера, вот как будет использоваться поле типа:

void print(const Shape &s)
{
  switch(s.type)
  {
  case Shape::TRIANGE:
    cout << "Triangle" << endl;
  case Shape::SQUARE:
    cout << "Square" << endl;
  default:
    cout << "None" << endl;
  }
}

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

Несмотря на эти недостатки и описанные в TC ++ PL, есть ли примеры, когда такая реализация (использующая поле типа) является лучшим решением, чем использование языковых возможностей виртуальных функций? Или эта практика должна быть в черном списке как чистое зло?

Реалистичные примеры предпочтительнее надуманных, но мне все равно будут интересны надуманные примеры. Кроме того, вы когда-нибудь видели это в рабочем коде (хотя виртуальные функции были бы проще)?

Ответы [ 9 ]

7 голосов
/ 28 сентября 2010

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

Это, более или менее, метод, используемый для различающихся объединений в нескольких из Статьи Александреску .

Например, если бы я реализовывал библиотеку JSON , я бы знал, что каждое значение может быть только Object, Array, String, Integer, Booleanили Null - спецификация не допускает других.

3 голосов
/ 28 сентября 2010

Тип enum может быть сериализован через memcpy, а v-таблица - нет.Аналогичная особенность заключается в том, что повреждение значения перечисления типа легко обрабатывается, повреждение указателя v-таблицы означает мгновенную ошибку.Нет никакого портативного способа даже проверить указатель v-таблицы на достоверность, вызывая dynamic_cast или typeinfo для выполнения RTTI-проверок на недопустимый объект, это неопределенное поведение.

Например, один экземпляр, где я выбираюиспользуйте иерархию типов со статической диспетчеризацией, управляемой дискриминатором, а не динамической диспетчеризацией при передаче указателя на структуру через очередь сообщений Windows.Это дает мне некоторую защиту от другого программного обеспечения, которое может распределять широковещательные сообщения из диапазона, который я использую (он должен быть зарезервирован для локальных сообщений приложения, не пропускайте GO, если считаете, что правило действительно соблюдается).

2 голосов
/ 28 сентября 2010

Следующее руководство взято из «Чистого кода» Роберта К. Мартина.«Мое общее правило для операторов switch заключается в том, что они могут быть допущены, если они появляются только один раз, используются для создания полиморфных объектов и скрыты за отношением наследования, так что остальная система не может их видеть».

Объяснение таково: если вы выставите поля типа для остальной части своего кода, вы получите несколько экземпляров вышеупомянутого оператора switch.Это явное нарушение СУХОГО.Когда вы добавляете тип, все эти переключатели необходимо изменить (или, что еще хуже, они становятся несовместимыми, не нарушая вашу сборку).

1 голос
/ 28 сентября 2010

Мое мнение: Это зависит.

Параметризованный Шаблон проектирования фабричного метода опирается на эту технику.

class Creator {
    public:
        virtual Product* Create(ProductId);
};

Product* Creator::Create (ProductId id) {
        if (id == MINE)  return new MyProduct;
        if (id == YOURS) return new YourProduct;
        // repeat for remaining products...

        return 0;
}

Итак, это плохо? Я так не думаю, потому что у нас нет другой альтернативы на данном этапе. Это место, где это абсолютно необходимо, так как это связано с созданием объекта. Тип объекта еще не известен.

Однако пример в OP - это пример, который, безусловно, нуждается в рефакторинге. Здесь мы уже имеем дело с существующим объектом / типом (переданным в качестве аргумента функции).

Как упоминает Херб Саттер -

"Выключить: Избегать включения тип объекта для настройки Поведение. Использование шаблонов и виртуальных функции, чтобы позволить типам (не их вызывающий код) решить их поведение. "

0 голосов
/ 28 сентября 2010

Лучший пример, который я могу придумать (и тот, с которым я сталкивался ранее), - это когда ваш набор типов фиксирован, а набор функций, которые вы хотите выполнять (которые зависят от этих типов), является плавным.Таким образом, когда вы добавляете новую функцию, вы изменяете одно место (добавляя один переключатель), а не добавляете новую базовую виртуальную функцию с реальной реализацией, разбросанной по всем классам в вашей иерархии типов.

0 голосов
/ 28 сентября 2010

Я думаю об использовании поля типа для решения проблемы векторной нарезки. То есть я хочу вектор иерархических объектов. Например, я хочу, чтобы мой вектор был вектором фигур, но я хочу хранить круги, прямоугольники, треугольники и т. Д.

Вы не можете сделать это самым очевидным простым способом из-за нарезки. Таким образом, нормальное решение состоит в том, чтобы иметь вместо него вектор указателей или умных указателей. Но я думаю, что есть случаи, когда использование поля типа будет более простым решением (избегая новых / удаления или альтернативных методов жизненного цикла).

0 голосов
/ 28 сентября 2010

Я думаю, что если тип точно соответствует подразумеваемым классам, то тип неправильный.Где это становится сложным, это где тип не совсем совпадает или его не так резать и сушить.

Если взять ваш пример, что, если type был Red, Green, Blue.Это типы фигур.Вы могли бы даже иметь цветовой класс как mixin;но это, вероятно, слишком много.

0 голосов
/ 28 сентября 2010

Разве нет затрат, связанных с виртуальными функциями и полиморфизмом? Как и поддержка таблицы vtable для каждого класса, увеличение размера объекта каждого класса на 4 байта, медленная скорость выполнения (хотя я никогда не измерял это) для надлежащего разрешения виртуальной функции. Таким образом, для простых ситуаций использование поля type представляется приемлемым.

0 голосов
/ 28 сентября 2010

Я не знаю ни одного реалистичного примера. Придуманные будут зависеть от какой-то веской причины, по которой виртуальные методы не могут быть использованы.

...