Что такое абстрактный класс
Перейти к содержимому

Что такое абстрактный класс

Абстрактные классы

XYZ School

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

Создавая собственные библиотеки классов, вы можете сами убедиться в том, что у метода зачастую отсутствует содержательное определение в контексте его базового класса. Подобная ситуация разрешается двумя способами. Один из них состоит в том, чтобы просто выдать предупреждающее сообщение. Такой способ может пригодиться в определенных ситуациях, например при отладке, но в практике программирования он обычно не применяется. В подобных случаях требуется какой-то способ, гарантирующий, что в производном классе действительно будут переопределены все необходимые методы. И такой способ в C# имеется. Он состоит в использовании абстрактного метода.

Абстрактный метод создается с помощью указываемого модификатора типа abstract. У абстрактного метода отсутствует тело, и поэтому он не реализуется в базовом классе. Это означает, что он должен быть переопределен в производном классе, поскольку его вариант из базового класса просто непригоден для использования. Нетрудно догадаться, что абстрактный метод автоматически становится виртуальным и не требует указания модификатора virtual. В действительности совместное использование модификаторов virtual и abstract считается ошибкой. Для определения абстрактного метода служит приведенная ниже общая форма:

abstract тип имя(список_параметров);

Как видите, у абстрактного метода отсутствует тело. Модификатор abstract может применяться только в методах экземпляра, но не в статических методах (static). Абстрактными могут быть также индексаторы и свойства.

Класс, содержащий один или больше абстрактных методов, должен быть также объявлен как абстрактный, и для этого перед его объявлением class указывается модификатор abstract. А поскольку реализация абстрактного класса не определяется полностью, то у него не может быть объектов. Следовательно, попытка создать объект абстрактного класса с помощью оператора new приведет к ошибке во время компиляции!

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

В данном примере создается абстрактный класс UserInfo в котором инкапсулируется абстрактный метод ui(), который, в свою очередь, переопределяется в классе UserFamily.

Следует отметить, что в абстрактные классы вполне допускается (и часто практикуется) включать конкретные методы, которые могут быть использованы в своем исходном виде в производном классе. А переопределению в производных классах подлежат только те методы, которые объявлены как abstract.

Введение в ООП с примерами на C#. Часть четвёртая. Абстрактные классы

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

Что такое абстрактные классы

В плане терминологии давайте доверимся MSDN:

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

Абстрактные классы в действии

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

Попытаемся скомпилировать этот код:

Compile time error: Cannot create an instance of the abstract class or interface ‘InheritanceAndPolymorphism.ClassA’

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

Описание методов в абстрактном классе

Попробуем добавить в наш абстрактный класс немного кода:

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

Использование абстрактного класса в качестве базового

Давайте попробуем создать ещё один класс:

Вау. Теперь всё спокойно компилируется.

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

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

Декларация методов в абстрактном классе

Теперь попробуем сделать вот так:

И… мы получаем ошибку компиляции:

Compile time error: ‘InheritanceAndPolymorphism.ClassA.YYY()’
must declare a body because it is not marked abstract, extern, or partial

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

…и снова получим ошибку компиляции:

Compiler error: ‘InheritanceAndPolymorphism.ClassB’ does not implement
inherited abstract member ‘InheritanceAndPolymorphism.ClassA.YYY()’

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

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

Реализация абстрактного метода в производном классе

Так, давайте тогда попробуем реализовать метод YYY() в классе ClassB :

На первый взгляд всё отлично, правда? Но на этот раз мы получим сразу две ошибки компиляции:

Compile time error: ‘InheritanceAndPolymorphism.ClassB’ does not implement
inherited abstract member ‘InheritanceAndPolymorphism.ClassA.YYY()’

Compile time warning: ‘InheritanceAndPolymorphism.ClassB.YYY()’ hides
inherited member ‘InheritanceAndPolymorphism.ClassA.YYY()’.

Дело в том, что в C# нужно явно объявить, что мы реализуем абстрактный метод класса-родителя с помощью ключевого слова override :

Ура! ^_^ У нас наконец-то нет никаких ошибок!

Абстрактный метод базового класса и метод с override класса-наследника должны быть одинаковы

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

То в консоли увидим следующую ошибку:

Compile time error: ‘InheritanceAndPolymorphism.ClassB.YYY()’: return type must be ‘void’
to match overridden member ‘InheritanceAndPolymorphism.ClassA.YYY()’

Инициализация переменных в абстрактных классах

В примерах выше, переменная int a будет обладать значением по умолчанию (0). Мы можем изменить его на нужное нам прямо в абстрактном классе — с этим не связано никаких особенностей.

Абстрактные методы в неабстрактных классах

Такой код не скомпилируется:

Compiler error: ‘InheritanceAndPolymorphism.ClassA.YYY()’ is abstract
but it is contained in non-abstract class ‘InheritanceAndPolymorphism.ClassA’

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

Вызов абстрактного метода родителя

Compile time error : Cannot call an abstract base member:
‘InheritanceAndPolymorphism.ClassA.YYY()’

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

Абстрактный класс, который наследуется от другого абстрактного класса

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

Может ли абстрактный класс быть sealed

И получим ошибку:

Compile time error: ‘InheritanceAndPolymorphism.ClassA’:
an abstract class cannot be sealed or static

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

Что нужно запомнить: Абстрактный класс не может иметь модификатор sealed .

Что нужно запомнить: Абстрактный класс не может иметь модификатор static .

Что такое абстрактный класс в С++

user avatar

Абстрактный класс в C++ — это класс, в котором объявлена хотя бы одна чисто виртуальная функция.

Описание

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

Но возникает возможность создать абстрактный класс, что противоречит архитектуре: как «выделанная общая часть» может быть полноценным классом? Ответ: никак, надо запретить создавать подобный класс. Для этого указывают один из методов как чистый виртуальный ( pure virtual method ), пример для С++:

тем самым заставляя классы наследники определить реализацию для данного метода. Понятное дело, что такое «ограничение» вызывает зависимость и бьет по гибкости, поэтому в 90% случаев таким методом делают деструктор, так как кто-кто, а деструктор в классах, где есть наследование и виртуальные методы нужен всегда. Но не стоит забывать, что при вызове деструктора класс вызывает все деструкторы своих родителей, и поэтому мы объязаны написать реализацию деструктора, в случае определения его как чисто виртуального в абстрактном классе:

Класс или его наследник перестаёт быть для компилятора абстрактным и экземпляр такого класса может быть создан, как только определена реализация для каждого pure virtual method.

Отличие от интерфейса

Так как понятия «интерфейс» и «абстрактный класс» путают, приведу их отличия:

  1. Каждый интерфейс является абстрактным классом, не каждый абстрактный класс — интерфейс (прим. #1).
  2. Интерфейс содержит только public секцию, абстрактный класс не имеет ограничений.
  3. Интерфейс содержит только pure virtual methods, абстрактный класс может содержать и поля, и обычные методы в добавок.
  4. Интерфейс реализуют, абстрактный класс наследуют (для С++ прим. #1)

Примечение #1: так как в С++ нету понятия интерфейс на уровне языка, то программисты симулируют его поведение через абстрактный класс, наследуя его.

Пример

Интересный, как по мне, способ использовать абстрактный класс в связке с этим паттерном Template Method Design Pattern.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *