Полиморфизм или шаблоны?
- Войдите на сайт для отправки комментариев
Вопрос из области интересов qwone, но может и кто другой подскажет?
Есть коллекция похожих обьектов , организованная как массив для циклической обработки. Сложность заключается в том, что один из параметров обьекта может иметь разный тип от обьекта к обьекту - для простоты скажем иногда int, иногда float.
Соответсвенно, все это может быть оформлено через полиморфизм или через шаблоны. Для примера два коротких кода:
Полиморфизм:
#include <stdio.h> class MyPar { public: int x; virtual void set_par() {}; }; class MyParInt: public MyPar { public: int pp1; void set_par(int a, int c) { x=a; pp1 =c;} }; class MyParFloat: public MyPar { public: float pp1; void set_par(int a, float c) { x=a; pp1 =c;} }; int main() { MyPar* aa[2]; MyParInt pi; MyParFloat pf; int i,y,z,g; y =3; z =5; g =55; float f = 333.44; pi.set_par(y,z); pf.set_par(g,f); aa[0] = π aa[1] = &pf; for (i=0; i<2; i++) { MyParInt* my_int = dynamic_cast<MyParInt*> (aa[i]); if (my_int) printf("class.x class.pp1 %d %d \n", my_int->x, my_int->pp1); else { MyParFloat* my_int = dynamic_cast<MyParFloat*> (aa[i]); if (my_int) printf("class.x class.pp1 %d %.2f \n", my_int->x, my_int->pp1); } } }
Шаблоны
#include <stdio.h> struct AbstractPar{ virtual void set_par() {}; }; template <class T> class MyPar:public AbstractPar { public: T pp1; int x; void setpar(int a, T b) { x=a; pp1=b;}; }; int main() { AbstractPar* aa[2]; MyPar<int> pi; MyPar<float> pf; int i,y,z,g; y =3; z =5; g =55; float f = 333.44; pi.setpar(y,z); pf.setpar(g,f); aa[0] = π aa[1] = &pf; for (i=0; i<2; i++) { MyPar<int>* my_int = dynamic_cast<MyPar<int>*> (aa[i]); if (my_int) printf("class.x class.pp1 %d %d \n", my_int->x, my_int->pp1); else { MyPar<float>* my_int = dynamic_cast<MyPar<float>*> (aa[i]); if (my_int) printf("class.x class.pp1 %d %.2f \n", my_int->x, my_int->pp1); } } }
Как видно, функция main(). практически одинакова, а вот описание классов в шаблонной версии чуть короче.
Вопрос - какой вариант более правильный. И стоит ли вообще этим пользоваться. (где-то попалось на глаза - "если в программе используется динамическое приведение - значит она спроектирована неправильно).
И второй вопрос - приведенный обьект my_int (напр строка 42 первого кода) - образуется копированием исходного обьекта? Нужно ли обязательно определять в исходных классах деструкторы, чтобы правильно подчищать my_int после динамического приведения?
Вопрос немножко похож на коня в вакууме. Без понимания как этот класс будет использоваться трудно выбирать реализацию. В большинстве случаев хватает простой перегрузки методов.
ЕвгенийП,
Пишу простенький командный интерфейс для настройки устройства через Serial. Есть набор команд, порядка десятка-полутора - каждая из которых либо устанавливает значение параметра, либо запускает процедуры иннициализации. диагностики, загрузки или сохранения конфига и тд
Для удобства (и из программисткой лени :) решил описать команды как обьекты. Каждый обьект содержит имя команды. краткое описание (3-5 слов), ссылку на изменяемый параметр или колбек исполняемой функции.
При получении управляющей команды она сравнивается в цикле с командами во всех указанных обьектах - после чего автоматически имеем методы работы с параметрами. пояняющие строки для меню и тд
Ну, я бы просто перегрузил бы конструктор и методы.
ЕвгенийП,
простите за тупой вопрос, я серьезно классы никогда не изучал, плаваю в терминологии. Разве перезагрузка метода - это не то же самое, что я делаю в варианте, который обозвал "полиморфизм"?
Нет.
Полиморфизм, это возможость обрабатывать объекты производных классов, в качестве объетков базовых классов.
Например, если мы определили базовый класс Человек (с датой рождения, именем и т.п.) и производный от него класс "Военнослужащий" с воинским званием, должностью и номером части и т.п., то, если классы в нашем языке полиморфны, то мы можем объект типа Военнослужащий передавать любой функции, ожидающей объект типа Человек и всё будет нормально работать.
А перегрузка операций - гораздо более простая вещь. Она и с наследованием-то никак не связана и может применяться не только в классах, а в любом другом пространстве имён (хоть на глобальном уровне). Это означает лишь то, что мы можем в одном и том же пространстве имён задать несколько функций и одинаковым именем, но различающихся по кколичеству и/или типам параметров. Компилятор сам разберётся какую из них вызывать, в зависимости от того, какие параметры передаются.
Вопрос из области интересов qwone, но может и кто другой подскажет?
Есть коллекция похожих обьектов , организованная как массив для циклической обработки. Сложность заключается в том, что один из параметров обьекта может иметь разный тип от обьекта к обьекту - для простоты скажем иногда int, иногда float.
Соответсвенно, все это может быть оформлено через полиморфизм или через шаблоны. Для примера два коротких кода
А Вы уверены что это два разных примера? ;)
Дело в том, что шаблоны - сущность этапа компиляции. Препроцессор развернет их, и далее они просто не существуют. А в вашем случае он развернет их именно в то, что у Вас названо пример с полиморфизмом ))) Потому в данном случае шаблоны просто короткая запись кода для второго примера и сравнивать их безсмыслено. Но в коде с шаблонами меньше букв, а без шаблона проще отлаживать.
Писать по какому варианту лучше - вопрос веры и интуиции. И сильно от задачи зависит. Если методы обработки данных мало зависят от типа данных - шаблоны предпочтительней, если сильно разные (например у визуальных обектов - может чекбокс быть, а может и контекстное меню) то шаблоны будут скорей мешать.
Полиморфизм, это возможость обрабатывать объекты производных классов, в качестве объетков базовых классов.
Офигеть как удивлен! Вы ж вроде препод -"язычник", а такую хрень пишите. Полиморфизм никак не привязан к классам и обектам. Вики в студию ! https://ru.wikipedia.org/wiki/Полиморфизм_(информатика)
В языках программирования и теории типовполиморфизмом называется способность функцииобрабатывать данные разных типов[1][2][3].
Существует несколько разновидностей полиморфизма. Две наиболее различных из них были описаны Кристофером Стрэчи[en] в 1967 году:
Тогда вобще еще до классов еще было жить и жить.
А Вы уверены что это два разных примера? ;)
Логик, спасибо, я и сам об этом подумал, когда у меня функция main() в обоих вариантах получилась практически одинаковая :)
перегрузка операций - гораздо более простая вещь. Она и с наследованием-то никак не связана и может применяться не только в классах, а в любом другом пространстве имён (хоть на глобальном уровне). Это означает лишь то, что мы можем в одном и том же пространстве имён задать несколько функций и одинаковым именем, но различающихся по кколичеству и/или типам параметров. Компилятор сам разберётся какую из них вызывать, в зависимости от того, какие параметры передаются.
Евгений, спасибо. Это я знаю и пользуюсь. Пока еще не соображу, как переписать мой код в этом ключе, но буду думать.
Википедик? Ну-ну.
Кстати, кто Вам сказал, что я предод? Последний раз я преподавал более 10 лет назад.
Не предод всеже )))
Преподавание калечит навсегда, и видно издалека:(
А пример полиморфизма без классов и прочего новодела -пожалуста, printf(). Оч неплохо справляется с разными типами. Но это не по теме.
По теме - а какие еще подходы можно применить по задаче ТС? Вполне можна указатели на функции обработки разных данных замутить. И насовать в массив. Там полная свобода. Вызывай какую хош с любыми параметрами. А завалится - сам виноват. Или передавать эти указатели параметром в функцию общей обработки данных, а внутри уже вызывать для действий специфических каждому типу. Или писат функции получающую void* и id типа и умеющую обрабатывать разные типы различая по id. Вобщем пути есть, есть где поизвращатся, было бы желание.
По теме - а какие еще подходы можно применить по задаче ТС? Вполне можна указатели на функции обработки разных данных замутить. И насовать в массив. Там полная свобода. Вызывай какую хош с любыми параметрами. А завалится - сам виноват. Или передавать эти указатели параметром в функцию общей обработки данных, а внутри уже вызывать для действий специфических каждому типу. Или писат функции получающую void* и id типа и умеющую обрабатывать разные типы различая по id. Вобщем пути есть, есть где поизвращатся, было бы желание.
Ну да, вариантов много. Для начала я сделал так - завел в классе указатели на переменные обоих типов - и инт и флоат. В зависимости от типа параметра нужный указатель присваивал, а другой оставался NULL. И простенький метод класса is_float(), сущность которого понятна из названия.
Подход корявенький, не спорю, но у него тоже есть свои плюсы перед всякими шаблонами и перегрузкой операторов. Все обьекты получились одного типа, их очень просто было обьединить в массив, и никаких приведений типов. Взял очередной экземпляр из массива, вызвал означенный метод is_float - и сразу знаешь, как работать с данными.
Но при большом разнообразии данных и методов обработки этот путь очень затратный.
ПС: Вот только научится на Си++ ООП сложновато. Капитализм блин не расчитан на проработку знаний. Так что программирование все больше скатывается в мистицизм.
Точно википедик.
он пианист и пидагог... все темы вопрошающих рано или поздно скатываются в грёбаный треш.
прежде чем вы все скатитесь в треш с преференсансом и медичками - ответьте на вопрос из последнего абзаца заголовка темы
прежде чем вы все скатитесь в треш с преференсансом и медичками - ответьте на вопрос из последнего абзаца заголовка темы
да! отвечайте все, пока не началось...
UPDATE
Тема оказалась теоретической.
Как выяснилось, ни один из кодов, приведенных в хаголовке - в Ардуино IDE не компилируется ( я обычно сначала упражняюсь в обычном текстовом редакторе и компилирую gcc /g++ под линуксом, а в ардуино переношу потом - уж больно IDE медленная).
Этот код под g++ собирается и работает, а в Ардуино IDE выдает ошибку:
error: 'dynamic_cast' not permitted with -fno-rtti
С опцией компилятора -frtti компиляция проходит, но не идет линковка. Вычитал на arduino.cc, что якобы avr-gcc не поддерживает RTTI.
В общем, придется остаться на первоначальном коде, который я описывал в сообщении #11. Этот код собирается без проблем и уже работает в реальном девайсе.
прежде чем вы все скатитесь в треш с преференсансом и медичками - ответьте на вопрос из последнего абзаца заголовка темы
Я просто боюсь отвечать. В тему набежали википедики (это люди, которые увидев молознакомое слово, лезут в википедию и, нахватавшись там высоких знаний, начинают с мегаапломбом поучать специалистов, такое уже было с Лиспом и с оптимизацией в C++). В холиварах я с некоторых пор не участвую (освоил метод Овечкина), а потому предпочитаю уйти из темы. Но, прежде, чем уйти, постараюсь ответить на Ваш вопрос.
Так вот, по поводу вопроса в последнем абзаце, я его просто не понял. В строке 42 на самом деле никакого копирования исходного объекта не происходит. В этом легко убедиться, если поставить отладочную печать в конструкторе. Потому никаких танцев с бубнами не нужно. Другое дело, если бы Вы явно определили операцию преобразования типа. Но тогда всё было бы в Ваших руках.
А про полиморфизм, запомните то, что я сказал - это правильно. Это очень широкое слово и оно может применяться практически в любой области знаний, начиная от философии. В контексте классов и ООП (а у данного поста контекст именно такой) оно означает именно то, что я говорил (раз уж тут так принято никому не верить и давать сомнительные ссылки, пожалуйста, читайте). А, вот, например, в биологии оно вообще никак не связано ни с классами, ни с функциями (и чего это Logic на это определение не сослался? Ещё сильнее уёл бы тупого "препода". Не нашёл, наверное).
Евгений, спасибо!
Ещё сильнее уёл бы тупого "препода".
Так и так нормально получилось ;)
Если есть претензии к общепринятому определению полиморфизма приведеному в вики - можете его попробовать его исправить. Только сомниваюсь в успехе ;)
А приводить ссылки на определения полиморфизма в C# не надо, потому как к ардуине и С/С++ это никак не относится. Реализация же полиморфизма в С# действительно возможна только в рамках классов, по простой причине: любая функция в С# может быть объявлена только в рамках класса. Полиморфной функции вне класса быть в C# не может, потому как никакой функции вне класса в С# нет. А в Си/С++ - есть. И могут реализовывать полиморфизм.
Признавайтесь, ссылаясь на определение из С#, Вы сознательно пытались манипулировать или просто не знали и C# тоже?
ПС. Вобще полиморфизм в програмировании (про биологию вам пишет ЕвгенийП) встречается часто и отличается многообразием, например в Си верно 1+1 и 1.0+1.0 Очевидно оператор + полиморфен. А в некоторых языках и "ab"+"cd" тоже верно. А в некоторых и для переменных во время исполнения сработает. Полиморфизм - он такой. Вездесущий, гибкий и никак не завязан с ООП. Наоборот ООП на нем основано на 1/3.
...ну, вот - началось.
и, вообще, считаю, что эту википедию, написанную пиндосовскими наймитами, нужно признать, запрещённой на РФ экстремистской организацией, разжигающей полиморфизм и призывающей к свержению конституционного строя на РФ.
О_О
Нет, Logic, холивара не будет. Вы сказали глупость, и Бог с Вами, не впервой. чай. Ничего Вам доказывать я не намерен и не обязан. Давайте попрощаемся.