Jump to content

Множественное наследование

(Перенаправлено из Единого наследования )

Множественное наследование — это особенность некоторых объектно-ориентированных , языков программирования в которых объект или класс может наследовать функции более чем от одного родительского объекта или родительского класса . Оно отличается от одиночного наследования, где объект или класс может наследовать только от одного конкретного объекта или класса.

Множественное наследование было спорным вопросом на протяжении многих лет. [1] [2] оппоненты указывают на его повышенную сложность и двусмысленность в таких ситуациях, как «проблема ромба», где может быть неясно, от какого родительского класса унаследована конкретная функция, если указанную функцию реализует более одного родительского класса. Эту проблему можно решить разными способами, в том числе с помощью виртуального наследования . [3] альтернативные методы композиции объектов, не основанные на наследовании, такие как примеси и черты Для устранения двусмысленности также были предложены .

Подробности

[ редактировать ]

В объектно-ориентированном программировании (ООП) наследование описывает отношения между двумя классами, в которых один класс ( дочерний класс) подклассом родительского является класса. Дочерний элемент наследует методы и атрибуты родительского элемента, что позволяет использовать общие функции. Например, можно создать переменный класс Mammal с такими функциями, как поедание, размножение и т. д.; затем определите дочерний класс Cat , который наследует эти функции без необходимости их явного программирования, а также добавляет новые функции, такие как погоня за мышами .

Множественное наследование позволяет программистам использовать более одной полностью ортогональной иерархии одновременно, например, позволяя Cat наследовать от персонажа мультфильма , домашнего животного и млекопитающего и получать доступ к функциям всех этих классов.

Реализации

[ редактировать ]

Языки, поддерживающие множественное наследование, включают: C++ , Common Lisp (через объектную систему Common Lisp (CLOS)), EuLisp (через объектную систему EuLisp TELOS), Curl , Dylan , Eiffel , Logtalk , Object REXX , Scala (с помощью миксинов классов- ). ), OCaml , Perl , POP-11 , Python , R , Raku и Tcl (встроено в версии 8.6 или через Incremental Tcl ( Incr Tcl ) в более ранних версиях [4] [5] ).

Среда выполнения IBM System Object Model (SOM) поддерживает множественное наследование, и любой язык программирования, предназначенный для SOM, может реализовать новые классы SOM, унаследованные от нескольких баз.

Некоторые объектно-ориентированные языки, такие как Swift , Java , Fortran начиная с версии 2003 года , C# и Ruby реализуют одиночное наследование , хотя протоколы или интерфейсы предоставляют некоторые функции истинного множественного наследования.

PHP использует классы признаков для наследования конкретных реализаций методов. Ruby использует модули для наследования нескольких методов.

Проблема алмазов

[ редактировать ]
Диаграмма наследования класса Diamond.

« Алмазная проблема » (иногда называемая «Смертельным алмазом смерти»). [6] ) — это неоднозначность, которая возникает, когда два класса B и C наследуют от A, а класс D наследует как от B, так и от C. Если в A есть метод, который B и C переопределили , а D не переопределяет его, то какая версия Какой метод наследует D: метод B или метод C?

Например, в контексте с графическим интерфейсом разработки программного обеспечения класс Button может наследовать от обоих классов Rectangle (для внешнего вида) и Clickable (для функциональности/обработки ввода) и классы Rectangle и Clickable оба наследуют от Object сорт. Теперь, если equals метод вызывается для Button объект и такого метода нет в Button класс, но есть переопределенный equals метод в Rectangle или Clickable (или оба), какой метод в конечном итоге следует вызвать?

Ее называют «проблемой ромба» из-за формы диаграммы наследования классов в этой ситуации. В этом случае класс A находится вверху, B и C по отдельности под ним, а D объединяет их внизу, образуя ромбовидную форму.

смягчение последствий

[ редактировать ]

