Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Я думаю всем уже окончательно заморочили голову бесконечные статьи про полиморфизм, но меня они достали тоже и я решил… написать свою.
C вообще функциональный язык
Те кто учил С/С++ раньше чем новомодные языки, понимают тонкости очень хорошо.Ой ли? Я знаю кучу вопросов на которые только один человек (из сотен «C++ гуру» приходивших к нам на работу) смог дать правильный ответ — и то только потому, что он до этого занимался разработкой компилятора C++. Даже на многие простейшие вопросы, касающиеся виртуального наследования люди ответить не могут. Про STL (а он вроде как часть языка — во всяком случае описание C++ его включает) я вообще молчу.
STL была включена в спецификацию позже.Это было больше 10 лет назад!
Согласен, что не обладая первоначальной подготовкой и опытом трудно разобраться с тонкостями, но так в любом деле, Вам не кажется?Да, но тут вроде речь идёт об использовании C++ для обучения новичков. Не стоит ли для этого выбрать язык в котором будет не так много тонкостей?
То, что в других языках нет невиртуальных функций ведь не означает, что не нужно объяснять, что будет происходить с функциями при наследовании.Не означает. Это просто означает, что в других языках нет этой идиотской ошибки проектирования, допущенной создателями C++. Скажем невиртуальные функции в Java на самом деле есть — только там нет с ними проблем: они описываются ключевым словом final, не перекрываются и не таят в себе никаких подвохов. И знать о том, что плохо спроектированных языках есть такое понятие как виртуальная функция не более полезно, чем умение склонять слова на латыни для людей, латынью не пользующихся.
Для «простого» объяснения слишком много букв.Простое объяснение — это объяснение, понятное максимальному кругу людей, а не максимально короткое. Вы своему ребёнку тоже будете рассказывать о том почему небо голубое вывалив на него кучку формул из курса физики?
Что такое объект в ООП? Это способ, позволяющий описывать большие программы.
Потому что определение класса закрыто и переопределению не подлежит. Иногда хочется добавить поля данных «на лету» (в CL это, кстати, тоже возможно), но гораздо чаще — те или иные методы.
extend int {
public const function times(block b) {
for (var n = 0; n < this; n++)
b(n);
}
}
export function main() {
3.times() { |i| puts(i as string); };
}Объяснение завязано на каких-то особенностях C++, в которых я не разбираюсьУгу. Про виртуальные функции (вернее про невиртуальные функции) сложно объяснить понятно ибо никакой простой концепции за ними не стоит. Это просто оптимизация — но с тяжёлыми последствиями. Ведь как реализуется полиморфизм? Где-то внутри объекта написано что именно нужно следать когда его попросят напечатать «Hello, world!». И компьютер каждый раз при вызове метода должен заглядывать «внутрь» и решать — то ли ему «писать ручкой по бумаге», то ли «долбить долотом камень». И это — операция не мгновенная. Но во многих случаях нам не нужно этого делать! Скажем если у нас есть ровно один объект соответствующий определённому резюме, никаких альтернативных (полиморфных) реализаций нет, то мы точно знаем — будем ли мы писать по бумаге или будем долбить камень. Это — подход Java/C#/etc. Простой, логичный, бесхитростный. Описал метод как финальный — всё, никаких вариантов! Никакого полиморфизма. Зато быстро. Ибо выбора-то нет и связанных с ним метаний — нету тоже. В случае же с C++/Delphi/etc используется хитрая композиция: варианты могут быть, но если мы точно знаем тип объекта — то мы можем обойти все эти изыски. А если не знаем — то получаем большой облом. Зачем так сделано? Правильный ответ: по историческим причинам. Нет в этом никакого смысла. На каждую программу где это применено по делу найдётся тысяча других программ, где эта возможность привела к сбоям, проблемам и многим часам отладки. Но так сделано — потому приходится с этим делом мириться…
Luca Cardelli написал прекрасную статью On Understanding Types, Data Abstraction, and Polymorphism. Рекомендую прочитать и осознать её, прежде чем учить кого-либо тому, что такое полиморфизм.Великолепная статья! Гениальная! Стиль Бурбаки — именно тот стиль, после которого на вопрос «сколько будет два плюс три» ребёнок даёт ответ «два плюс три будет столько же, сколько три плюс два, потому что сложение коммутативно»…
На мой взгляд объяснение полиморфизма следует начинать как раз с функций ведущих себя одинаково на значениях разного типа (операции со списками, например).Ни в коем случае! Для того, чтобы человек мог поверить в то, что пара совершенно обычных функций
Великолепная статья! Гениальная! Стиль Бурбаки — именно тот стиль, после которого на вопрос «сколько будет два плюс три» ребёнок даёт ответ «два плюс три будет столько же, сколько три плюс два, потому что сложение коммутативно»…
Этот принцип просто-напросто гласит что резюме работника не совпадает с самим работником.
Если вы думаете, что ребёнок поймёт что такое полиморфизм после того, как прочитает взрывающую мозг фразу.Не поймёт. Я хотя и с сарказмом, но довольно-таки чётко определил аудиторию: программист не знающему ни одного, известного вам, языка программирования (ну например он всю жизнь использовал только FORTRAN-66). У детей большие проблемы с воспринятием таких абстрактных концепций как процедура и функция. Где-то до 11-12 лет их этому всему учить бесполезно (за редким исключением). И уж полиморфизму — тем более.
Эта фраза построенна весьма характерным образом и выдаёт наличие у вас научно-технической подготовки, но для обучения она неподходит.Почему нет? Предложите что-нибудь получше. IMNSHO если человек не может воспринять подобную концепцию, то объяснять ему что такое полиморфизм либо рано, либо просто не нужно.
Потому что для любого человека эта фраза тавтология. Потому трудно представить себе человека, который скажет фразу «резюме совпадает с человеком» и даже представить это себе трудно.И именно поэтому она там и появилась. В чём проблема? Изначально полиморфизм — это разделение одного интерфейса и множества реализаций. Что пример с человеком и резюме иллюстрирует как нельзя лучше. Потом можно заметить что есть другие похожие вещи — и их тоже назвать полиморфизмом (людям, знакомым с generics в Java5 или C# будет легче, чем другим). Но если с этого начать… ой плохо будет…
Программисту знающему один язык, легко прочитать статью Карделли и осознать полиморфизм.Если бы. Математику не знающему ни одного языка легко прочитать статью Карделли и осознать что такое полиморфизм. Программисты же… Думаю 5 из 10 знающих что такое полиморфизм прочитав статью Карделли скажут «ну и бред же там написан», а 9 из 10 не знающих что такое полиморфизм не только ничего не поймут, но будут твёрдо уверены в том, что Карделли чего-то явно не того выкурил…
Программист всегда противится и критикует решения, которые не понимает и не хочет в них разбираться. Более того, для людтей, которые сменили несколько языков, с каждым новым открывается его понимание и со временем влюбленность в него.Ой ли? Не cудите обо всех по себе. К людям, которые «Пастернака не читали, но хотят сказать...» иначе как со смехом относиться нельзя. Но почему вы считате что все кто критикуют C++ — его не знают?
И еще одно, хорошие или плохие C/C++ знать вам рано или поздно придется, по вполне объективным причинам ;).Ну если «он очень разрекрамирован и моден» считать объективными причинами, то да. Скажу больше: за последние несколько лет мне с C++ пришлось общаться больше, чем с каким-либо другим языком. Но полюбить язык в котором если нужно удалить гланды, то это делается строго через задний проход я не могу.
Вы совершаете обычную ошибку, привязывая понятие полиморфизма к ООП.Это не ошибка. Я потому и говорю о полиморфизме раньше наследования, что для него не требуется классических классов/объектьов или чего-нибудь в этом роде и даже наследование.
Классический пример не объектно-ориентированного полиморфизма, например, в C++ — шаблоны и перегрузка функций (и операторов).И? Где это вступает в противоречие с тем, что мною описано? Перегрузка функция — весьма ограниченный полиморфизм времени компиляции, шаблоны — классический пример полиморфизма без явного выписывания «резюме». И как раз-таки эти механизмы — отличная иллюстрация к убогости C++: перегрузка функций и шаблоны в C++ — отлично реализуют ООП… если вы не используете наследование и виртуальные функции. Как только вы их смешиваете с шаблонами — всё, проще застрелиться. Недаром не только STL, то и большинство других «шаблонных» библиотек не используют «классический C++ ООП» с виртуальными функциями. Генерики (как в Java или C#) не столь гибки, как шаблоны, но зато гораздо проще в использовании — и решают 99% практических проблем.
Я думал, здесь не дети собрались.А что — в полночь когда человеку исполняется 18 в нём переключается рубильник и он становится сразу другим человеком?
А если здесь взрослые думающие люди, то рассказ о полиморфизме в обязательном контексте ООП вводит их в заблуждение.Если они хорошо думающие — то нет. Ибо никто им не мешает поняв что такое полиморфизм на простом опиании посмотреть более формальное описание в других источниках. Если же они не очень хорошо думающие, то лучше пусть они получат хоть какое-то понятие о том что такое полиморфизм, чем увязнут в высоких абстракциях.
Что касается C++ — вы неправильно понимаете его философию.У C++ нет философии. Просто нет. Филосьфия есть у его отдельных компонент, но единой, целостной философии у этого языка нет — отсюда все беды.
Если вам не нужна эффективность, или просто не хватает… чего-то… чтобы разобраться в сложности C++ — это не повод говорить об его убогости.Я боюсь это вам не хватает… чего-то… чтобы критически взглянуть на своего кумира. Хотя бы один пример: какой глубокий смысл в невиртуальных функциях классов? Скажете «эффективность»? Чёрта с два: final методы Java в этом смысле ничуть ни хуже — а подвохов таят несравненно меньше. Или другой: какой смысл в конструировании объектов по частям (которое не позволяет использовать виртуальные функции в конструкторе как виртуальные)? Это приводит к лишнему коду, замедлению (небольшому, впрочем) и вынуждает изобретать кучу дурацких приёмов (в частности наиболее распространённый подход — ничего не делать в конструкторе, а делать всё в обычной функции Init, что разбивает красивую идею конструкторов вдребезги).
У С++ есть четкая философия, почитайте хотя бы Страуструпа «Дизайн и эволюция С++» (знаю, не читали и читать не будете).Почему же. Читал. Уже её достаточно для того, чтобы понять почему C++ оказался таким угробищем. Он был спроектирован как PL/1 — соберём кучу интересных идей из разных языков и засунем их в один. Так удобные, красивые языки не получаются. Так получаются Франкенштейны.
Java — ни в коем случае не пример для сравнения с C++, который по эффективности пока недостижим. Покажите мне язык, который по эффективности был сравним с С++ и имел бы существенно меньше недостатков?А вы их сравнивали? Java последних версий проигрывает C++ на реальных задачах где-то 20-30% в скорости. Для многих задач это — некритично. 90% (хорошо если не 99%) написанного на C++ кода можно было бы спокойно переписать на других языках — и никто от этого особо не пострадал бы.
Что касается final в Java, я честно говоря не думаю, что он в реализации чем-то отличается от не-virtual в С++.Ничем не отличается.
Согласен, легче ошибиться не написав virtual, чем зря написав final, хотя это дело привычки, как == и =.Это не «дело привычки». Это вещь, которая уже унесла в общей сложности миллиарды долларов. Если вы «зря написали» final, то компилятор сообщит вам об ошибке, вы уберёте одну строку и всё. Если вы забудете слово virtual, то во многих случаях всё кончится многими часами отладки. Если бы невиртуальные функции нельзя было бы перекрывать, а от классов с невиртуальными деструкторами наследоваться, то «заточенность на быстродействие» ничуть бы не пострадала, но кучу времени и сил удалось бы съэкономить. Если бы пресловутую эквивалентность указателя и массива отменили бы для классов с виртуальными деструкторами — то ещё больше удалось бы съэкономить. Но это ж «специальные случаи»!!! Низя, низя, низя.
Конструкторы в паре с деструкторами — это способ инициализации/разрушения объектов со своей философией. Тот способ, который предлагает Java тоже не лишен недостатков (взять хотя бы проблемы с освобождением ресурсов не-памяти).Добавить RIAA в язык — не бог весть какая проблема. Можно сделать, скажем, как в python. А вот отсутствие нормальных механизмов отражений (reflection) приводит к тому, что все изобретают свои костыли (MOC, COM, CORBA, etc).
А StyleGuide — вынужденная мера, поскольку настоящих спецов по C++ — единицы, остальные только думают, что его знают, поэтому городят на нем черт знает что и поругивают его от беспомощности.Нет — это отсуствие философии в языке. TIMTOWTDI (There is more than one way to do it) не работает для крупных комплексах, тут другой приницип нужен (There should be one — and preferably only one — obvious way to do it). StyleGuide эту проблему худо-бедно решает, но необходимость подобных действий язык он явно не красят.
Полиморфизм, шмолиморфизм…