Light-electric.com

IT Журнал
4 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Наследование в программировании это

Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании

    Переводы, 15 июля 2015 в 1:28

Вступление

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

Давайте сразу тезисно опишем, что такое наследование:

  • Это механизм создания нового класса на основе уже существующего старого.
  • Старый класс называется «родительским», «предком» («super class»).
  • Новый класс называется «дочерним», «наследником» («sub class»).
  • Наследование нужно для повторного использования кода, которое облегчает следование принципу DRY (Don’t Repeat Yourself — Не повторяйся).
  • Дочерний класс содержит методы и переменные родительского.

Рассмотрим наследование в действии

Создайте консольное приложение и назовите его InheritanceAndPolymorphism . Добавьте два класса, с названиями ClassA и ClassB , как показано ниже:

Как вы можете видеть, класс A пуст, а в B мы добавили два метода и переменную x со значением 100.

Теперь в главном методе Program.cs напишите следующее:

Разумеется, этот код вызовет ошибку:

Error: ‘InheritanceAndPolymorphism.ClassA’ does not contain a definition for ‘Display1’ and no extension method ‘Display1’ accepting a first argument of type ‘InheritanceAndPolymorphism.ClassA’ could be found (are you missing a using directive or an assembly reference?)

Очевидно, причина в том, что в классе А нет метода, который мы вызываем. Однако он есть у класса B. Было бы здорово, если бы мы могли получить доступ ко всему коду в B из A!

Теперь измените описание первого класса на следующее:

Теперь после выполнения программы мы получим:

Т.е. теперь ClassA наследует публичные методы из ClassB , это то же самое, если бы мы скопировали весь код из B в A. Всё, что объект класса B может делать, может и объект класса A. ClassA — дочерний класс, а ClassB — родительский.

Что нужно запомнить: как сын получается похожим на отца, наследует его черты, так и дочерний класс имеет параметры родительского.

LATOKEN, Москва, от 3500 до 5000 $

Теперь давайте представим, что ClassA тоже имеет метод Display1 :

Что будет, если мы запустим код теперь? Каким будет вывод? И будет ли вывод вообще или выйдет ошибка компиляции? Давайте проверим.

Однако мы также получим предупреждение:

Warning: ‘InheritanceAndPolymorphism.ClassA.Display1()’ hides inherited member ‘InheritanceAndPolymorphism.ClassB.Display1()’. Use the new keyword if hiding was intended.

Что нужно запомнить: ничто не может помешать создать в дочернем классе такой же метод, как и в родительском.

Когда мы вызываем a.Display1() , C# сначала ищет Display1() в ClassA , а только потом в ClassB . Поскольку в A такой метод есть, вызывается именно он.

Что нужно запомнить: методы дочерних классов имеют приоритет при выполнении.

Такая возможность нам даётся для того, чтобы мы могли изменить поведение методов предка, если оно нас не устраивает. Однако мы всё равно можем вызывать методы родительского класса следующим образом:

В таком случае вывод будет:

ClassA Display1
ClassB Display1

Что нужно запомнить: ключевое слово base может быть использовано для обращения к методам класса-предка.

Что же, вверх по иерархии мы обращаться можем. Давайте попробуем сделать наоборот:

Error: ‘InheritanceAndPolymorphism.ClassB’ does not contain a definition for ‘Display2’ and no extension method ‘Display2’ accepting a first argument of type ‘InheritanceAndPolymorphism.ClassB’ could be found (are you missing a using directive or an assembly reference?)

Что нужно запомнить: наследование не работает в обратном направлении.

Когда класс A наследуется от B, он получает все его методы и может их использовать. Однако методы, которые были добавлены в A, не загружаются наверх в B, наследование не имеет обратной совместимости. Если попытаться вызвать из класса-родителя метод, который создан в классе-наследнике, вы получите ошибку.

Что нужно запомнить: кроме конструкторов и деструкторов, дочерний класс получает от родителя абсолютно всё.

