Шаблонный класс или абстрактный базовый класс?

9 ответов

Это зависит от Ваших целей. Можно использовать версию 1, если Вы

  • Намереваетесь заменить тормоза автомобиля (во времени выполнения)
  • , Намереваются раздать Автомобиль для необрабатывания функций по шаблону

, я обычно предпочитал бы версию 1 с помощью полиморфизма во время выполнения, потому что это все еще гибко и позволяет, у Вас, чтобы иметь Автомобиль все еще есть тот же тип: Car<Opel> другой тип, чем Car<Nissan>. Если Вашими целями является высокая эффективность при использовании тормозов часто, я рекомендую Вам использовать шаблонный подход. Между прочим, это называют основанным на политике дизайном. Вы обеспечиваете политика тормоза . Пример, потому что Вы сказали Вас, запрограммировал в Java, возможно Вы слишком еще не испытаны с C++. Один способ сделать его:

template<typename Accelerator, typename Brakes>
class Car {
    Accelerator accelerator;
    Brakes brakes;

public:
    void brake() {
        brakes.brake();
    }
}

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

class VehicleBase {
protected:
    std::string model;
    std::string manufacturer;
    // ...

public:
    ~VehicleBase() { }
    virtual bool checkHealth() = 0;
};


template<typename Accelerator, typename Breaks>
class Car : public VehicleBase {
    Accelerator accelerator;
    Breaks breaks;
    // ...

    virtual bool checkHealth() { ... }
};

Кстати, который является также подходом, который используют потоки C++: std::ios_base содержит флаги, и наполните, которые не зависят от символьного типа или черт как openmode, флаги формата и материал, в то время как std::basic_ios тогда шаблон класса, который наследовал его. Это также уменьшает чрезмерное увеличение размера кода путем совместного использования кода, который характерен для всех инстанцирований шаблона класса.

Частное Наследование?

Частного наследования нужно избежать в целом. Это только очень редко полезно, и включение является лучшей идеей в большинстве случаев. Общий падеж, где противоположное верно, когда размер действительно крайне важен (основанный на политике строковый класс, например): Пустая Оптимизация Базового класса может применяться при получении из пустого класса политики (просто содержащий функции).

Использование Read и злоупотребления Наследованием Herb Sutter.

33
ответ дан Johannes Schaub - litb 23 September 2019 в 20:29
поделиться

Эмпирическое правило:

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

32
ответ дан Nemanja Trifunovic 23 September 2019 в 20:29
поделиться

Шаблоны являются способом позволить классу использовать переменную, о которой Вы действительно не заботитесь о типе. Наследование является способом определить то, что класс основан на своих атрибутах. Это , "-" по сравнению с, "имеет -" вопрос.

5
ответ дан John Ellinwood 23 September 2019 в 20:29
поделиться

Другие опции:

  1. Использование Шаблон "посетитель" (позволяют внешнему коду работать над Вашим классом).
  2. Воплощают некоторую часть Вашего класса, например, через итераторы, что универсальный основанный на итераторе код может работать над ними. Это работает лучше всего, если Ваш объект является контейнером других объектов.
  3. Видят также Стратегическая модель (существуют примеры C++ внутри)
6
ответ дан Assaf Lavie 23 September 2019 в 20:29
поделиться

Используйте интерфейс, если Вы предполагаете для поддержки различных классов Повреждения и его иерархии сразу.

Car( new Brake() )
Car( new BrakeABC() )
Car( new CoolBrake() )

И Вы не знаете эту информацию во время компиляции.

, Если Вы знаете, какое Повреждение Вы собираетесь использовать 2b, правильный выбор для Вас определить различные Автомобильные классы. Тормоз в этом случае будет Вашим автомобилем "Стратегия", и можно установить по умолчанию.

я не использовал бы 2a. Вместо этого можно добавить статические методы Повредить и назвать их без экземпляра.

0
ответ дан Mykola Golubyev 23 September 2019 в 20:29
поделиться

Абстрактный базовый класс имеет на издержках виртуальных вызовов, но это имеет преимущество, что все производные классы являются действительно базовыми классами. Не, итак, когда Вы используете шаблоны †“Car< Brake> и Car< BrakeWithABS> не связаны друг с другом, и Вы будете иметь или к dynamic_cast и проверке на пустой указатель или иметь шаблоны для всего кода, который имеет дело с Автомобилем.

2
ответ дан sharptooth 23 September 2019 в 20:29
поделиться

На большую часть Вашего вопроса уже ответили, но я хотел уточнить этот бит:

Происходя из Java, я естественно склонен всегда использовать версию 1, но шаблонные версии, кажется, часто предпочитаются, например, в коде STL? Если это правда, это только из-за эффективности памяти и т.д. (никакое наследование, никакие вызовы виртуальной функции)?

Это - часть его. Но другим фактором является добавленная безопасность типов. Когда Вы рассматриваете a BrakeWithABS как a Brake, Вы теряете информацию о типе. Вы больше не знаете, что объект на самом деле a BrakeWithABS. Если это - шаблонный параметр, Вы имеете в наличии точный тип, который в некоторых случаях может позволить компилятору работать лучше typechecking. Или это может быть полезно в обеспечении, что корректную перегрузку функции называют. (если stopCar() передает объект Тормоза второй функции, которая может иметь отдельную перегрузку для BrakeWithABS, это не назовут при использовании наследования и Вашего BrakeWithABS был брошен к a Brake.

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

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

4
ответ дан jalf 27 November 2019 в 04:25
поделиться

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

Однако использование шаблонов не устраняет Вас от выполнения обоих (создание шаблонной более гибкой версии):

struct Brake {
    virtual void stopCar() = 0;
};

struct BrakeChooser {
    BrakeChooser(Brake *brake) : brake(brake) {}
    void stopCar() { brake->stopCar(); }

    Brake *brake;
};

template<class Brake>
struct Car
{
    Car(Brake brake = Brake()) : brake(brake) {}
    void slamTheBrakePedal() { brake.stopCar(); }

    Brake brake;
};


// instantiation
Car<BrakeChooser> car(BrakeChooser(new AntiLockBrakes()));

Однако я, вероятно, НЕ использовал бы шаблоны для этого... Но его действительно просто персональный вкус.

3
ответ дан Community 27 November 2019 в 04:25
поделиться

Лично я всегда предпочел бы использовать интерфейсы поверх шаблонов по нескольким причинам:

  1. Шаблоны Ошибки компиляции и компоновки иногда бывают загадочными
  2. Трудно отладить код, основанный на шаблонах (по крайней мере, в Visual Studio IDE)
  3. Шаблоны могут увеличить размер ваших двоичных файлов.
  4. Шаблоны требуют, чтобы вы поместили весь его код в заголовочный файл, что делает Класс шаблонов немного сложнее для понимания.
  5. Шаблоны трудно поддерживать начинающим программистам.

Я использую шаблоны только тогда, когда виртуальные таблицы создают какие-то издержки.

Конечно, это только мое мнение.

0
ответ дан user88637 27 November 2019 в 04:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: