#c
#c
Вопрос:
Допустим, у меня есть класс, который может использовать различные типы функций расстояния (евклидово расстояние и т.д.) Для выполнения некоторых сравнений. Я реализовал эти функции как подклассы класса Distance:
class Distance;
class EuclideanDistance : public Distance;
class OtherDistance : public Distance;
Кажется, что для выбора типа расстояния для использования я мог бы сделать:
template <typename TDistance>
class MyClass;
и создайте его с помощью:
MyClass<EuclideanDistance> instance;
или выполните то же самое с:
class MyClass
{
public:
Distance* myDistanceFunction;
}
и созданием его экземпляра с помощью
MyClass instance;
instance.myDistanceFunction = new EuclideanDistance;
есть ли какие-либо преимущества одного метода перед другим?
Спасибо,
Дэвид
Ответ №1:
Объединение (т. Е. решение без шаблонов) предпочтительнее, поскольку оно обеспечивает большую гибкость, позволяет изменять реализацию distance во время выполнения, генерирует более чистые сообщения об ошибках и более чистые объектные файлы (меньше символов).
Кроме того, классы, созданные на основе шаблона, параметризованного разными типами (реализации distance), будут считаться разными типами и не будут взаимозаменяемыми: MyClass<EuclideanDistance>
это другой тип, чем MyClass<MinkowskiDistance>
. Это заставит вас создавать все функции, которые также работают с MyClass
шаблонами, и в конечном итоге приведет к большей сложности без каких-либо дополнительных преимуществ.
Шаблоны следует использовать, когда вам нужно ослабить типобезопасность языка, например, когда вы пишете класс, который должен работать с несколькими несвязанными типами (не производными от общего базового класса / интерфейса), которые, тем не менее, ведут себя аналогичным образом (например, у всех есть kwak()
функция-член). Это называется утиным вводом: типы формально не связаны, но все обладают схожими свойствами.
В случае, если вы не можете гарантировать, что все реализации distance являются производными от общего базового класса / интерфейса, вам может потребоваться использовать шаблоны. В противном случае предпочитайте простое и гибкое объединение.
Ответ №2:
Ну, помимо проблемы времени компиляции и времени выполнения, самая большая разница была отчасти запрещена вашим ранее существующим кодом. Использование шаблона позволило бы вам использовать любые типы, которые поддерживают общую операцию (например, getDistance), без необходимости их использования в одной иерархии. Это означало бы, что Euclidean distance и ваш другой класс могли бы быть совершенно другими, но все еще пригодными для использования в шаблоне, если они поддерживают подмножество элементов, используемых шаблоном.
Комментарии:
1. Я вижу — так что я бы просто хотел, чтобы EuclideanDistance и OtherDistance были отдельными классами, не производными от какого-либо родительского класса Distance.
2. Это было бы вполне возможно 🙂 Шаблоны допускают универсальное программирование, рассматривая вещи, которые имеют один и тот же неявный интерфейс (вещи, требуемые шаблоном), как одинаковые. Это та же концепция, что и то, как STL часто позволяет передавать функтор (функциональный объект) или фактическую функцию — шаблону, использующему переданную функцию или функтор, все равно, какой это, ему просто важно, чтобы переданный параметр вызывался с синтаксисом (). Таким образом, шаблоны намного более гибкие, чем иерархия наследования (но у обоих определенно есть плюсы и минусы).
Ответ №3:
Первый фиксирует метрику расстояния для класса во время компиляции, в то время как второй делает это во время выполнения. Первый, вероятно, более эффективен, поскольку позволяет компилятору оптимизировать его части, в то время как второй может быть более гибким.
Комментарии:
1. Во втором методе — есть ли способ создать второй объект того же типа, что и первый?
2. @DavidDoria: какой объект вы имеете в виду? A
MyClass
, aEuclideanDistance
?3. Другой объект того же типа, что и myDistanceFunction внутри MyClass.
4. @DavidDoria: Если вы создадите a
typename TDistance
внутри aMyClass<EuclideanDistance>
, то он станетEuclideanDistance
.5. Конечно, но как насчет другого случая — не шаблонного класса. Не могли бы вы использовать функцию typeof() или что-то подобное, чтобы создать второй экземпляр функции расстояния того же типа, что и «основной» объект функции расстояния в классе?
Ответ №4:
Общедоступное наследование означает -A. Возможно ли взаимодействие EuclideanDisatance с «LorentzDistance» или «QuaternionDistance» (или любым другим) каким-либо значимым образом? Являются ли они обоими «расстояниями» в том смысле, что какой-то код где-то был бы рад использовать любой из них нетривиальным способом (то есть чем-то другим, кроме печати значения и т.д.)? Я предполагаю, что нет, это не так.
Итак, вам нужны обобщения, а не наследование. Шаблоны — это более простое обещание: две вещи синтаксически «похожи» друг на друга.