Если класс С, будет унаследован от класса B, который, в свою очередь, будет унаследован от класса A, то класс C унаследует члены как от класса B, так и от класса A. Это транзитивное свойство наследования. Потомок перенимает все члены родителей и не может исключить какие-либо. Он может «спрятать» их, создав свой метод с тем же именем. Конечно, это никак не повлияет на родительский класс, просто в дочернем метод не будет виден.

Члены класса могут быть двух типов — статический, который принадлежит именно классу, или обычный, который доступен только из реализаций класса (его объектов). Чтобы сделать член статическим мы должны использовать ключевое слово static .

Если мы не наследуем класс ни от какого другого, подразумевается, что мы наследуем его от класса object . Это — родитель всех классов, и он единственный не унаследован ни от чего. Таким образом, такой код:

Автоматически воспринимается C# так:

Таким образом, по свойству транзитивности, ClassA также является наследником object .

Теперь ещё один момент. Если мы захотим сделать так:

То у нас это не получится:

‘InheritanceAndPolymorphism.ClassW’ cannot derive from special class ‘System.ValueType’
‘InheritanceAndPolymorphism.ClassX’ cannot derive from special class ‘System.Enum’
‘InheritanceAndPolymorphism.ClassY’ cannot derive from special class ‘System.Delegate’
‘InheritanceAndPolymorphism.ClassZ’ cannot derive from special class ‘System.Array’

Заметили словосочетание «special class»? Такие классы нельзя расширять.

Что нужно запомнить: ваши классы не могут быть унаследованы от встроенных классов вроде System.ValueType , System.Enum , System.Delegate , System.Array и т.д.

Выше мы описали три класса: ClassW , ClassX и ClassY , который наследуется от первых двух. Теперь попробуем это скомпилировать:

Compile time Error: Class ‘InheritanceAndPolymorphism.ClassY’ cannot have multiple base classes: ‘InheritanceAndPolymorphism.ClassW’ and ‘ClassX’.

Что ещё нужно запомнить: класс может иметь только одного родителя, множественное наследование в C# не поддерживается (оно поддерживается у интерфейсов, но в этой статье мы о них речи не ведём).

Если мы попробуем обойти это правило таким образом:

То это не пройдёт:

Error: Circular base class dependency involving ‘InheritanceAndPolymorphism.ClassX’ and ‘InheritanceAndPolymorphism.ClassW’.

Что нужно запомнить: классы не могут наследоваться циклически (1-й от 2-го, 2-й от 3-го 3-й от 1-го), что, в общем-то, логично.

Операции с объектами

Здесь мы пытаемся приравнять объект от разных классов друг к другу.

Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassB’ to ‘InheritanceAndPolymorphism.ClassA’

Читать еще:  Значок безопасное извлечение устройства не работает

Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassA’ to ‘InheritanceAndPolymorphism.ClassB’

Однако у нас это плохо получается. Даже несмотря на то, что они имеют одинаковые поля с одинаковыми значениями. Даже если бы эти поля имели одинаковые названия. C# работает с типами очень чётко — вы не можете приравнять два объекта от двух независимых классов. Однако, если бы класс A наследовался от B:

…мы бы продвинулсь немногим дальше:

Error: Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassB’ to ‘InheritanceAndPolymorphism.ClassA’. An explicit conversion exists (are you missing a cast?)

Как я уже говорил, C# подходит к вопросам типов очень дотошно. Класс A унаследован от B, значит, имеет все его поля и методы — при назначении переменной типа B объекта типа A проблем не возникает. Однако вы уже знаете, что в обратную сторону это не работает — в классе B нет полей и методов, которые могут быть в A.

Что нужно запомнить: вы можете назначить переменной родительского типа объект дочернего, но не наоборот.

Здесь нам наконец-то представляется шанс обмануть правило:

Приведение типа здесь сработает, но только потому, что эти классы находятся в наследственных отношениях. Два обособленных непримитивных типа привести друг к другу нельзя.

Итак, наш последний блок кода:

Error: Cannot implicitly convert type ‘int’ to ‘char’. An explicit conversion exists (are you missing a cast?)

Что нужно запомнить: можно конвертировать char в int . Нельзя конвертировать int в char (причина в том, что диапазон целого числа больше, чем символа).

Заключение

В этой части мы рассмотрели наследование. Мы попробовали запускать разные варианты кода, чтобы возможно глубже понять суть этого принципа. *этот текст будет изменён после перевода следующей статьи* In my next article, we’ll be discussing about run time polymorphism. Inheritance plays a very important role in run time polymorphism.

Вот что вы должны были запомнить за сегодня:

  • как сын получается похожим на отца, наследует его черты, так и дочерний класс имеет параметры родительского;
  • ничто не может помешать создать в дочернем классе такой же метод, как и в родительском;
  • методы дочерних классов имеют приоритет при выполнении;
  • ключевое слово base может быть использовано для обращения к методам класса-предка;
  • наследование не работает в обратном направлении;
  • кроме конструкторов и деструкторов, дочерний класс получает от родителя абсолютно всё;
  • ваши классы не могут быть унаследованы от встроенных классов вроде System.ValueType , System.Enum , System.Delegate , System.Array и т.д.;
  • класс может иметь только одного родителя, множественное наследование классов в C# не поддерживается;
  • классы не могут наследоваться циклически (1-й от 2-го, 2-й от 3-го 3-й от 1-го), это невозможно чисто логически;
  • вы можете назначить переменной родительского типа объект дочернего, но не наоборот;
  • можно конвертировать char в int . Нельзя конвертировать int в char (причина в том, что диапазон целого числа больше, чем символа).

Напоминаем вам, что в первой статье этой серии вы можете прочитать о полиморфизме. Продолжайте учиться программировать с нами!

Наследование (программирование)

  • Найти и оформить в виде сносок ссылки на авторитетные источники, подтверждающие написанное.

Насле́дование — механизм объектно-ориентированного программирования (наряду с инкапсуляцией, полиморфизмом и абстракцией), позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом.

Другими словами, класс-наследник реализует спецификацию уже существующего класса (базовый класс). Это позволяет обращаться с объектами класса-наследника точно так же, как с объектами базового класса.

Содержание

Типы наследования

Простое наследование

Класс, от которого произошло наследование, называется базовым или родительским (англ. base class ). Классы, которые произошли от базового, называются потомками, наследниками или производными классами (англ. derived class ).

В некоторых языках используются абстрактные классы. Абстрактный класс — это класс, содержащий хотя бы один абстрактный метод, он описан в программе, имеет поля, методы и не может использоваться для непосредственного создания объекта. То есть от абстрактного класса можно только наследовать. Объекты создаются только на основе производных классов, наследованных от абстрактного. Например, абстрактным классом может быть базовый класс «сотрудник вуза», от которого наследуются классы «аспирант», «профессор» и т. д. Так как производные классы имеют общие поля и функции (например, поле «год рождения»), то эти члены класса могут быть описаны в базовом классе. В программе создаются объекты на основе классов «аспирант», «профессор», но нет смысла создавать объект на основе класса «сотрудник вуза».

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

При множественном наследовании у класса может быть более одного предка. В этом случае класс наследует методы всех предков. Достоинства такого подхода в большей гибкости. Множественное наследование реализовано в C++. Из других языков, предоставляющих эту возможность, можно отметить Python и Эйфель. Множественное наследование поддерживается в языке UML.

