Почему некоторые языки предпочитают статическое связывание методов, а не динамическое?
Почему в C++, C# и Ada 95 по умолчанию принято решение использовать статическую привязку метода, а не динамическую привязку метода?
Означает ли выигрыш в скорости реализации потерю абстракции и возможности повторного использования?
4 ответа
В общем, вы можете считать, что вы должны разработать базовый класс для расширяемости. Если функция-член (для использования словаря C++) не предназначена для переопределения, есть хороший шанс, что ее переопределение на практике будет невозможно, и, несомненно, это невозможно без знания того, что разработчик класса думаю, это детали реализации и будет меняться без предварительного уведомления.
Некоторые дополнительные соображения для двух языков (я не знаю C# достаточно, чтобы написать об этом):
Ада 95 имела бы проблемы с совместимостью с Ада 83, если бы выбор был другим. И, учитывая всю объектную модель Ады 95, делать это иначе не имело бы смысла (но вы можете считать, что совместимость была фактором при выборе объектной модели).
Для C++ производительность, безусловно, была фактором. То, что вы не платите за то, что вы не используете принцип, и возможность использовать C++ так же, как лучший C, весьма способствовали его успеху.
Очевидный ответ заключается в том, что большинство функций не должны быть виртуальными. Как указывает AProgrammer, если функция не была специально разработана для переопределения, вы, вероятно, не сможете переопределить ее (виртуальную или нет), не нарушая инварианты класса. (Когда я работаю в Java, например, я в конечном итоге объявляю большинство функций final
, как вопрос хорошей инженерии. C++ и Ada принимают правильное решение: автор должен прямо указать, что функция предназначена для переопределения.
Кроме того, C++ и (я думаю) Ada поддерживают семантику значений. И семантика значения не работает с полиморфизмом; в Java такие классы, как java.lang.String
являются final
, чтобы имитировать семантику значений для них. Однако многие программисты не беспокоятся об этом, так как это не по умолчанию. (Аналогичным образом, слишком многие программисты на C++ не допускают запрета на копирование и присваивание, когда класс полиморфен.)
Наконец, даже когда класс полиморфен и предназначен для наследования, договор все еще указывается и, насколько это разумно, принудительно применяется в базовом классе. В C++, как правило, это означает, что public
функции не являются виртуальными, поскольку именно публичные функции определяют и обеспечивают выполнение договора.
Я не могу говорить об Ada, но для C++ две важные цели для дизайна C++ были:
- обратная совместимость с C
- Вы не должны ничего платить (насколько это возможно) за функции, которые вы не используете
Хотя ни один из них не обязательно должен указывать на то, что динамическое связывание не может быть выбрано по умолчанию, наличие статического связывания метода (я предполагаю, что вы имеете в виду не виртуальные функции-члены) действительно лучше подходит для этих целей проектирования.
Я дам одну из двух третей ответа Майкла Берра.
Для Ады было важной целью разработки, чтобы язык подходил для системного программирования и использования на небольших встроенных устройствах реального времени (например, ракетные и бомбовые процессоры). Возможно, сейчас есть методы, которые позволили бы динамическим языкам хорошо выполнять такие вещи, но, конечно, не было еще в конце 70-х и начале 80-х, когда язык впервые разрабатывался. Конечно, Ada95 не мог радикально отклониться от базового базового дизайна оригинального языка, так же как C++ не мог от C.
При этом как Ada, так и C++ (и, конечно, C#?) Предоставляют способ динамического связывания методов ("динамическая диспетчеризация"), если вы действительно этого хотите. В обоих случаях это допускается с помощью указателей, которые, по-моему, подвержены ошибкам. Это также может затруднить отладку, поскольку из одних источников трудно сказать, что именно вызывается. Поэтому я избегаю этого, если мне это действительно не нужно.