Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Виртуальные функции






Виртуальные функции – это специальный тип функций-членов класса.

Виртуальная функция отличается от обычной функции тем, что для обычных функций связывание вызова функции с ее определением выполняется на этапе компиляции программы (раннее связывание), а для виртуальных функций – нет. Понятие “виртуальная функция” тесно связано с наследованием классов. Функция-член класса может быть объявлена как виртуальная при следующих условиях:

· класс, содержащий виртуальную функцию, будет базовым классом;

· реализация функции зависит от класса и будет разной в каждом производном классе.

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

Для объявления виртуальной функции используется ключевое слово virtual. Например:

 

class Base

{

public:

virtual void virt() {/* … */}

// …

};

 

Ключевое слово virtual предписывает компилятору генерировать некоторую дополнительную информацию о функции. Если функция переопределяется в производном классе и вызывается с указателем (или ссылкой) базового класса, ссылающимся на представитель производного класса, эта информация позволяет определить, какой из вариантов функции должен быть выбран: такой вызов будет адресован функции производного класса.

 

В иерархии классов, если метод в подклассе имеет такое же имя и сигнатуру типов (type signature), как метод в его суперклассе, говорят, что метод в подклассе переопределяет (override) метод в суперклассе. Когда переопределенный метод вызывается в подклассе, он будет всегда обращаться к версии этого метода, определенной подклассом. Версия метода, определенная суперклассом, будет скрыта. Рассмотрим следующий фрагмент:

// Переопределение методов, class A { int i, j;

A(int a, int b) {

i = a;

j = b; }

// показать i и j на экране void show() {

System.out.println(" i и j: " + i + " " + j); } }

class В extends A { int k;

В(int a, int b, int c) { super(a, b); k = c; }

// Показать на экране k (этот show() переопределяет show() из А) void show() {

System.out.println(" k: " + k); } }

class Override {

public static void main(String args[]) { В subOb = new B(l, 2, 3);

subOb.show(); // здесь вызывается show() из В

} }

Вывод этой программы:

k: 3

Когда show() вызвается для объекта типа в, используется версия show(), определенная в классе в. То есть версия show() внутри в переопределяет (отменяет) версию, объявленную в а.

Если нужно обратиться к версии суперкласса переопределенной функции, то можно сделать это, используя super.

 

Зачем нужны переопределенные методы?

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

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

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

 

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

Существуют ситуации, когда нужно определить суперкласс, который объяв­ляет структуру некоторой абстракции без законченной реализации каждого метода. То есть, иногда необходимо создать суперкласс, определяющий только обобщенную форму, которая будет раздельно использоваться всеми его подклассами, предоставляя каждому подклассу возможность заполнить ее своими деталями. Такой класс определяет только природу методов, кон­кретно реализуемых подклассами. Подобная ситуация может возникнуть, например, когда суперкласс не способен создать значимую реализацию ме­тода. В этом случае находится класс Figure в предыдущем примере. Опреде­ление area () можно рассматривать в качестве " хранителя места". Метод не вычисляет и не отображает площадь ни для какого объекта.

При создании собственных библиотек классов вовсе не обязательно иметь сколько-нибудь значимое определение методов в контексте их суперклассов. Управлять этой ситуацией можно двумя способами. Во-первых, как показа­но в предыдущем примере, можно просто выдать предупреждающее сооб­щение. Хотя этот подход в некоторых ситуациях, таких как отладка, может быть полезен, обычно он неприемлем. Во-вторых, существует возможность определять методы суперкласса, которые должны быть переопределены под­классом для того, чтобы приобрести для указанного подкласса определенное значение. Рассмотрим класс Triangle. В этом случае не имеет никакого зна­чения, определен метод area () или нет. Вы просто хотите иметь некоторый способ, гарантирующий, что подкласс действительно переопределяет все необходимые методы. Решением этой проблемы является абстрактный метод.

Можно потребовать, чтобы некоторые методы были переопределены под­классами с помощью модификатора типа abstract. Эти методы иногда на­зывают методами, " отданными на ответственность подклассу" (subclasser re­sponsibility), потому что для них не определено никакой реализации в супер­классе. Таким образом, подкласс обязательно должен переопределить их — он не может просто использовать версию, определенную в суперклассе. Для объявления абстрактного метода используется следующая общая форма:

abstract type (parameter-list);

Обратите внимание, что тело метода отсутствует.

Любой класс, который содержит один и более абстрактных методов, должен также быть объявлен абстрактным. Чтобы объявить абстрактный класс, про­сто используйте ключевое слово abstract перед ключевым словом class в начале объявления класса. Нельзя создавать никакие объекты абстрактного класса. То есть для абстрактного класса нельзя прямо создать объект с по­мощью операции new. Такие объекты были бы бесполезны, потому что абст­рактный класс определен не полностью. Вы не можете также объявлять аб­страктные конструкторы или абстрактные статические методы. Любой под­класс абстрактного класса должен или реализовать вс, е абстрактные методы суперкласса, или сам должен быть объявлен как abstract.

Далее показан простой пример класса с абстрактным методом, за которым следует класс, реализующий данный метод:

// Простая демонстрация абстракций Java. abstract class A {

abstract void callme ();

// в абстрактных классах допустимы обычные методы void callmetoo() {

System. out. println (" Это конкретный метод.");

class В extends A { void callme () {

System. out. println (" В - реализация callme. ");

class AbstractDemo {

public static void main (String args[]) { В b = new В ();

b. callme (); b. callmetoo ();

}

}

Обратите внимание, что никакие объекты класса а не объявлены в програм­ме. Как сказано выше, невозможно определять экземпляры (объекты) абст­рактного класса. Кроме того, класс а реализует конкретный метод с именем callmetoo (). Это вполне допустимо. Абстрактные классы могут включать столько реализаций, сколько необходимо.

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

Через ссылочные переменные суперкласса выбираются переопределенные методы (во время выполнения).

Использование ключевого слова final с наследованием

Ключевое слово final имеет три применения. Первое — его можно исполь­зовать для создания эквивалента именованной константы. Такое использо­вание было описано в предыдущей главе. Два других применения final свя­заны с наследованием. Оба рассмотрены ниже.

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.