Множественное наследование — потенциальный источник ошибок, которые могут возникнуть из-за наличия одинаковых имен методов в предках. В языках, которые позиционируются как наследники C++ (Java, C# и др.), от множественного наследования было решено отказаться в пользу интерфейсов. Практически всегда можно обойтись без использования данного механизма. Однако, если такая необходимость все-таки возникла, то, для разрешения конфликтов использования наследованных методов с одинаковыми именами, возможно, например, применить операцию расширения видимости — «::» — для вызова конкретного метода конкретного родителя.

Попытка решения проблемы наличия одинаковых имен методов в предках была предпринята в языке Эйфель, в котором при описании нового класса необходимо явно указывать импортируемые члены каждого из наследуемых классов и их именование в дочернем классе.

Большинство современных объектно-ориентированных языков программирования (C#, Java, Delphi и др.) поддерживают возможность одновременно наследоваться от класса-предка и реализовать методы нескольких интерфейсов одним и тем же классом. Этот механизм позволяет во многом заменить множественное наследование — методы интерфейсов необходимо переопределять явно, что исключает ошибки при наследовании функциональности одинаковых методов различных классов-предков.

Понятие наследования в программировании

Давайте рассмотрим такое понятие, как наследование в программировании.

Зачем это нужно и как это применять на практике?

Возможно, вы не раз видели такую картинку (см. видео), что есть общий (родительский) класс животные, который имеет дочерние элементы в виде кошек, собак др. объектов, которые являются подмножеством класса животные.

Читать еще:  Программирование в сетях windows

Давайте будем разбираться, как это может быть применимо к программированию.

Это теоретическая часть и мы не привязываемся ни к какому языку программирования.

Главная проблема при разработке больших и сложных приложений в том, что в них очень много объектов. См. видео картинку. Могут быть наземные, воздушные элементы и.т.д.

Таких объектов, из которых состоит программа, может быть очень и очень много. В играх — это персонажи этих игр.

На сайтах тоже бывают объекты, например, пост, страница, товар и.т.д. Это все объекты.

Основная сложность в управлении этими объектами.

Предположим, нам нужна команда для объектов, которые обладают каким-то общим свойством.

Например, мы разрабатываем игру и нам нужно дать команду всем воздушным элементам команду приземлиться.

Мы можем дать эту команду:

Или можно дать эту команду абстрактному понятию «летательный аппарат».

В итоге, все летательные аппараты должны пойти на посадку.

Обратите внимание, что количество строк кода уменьшилось до одной строки. Разница в эффективности очевидна.

Как же такое стало возможным, что столько много строк кода, преобразовались в одну?

Это стало возможным из-за того, что мы объединили наши объекты по какому-то общему признаку в отдельную сущность и назвали эту сущность «летательный_аппарат». Это и есть то самое наследование.

Т.е. наследование — это процесс при проектировании нашего приложения при котором мы объединяем какие-то объекты, которые имеют схожие характеристики в единую структуру с главным родительским элементом.

Наследование — это один из способов, как можно организовать код программы. Можно обойтись и без наследования.

Вопрос в том, насколько удобно вам будет в дальнейшем вашу программу расширять, пользоваться кодом этого приложения и взаимодействовать с такой программой.

При проектировании программы, все объекты отображаются в виде схемы, каждый объект прорисовывается в виде прямоугольника (см. видео). Вверху название этого объекта, а внизу, свойства и методы, которые этот объект принимает.

Наследование в такой диаграмме показывается в виде стрелки и такая диаграмма называется UML-диаграмма.

Например, у нас есть сайт и на этом сайте родительским классом будет являться Контент, а дочерними классами могут являться посты и страницы.

И у страниц и у постов есть заголовок, содержимое, комментарии, но каждый из дочерних элементов имеет свои особенные свойства или методы. Например, страница имеет категорию, пост имеет рубрики и.т.д.

Можно не применять наследование для такой структуры, но мы получаем дублирование свойств и методов в каждом классе.

+ Нет повтора кода

+ Простота расширения приложения

Если мы разрабатываем приложение и у нас появился еще один вид контента, например товар. У товара может быть также заголовок, контент и.т.д.

Мы можем просто расширить родительский класс контент и у нас уже появляется новый элемент контента.

+ Простота управления сущностями

Например, если нам нужно выполнить поиск по содержимому всего контента на сайте, намного проще (без использования сложных запросов к базе данных) это будет сделать в варианте, где используется наследование.

Во многих сложных и больших программах, вы можете увидеть ситуацию, что какой-то объект расширяет другие объекты. Есть родительские объекты, а есть дочерние объекты. Каким бы языком программирования вы бы не пользовались, такое понятие имеет место быть и с этим можно очень часто сталкиваться.

Надеюсь стало понятнее, что такое наследование и зачем это нужно и теперь вы можете посмотреть конструкции по наследованию, которые есть в вашем языке программирования.

Главное понимать смысл и процесс программирования должен стать для вас намного более удобнее, проще и приятнее.

Чтобы оставить сообщение, зарегистрируйтесь/войдите на сайт через:

Или зарегистрируйтесь через социальные сети:

ООП. Часть 5. Наследование и ещё немного полиморфизма

Вы всё время пользуетесь результатами наследования, даже если не знаете этого. Рассказываем, как меньше дублировать код и что общего у всех классов.

Оглавление:

Вот мы и подобрались к последнему столпу объектно-ориентированного программирования — наследованию. С его помощью можно создавать классы с общим функционалом, не копируя каждый раз одни и те же поля и методы.

Евгений Кучерявый

Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.

Как наследовать класс

Для начала создадим класс, от которого будем наследовать. Обычно его называют базовым или родительским:

Этот класс (Vehicle) представляет собой транспортное средство, но пока у него есть только слишком общие свойства (название, координаты и скорость) и поведение (перемещение). Нам может понадобиться реализовать класс, который тоже относится к транспортным средствам, но более конкретным. Например, это будет автомобиль (Car).

Если мы хотим, чтобы класс Car наследовал поля и методы класса Vehicle, то при его объявлении после названия нужно поставить двоеточие и имя родительского класса:

Теперь объекты класса Car обладают всеми полями и методами класса Vehicle:

Внимание! Наследовать можно только от одного класса.

Добавление новых полей и методов

Чтобы добавить в дочерний класс новое поле или метод, нужно просто объявить их:

Теперь объекты этого класса могут использовать как метод Move (), так и метод Beep (). То же самое касается и полей.

Наследование конструкторов

Допустим, у родительского класса есть конструктор, который принимает один аргумент:

Все дочерние классы должны вызывать его в своих конструкторах, передавая аргумент того же типа. Для этого используется ключевое слово base:

В скобках после base указывается аргумент, который нужно передать в родительский класс. При этом повторно описывать логику присваивания name не нужно.

Если вы не хотите ничего вызывать, то просто создайте в наследуемом классе пустой конструктор.

Переопределение методов

Часто бывает нужно, чтобы какой-то метод в дочернем классе работал немного иначе, чем в родительском. Например, в методе Move () для класса Car можно прописать условие, которое будет проверять, не кончилось ли топливо. Точно так же может появиться необходимость переопределить свойство.

Методы и свойства, которые можно переопределить, называются виртуальными. В родительском классе для них указывается модификатор virtual:

А в дочернем для переопределения используется модификатор override:

Таким образом можно определить разную логику для разных классов. Это тоже можно считать полиморфизмом.

Читать еще:  Умк по программированию на vba

Наследование от класса Object

Несмотря на то что наследовать можно только от одного класса, существует также и класс Object, который является родительским для всех остальных. У него есть четыре метода:

  1. Equals () — проверяет, равен ли текущий объект тому, что был передан в аргументе.
  2. ToString () — преобразует объект в строку.
  3. GetHashCode () — получает числовой хеш объекта. Этот метод редко используется, потому что может возвращать одинаковый хеш для разных объектов.
  4. GetType () — получает тип объекта.

Хеш — результат преобразования данных, который используется в криптографии.

Любой из них также может быть переопределён или перегружен. Например, метод Equals () можно использовать, чтобы он проверял, равны ли поля объектов:

В данном случае это именно перегрузка, потому что ни один из вариантов метода Equals () не принимал объект класса Car. Отсюда следует, что переопределить можно только метод с такими же принимаемыми аргументами.

Особенности наследования

Есть несколько особенностей, которые нужно знать при работе с наследованием:

  1. Наследовать можно только от класса, уровень доступа которого выше дочернего или равен ему. То есть публичный класс не может наследоваться от приватного.
  2. Дочерний класс не может обращаться к приватным полям и методам родительского. Поэтому нужно либо определять логику приватных компонентов в базовом классе, либо создавать публичные свойства и методы, которые будут своего рода посредниками.
  3. У дочернего класса может быть только один родительский, но у родительского может быть несколько дочерних.
  4. Нельзя наследовать от класса с модификатором static.
  5. Можно наследовать от класса, который наследует от другого класса. Но с этим лучше не злоупотреблять, потому что можно быстро запутаться в их взаимосвязях.

Чтобы лучше это усвоить, стоит попробовать поработать с каждой особенностью на практике и немного поэкспериментировать.

Домашнее задание

Создайте несколько классов персонажей: например, воин, лучник и маг.

Каждый из них должен быть родительским для нескольких других классов допустим, воин будет базовым классом для рыцаря и берсеркера.

У всех персонажей должен быть метод Attack (), при вызове которого у разных персонажей будут выводиться различные сообщения. Например, если атаковать будет маг, то мы должны увидеть сообщение, что он запустил огненный шар.

Заключение

С помощью наследования можно создавать множество полезных классов с общим поведением и свойствами, при этом не дублируя код. Однако это ещё не всё, что можно использовать, — в следующей статье вы узнаете про интерфейсы и абстрактные классы.

А чтобы на практике узнать, как используется ООП со всеми его особенностями, записывайтесь на курс «C#-разработчик с 0 до PRO». Вы попробуете разрабатывать на C# сайты и десктопные приложения, выжимая максимум из объектно-ориентированного программирования.

C ++ — Наследование

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

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

Идея наследования реализует это отношения. Например, млекопитающее животное IS-A, млекопитающее собаки IS-A, следовательно, животное IS-A собаки и так далее.

Базовые и производные классы

Класс может быть получен из более чем одного класса, что означает, что он может наследовать данные и функции из нескольких базовых классов. Чтобы определить производный класс, мы используем список производных классов, чтобы указать базовый класс (es). Список дериваций классов называет один или несколько базовых классов и имеет форму —

Если спецификатор доступа является одним из общедоступных, защищенных или закрытых , а базовым классом является имя ранее определенного класса. Если спецификатор доступа не используется, он по умолчанию является закрытым.

Рассмотрим базовый класс Shape и его производный класс Rectangle следующим образом:

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Контроль доступа и наследование

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

Мы можем суммировать различные типы доступа в зависимости от того, кто может получить к ним доступ следующим образом:

Access

public

protected

private

Производный класс наследует все методы базового класса со следующими исключениями:

  • Конструкторы, деструкторы и конструкторы копирования базового класса.
  • Перегруженные операторы базового класса.
  • Друг-функции базового класса.

Тип наследования

При выводе класса из базового класса базовый класс может наследоваться через общедоступное, защищенное или частное наследование. Тип наследования определяется спецификатором доступа, как описано выше.

Едва ли мы использовать защищенный или частное наследование, но общественное наследование обычно используется. При использовании другого типа наследования применяются следующие правила:

  • Public Inheritance — При выводе класса из общедоступногобазового класса публичные члены базового класса становятся общедоступными членами производного класса, а защищенныечлены базового класса становятся защищенными членами производного класса. Частные члены базового класса никогда не доступны непосредственно из производного класса, но могут быть доступны через вызовы для публичных и защищенных членов базового класса.
  • Protected legacy. При получении из защищенногобазового класса общедоступные и защищенные члены базового класса становятся защищенными членами производного класса.
  • Private Inheritance — При получении из частного базового класса общедоступные и защищенные члены базового класса становятся частными членами производного класса.

Многократное наследование

Класс C ++ может наследовать членов из более чем одного класса, и вот расширенный синтаксис —

Если доступ является одним из общедоступных, защищенных или закрытых и будет предоставлен для каждого базового класса, они будут разделены запятой, как показано выше. Попробуем следующий пример —

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Ссылка на основную публикацию
ВсеИнструменты
Adblock
detector