Языки по-разному решают проблемы повторного наследования.

  • C# (начиная с C# 8.0) допускает реализацию метода интерфейса по умолчанию, в результате чего класс A, реализация интерфейсов Ia и Ib с аналогичными методами, имеющими реализации по умолчанию, иметь два «унаследованных» метода с одинаковой сигнатурой, что приводит к проблеме с ромбом. Это смягчается либо требованием A реализовать сам метод, тем самым устраняя двусмысленность или заставляя вызывающую сторону сначала привести A объект к соответствующему интерфейсу, чтобы использовать реализацию этого метода по умолчанию (например, ((Ia) aInstance).Method();).
  • C++ по умолчанию следует каждому пути наследования отдельно, поэтому D объект на самом деле будет содержать два отдельных A объекты и способы использования Aчлены должны иметь соответствующую квалификацию. Если наследство от A к B и наследство от A к C оба отмечены " virtual" (например, " class B : virtual public A"), C++ уделяет особое внимание созданию только одного A объект и способы его использования AЧлены работают корректно. Если виртуальное и невиртуальное наследование смешаны, существует единое виртуальное наследование. Aи невиртуальный A для каждого невиртуального пути наследования A. C++ требует явного указания, из какого родительского класса вызывается используемая функция, т.е. Worker::Human.Age. C++ не поддерживает явное повторное наследование, поскольку не было бы возможности определить, какой суперкласс использовать (т. е. класс появляется более одного раза в одном списке производных [класс Dog: public Animal, Animal]). C++ также позволяет создавать один экземпляр множественного класса с помощью механизма виртуального наследования (т. е. Worker::Human и Musician::Human будет ссылаться на один и тот же объект).
  • Common Lisp CLOS пытается обеспечить как разумное поведение по умолчанию, так и возможность его переопределить. По умолчанию, проще говоря, методы сортируются по D,B,C,A, когда B записан перед C в определении класса. Выбирается метод с наиболее конкретными классами аргументов (D>(B,C)>A) ; затем в том порядке, в котором родительские классы названы в определении подкласса (B>C). Однако программист может обойти это, задав определенный порядок разрешения методов или указав правило комбинирования методов. Это называется комбинацией методов, которую можно полностью контролировать. MOP ( протокол метаобъектов ) также предоставляет средства для изменения наследования, динамической диспетчеризации , создания экземпляров классов и других внутренних механизмов, не влияя на стабильность системы.
  • Curl только те классы, которые явно помечены как общие позволяет повторно наследовать . Общие классы должны определять вторичный конструктор для каждого обычного конструктора в классе. Обычный конструктор вызывается при первой инициализации состояния общего класса с помощью конструктора подкласса, а вторичный конструктор будет вызываться для всех остальных подклассов.
  • В Eiffel функции предков выбираются явно с помощью директив select и rename. Это позволяет разделить функции базового класса между его потомками или предоставить каждому из них отдельную копию базового класса. Eiffel позволяет явно объединять или разделять функции, унаследованные от классов-предков. Eiffel автоматически объединит функции, если они имеют одинаковое имя и реализацию. У автора класса есть возможность переименовать унаследованные функции, чтобы разделить их. Множественное наследование — частое явление при разработке Эйфеля; Например, большинство эффективных классов в широко используемой библиотеке структур данных и алгоритмов EiffelBase имеют двух или более родителей. [7]
  • Go предотвращает проблему ромба во время компиляции. Если структура D встраивает две структуры B и C оба из которых имеют метод F(), таким образом удовлетворяя интерфейсу A, компилятор будет жаловаться на "неоднозначный селектор", если D.F() вызывается, или если экземпляр D присваивается переменной типа A. B и Cметоды могут быть вызваны явно с помощью D.B.F() или D.C.F().
  • В Java 8 представлены методы по умолчанию для интерфейсов. Если A,B,C являются интерфейсами, B,C каждый из них может предоставить различную реализацию абстрактного метода A, вызывая проблему с алмазами. Любой класс D должен переопределить метод (тело которого может просто перенаправить вызов одной из суперреализаций), иначе неоднозначность будет отклонена как ошибка компиляции. [8] До версии Java 8 Java не подвергалась риску проблемы Diamond, поскольку она не поддерживала множественное наследование, а методы интерфейса по умолчанию были недоступны.
  • JavaFX Script в версии 1.2 допускает множественное наследование посредством использования примесей . В случае конфликта компилятор запрещает прямое использование неоднозначной переменной или функции. Доступ к каждому унаследованному члену по-прежнему можно получить, приведя объект к интересующему миксину, например (individual as Person).printInfo();.
  • Kotlin допускает множественное наследование интерфейсов, однако в сценарии проблемы Diamond дочерний класс должен переопределить метод, вызывающий конфликт наследования, и указать, какую реализацию родительского класса следует использовать. например super<ChosenParentInterface>.someMethod()
  • Logtalk поддерживает множественное наследование как интерфейса, так и реализации, позволяя объявлять псевдонимы методов , которые обеспечивают как переименование, так и доступ к методам, которые были бы замаскированы механизмом разрешения конфликтов по умолчанию.
  • В OCaml родительские классы указываются индивидуально в теле определения класса. Методы (и атрибуты) наследуются в том же порядке, причем каждый новый унаследованный метод переопределяет любые существующие методы. OCaml выбирает последнее соответствующее определение списка наследования классов, чтобы решить, какую реализацию метода использовать в случае неоднозначности. Чтобы переопределить поведение по умолчанию, достаточно просто квалифицировать вызов метода с нужным определением класса.
  • Perl использует список классов для наследования в виде упорядоченного списка. Компилятор использует первый найденный метод путем поиска в глубину списка суперклассов или использования линеаризации C3 иерархии классов. Различные расширения предоставляют альтернативные схемы композиции классов. Порядок наследования влияет на семантику класса. В приведенной выше двусмысленности класс B и его предки будут проверены перед классом C и его предки, поэтому метод в A будет унаследован через B. Это общее с Io и Picolisp . В Perl это поведение можно переопределить с помощью mro или другие модули для использования линеаризации C3 или других алгоритмов. [9]
  • Python имеет ту же структуру, что и Perl, но, в отличие от Perl, включает ее в синтаксис языка. Порядок наследования влияет на семантику класса. Python пришлось столкнуться с этим после введения классов нового стиля, все из которых имеют общего предка, object. Python создает список классов, используя алгоритм линеаризации C3 (или порядок разрешения методов (MRO)). Этот алгоритм накладывает два ограничения: дети предшествуют своим родителям, и если класс наследуется от нескольких классов, они сохраняются в порядке, указанном в кортеже базовых классов (однако в этом случае некоторые классы, расположенные выше в графе наследования, могут предшествовать классам, расположенным ниже в графе наследования). график [10] ). Таким образом, порядок разрешения метода следующий: D, B, C, A. [11]
  • Классы Ruby имеют только одного родителя, но могут также наследовать от нескольких модулей; Определения классов Ruby выполняются, и (пере)определение метода скрывает любое ранее существовавшее определение во время выполнения. В отсутствие метапрограммирования во время выполнения это имеет примерно ту же семантику, что и крайнее правое разрешение в глубину.
  • Scala допускает множественное создание экземпляров типажей , что допускает множественное наследование путем добавления различия между иерархией классов и иерархией типажей. Класс может наследовать только один класс, но может смешивать любое количество признаков. Scala разрешает имена методов, используя поиск расширенных «признаков» справа в глубину, прежде чем исключать все, кроме последнего вхождения каждого модуля в результирующем списке. Итак, порядок разрешения: [ D, C, A, B, A], что сводится к [ D, C, B, A].
  • Tcl допускает использование нескольких родительских классов; порядок спецификации в объявлении класса влияет на разрешение имен членов, использующих алгоритм линеаризации C3 . [12]

Языки, допускающие только одиночное наследование , где класс может быть производным только от одного базового класса, не имеют проблемы ромба. Причина этого в том, что такие языки имеют не более одной реализации любого метода на любом уровне цепочки наследования независимо от повторения или размещения методов. Обычно эти языки позволяют классам реализовывать несколько протоколов , называемых интерфейсами в Java. Эти протоколы определяют методы, но не предоставляют конкретных реализаций. Эта стратегия использовалась ActionScript , C# , D , Java , Nemerle , Object Pascal , Objective-C , Smalltalk , Swift и PHP . [13] Все эти языки позволяют классам реализовывать несколько протоколов.

Более того, Ada , C#, Java, Object Pascal, Objective-C, Swift и PHP допускают множественное наследование интерфейсов (называемых протоколами в Objective-C и Swift). Интерфейсы подобны абстрактным базовым классам, которые определяют сигнатуры методов без реализации какого-либо поведения. («Чистые» интерфейсы, такие как интерфейсы Java до версии 7, не допускают никакой реализации или данных экземпляра в интерфейсе.) Тем не менее, даже когда несколько интерфейсов объявляют одну и ту же сигнатуру метода, как только этот метод реализован (определен) в любом месте цепочки наследования он переопределяет любую реализацию этого метода в цепочке над ним (в его суперклассах). Следовательно, на любом уровне цепочки наследования может существовать не более одной реализации любого метода. Таким образом, реализация метода с одним наследованием не демонстрирует проблему ромба даже при множественном наследовании интерфейсов. С введением реализации по умолчанию для интерфейсов в Java 8 и C# 8 все еще возможно создать проблему ромба, хотя это будет проявляться только как ошибка времени компиляции.

См. также

[ редактировать ]
  1. ^ Каргилл, штат Техас (зима 1991 г.). «Противоречие: аргументы против множественного наследования в C++». Вычислительные системы . 4 (1): 69–82.
  2. ^ Уолдо, Джим (весна 1991 г.). «Противоречие: аргументы в пользу множественного наследования в C ++». Вычислительные системы . 4 (2): 157–171.
  3. ^ Шерли, Натанаэль; Дюкасс, Стефан; Ньерстраз, Оскар; Блэк, Эндрю. «Черты характера: составные единицы поведения» (PDF) . Веб.cecs.pdx.edu . Проверено 21 октября 2016 г.
  4. ^ "инкр Tcl" . блог.tcl.tk. ​Проверено 14 апреля 2020 г.
  5. ^ «Введение в язык программирования Tcl» . www2.lib.uchicago.edu . Проверено 14 апреля 2020 г.
  6. ^ Мартин, Роберт К. (9 марта 1997 г.). «Java и C++: критическое сравнение» (PDF) . Objectmentor.com . Архивировано из оригинала (PDF) 24 октября 2005 г. Проверено 21 октября 2016 г.
  7. ^ «Стандарт ECMA-367» . Ecma-international.org . Проверено 21 октября 2016 г.
  8. ^ «Состояние лямбды» . Cr.openjdk.java.net . Проверено 21 октября 2016 г.
  9. ^ "перлобж" . perldoc.perl.org . Проверено 21 октября 2016 г.
  10. ^ Абстрактный. «Порядок разрешения методов Python 2.3» . Python.org . Проверено 21 октября 2016 г.
  11. ^ «Объединение типов и классов в Python 2.2» . Python.org . Проверено 21 октября 2016 г.
  12. ^ «Manpage класса» . Tcl.tk. ​16 ноября 1999 г. Проверено 21 октября 2016 г.
  13. ^ «Объектные интерфейсы — Руководство» . PHP.net . 4 июля 2007 г. Проверено 21 октября 2016 г.

Дальнейшее чтение

[ редактировать ]
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: cd7b5704303610b1aa85336409211d5b__1700956440
URL1:https://arc.ask3.ru/arc/aa/cd/5b/cd7b5704303610b1aa85336409211d5b.html
Заголовок, (Title) документа по адресу, URL1:
Multiple inheritance - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)