Директива sealed в объявлении класса делает наследование от него невозможным.
⇐ ПредыдущаяСтр 2 из 2 В Java бывает только implements - реализация интерфейса
Полиморфное поведение возможно только для родственных классов. Полиморфное поведение – когда в первом случае вызывается f() Base класса, а во втором f() Derived класса. Virtual дает полиморфность. class Base{ class Derived: public Base{ public: virtual void f(){}; } public: virtual void f(){}; }
int main(){ Base b; Derived d; Base *pb; pb=&b; pb -> f(); // f() класса Base pb=&d; pb ->f(); // f() класса Derived }
20) Виртуальные функции. Механизм реализации виртуальных функций. Чтобы объявить функцию как виртуальную, необходимо добавить ключевое слово virutal перед именем возвращаемого типа: Несколько замечаний по виртуальным функциям: 1. В массивах указателей на базовый класс можно хранить объекты только полиморфных типов (базовый и все производные). 2. В массив нужно объединять только те объекты, которые обладают методами с одинаковыми названиями, но разной реализацией. 3. Если некоторый метод базового класса объявлен виртуальной функцией, тогда методы с такой же сигнатурой во всех производных классах тоже будут виртуальными. Virtual дает полиморфность. class Base{ class Derived: public Base{ public: virtual void f(){}; } public: virtual void f(){}; }
int main(){ Base b; Derived d; Base *pb; pb=&b; pb -> f(); // f() класса Base pb=&d; pb ->f(); // f() класса Derived } 21) Абстрактные классы в C++. Абстрактный класс является классом, который может использоваться только в качестве базового для других классов. Абстрактный класс содержит одну или несколько чистых виртуальных функций. Чистая виртуальная функция может рассматриваться как встроенная функция, тело которой определено как =0 (чистый спецификатор). Для чистой виртуальной функции не нужно приводить действительное определение; предполагается, что она переопределяется в производных классах.
К абстрактным классам применимы следующие правила: • абстрактный класс не может использоваться в качестве типа аргу-мента функции или типа возвращаемого значения; • абстрактный класс нельзя использовать в явном преобразовании; • нельзя определить представитель абстрактного класса (локальную/глобальную переменную или элемент данных); • можно определять указатель или ссылку на абстрактный класс; • если класс, производный от абстрактного, не определяет все чистые виртуальные функции абстрактного класса, он также является абстрактным. Например: class Bird { public: void virtual Sing() = 0; };//класс Eagle – также абстрактный class Eagle: public Bird {};// GoldenEagle – не является абстрактным class GoldenEagle: public Bird { void Sign() {…} }; Чистые виртуальные функции Можно подумать, что все остальные функции грязные! Нет, конечно. Чистая в данном случае означает буквально пустая функция. Давайте посмотрим, что такое чистая виртуальная функция. Код: class A { public: virtual void v_function(void)=0;//чистая виртуальная функция }; Как видите, все отличие только в том, что появилась конструкция =0, которая называется чистый спецификатор. Чистая виртуальная функция абсолютно ничего не делает и недоступна для вызовов. Ее назначение служить основой (если хотите, шаблоном) для замещающих функций в производных классах. Класс, который содержит хотя бы одну чистую виртуальную функцию, называется абстрактным классом. Почему абстрактным? Потому, что создавать самостоятельные объекты такого класса нельзя. Это всего лишь заготовка для других классов. Механизм абстрактных классов разработан для представления общих понятий, которые в дальнейшем предполагается конкретизировать. Эти общие понятия обычно невозможно использовать непосредственно, но на их основе можно, как на базе, построить производные частные классы, пригодные для описания конкретных объектов.
23) Абстрактные классы в Java. Абстрактные классы в С#.
Методы класса могут быть объявлены как абстрактные. Это означает, что в этом классе нет реализации этих методов. Абстрактные методы пишутся с модификатором abstract. Класс, в котором есть хотя бы один абстрактный метод, называется абстрактным (в таком классе могу быть и обычные методы). Нельзя создавать экземпляры абстрактного класса — такой класс может использоваться только в качестве базового класса для других классов. Для потомка такого класса есть две возможности — или он реализует все абстрактные методы базового класса (и в этом случае для такого класса-потомка мы сможем создавать его экземпляры), или реализует не все абстрактные методы базового класса (в этом случае он является тоже абстрактным классом, и единственная возможность его использования — это производить от него классы потомки) С# Модификатор abstract указывает на то, что класс может быть использован только как базовый класс при наследовании. Абстрактные классы могут содержать абстрактные методы и методы доступа. Создавать экземпляры абстрактного класса нельзя. Неабстрактный класс, являющийся производным от абстрактного, должен содержать фактические реализации всех наследуемых абстрактных методов и методов доступа. Чтобы указать отсутствие реализации в методе или свойстве, воспользуйтесь модификатором abstract в объявлении метода или свойства. Абстрактный метод - это неявный виртуальный метод. Объявления абстрактных методов допускаются только в абстрактных классах. Поскольку объявление абстрактного метода не предоставляет фактической реализации, тело метода отсутствует, объявление метода просто заканчивается точкой с запятой, аналогично объявлению прототипов: public abstract void AbstractMethod(); Реализация предоставляется методом переопределения override, который является членом неабстрактного класса.
Использование статических или виртуальных модификаторов в объявлении абстрактного метода или свойства является недопустимым. Действие абстрактных свойств аналогично абстрактным методам, за исключением отличий в синтаксисе объявлений и вызовов. Абстрактное унаследованное свойство может быть переопределено в производном классе за счет включения объявления свойства, использующего модификатор переопределения. Java Бывают ситуации, когда нужно определить класс, в котором задана структура какой-либо абстракции, но полная реализация всех методов отсутствует. В таких случаях вы можете с помощью модификатора типа abstract объявить, что некоторые из методов обязательно должны быть замещены в подклассах. Любой класс, содержащий методыabstract, также должен быть объявлен как abstract. Поскольку у таких классов отсутствует полная реализация, их представителей нельзя создавать с помощью оператора new. Кроме того, нельзя объявлять абстрактными конструкторы и статические методы. Любой подкласс абстрактного класса либо обязан предоставить реализацию всех абстрактных методов своего суперкласса, либо сам должен быть объявлен абстрактным, Интерфейсы Java созданы для поддержки динамического выбора (resolution) методов во время выполнения программы. Интерфейсы похожи на классы, но в отличие от последних у интерфейсов нет переменных представителей, а в объявлениях методов отсутствует реализация. Класс может иметь любое количество интерфейсов. Все, что нужно сделать - это реализовать в классе полный набор методов всех интерфейсов. Интерфейсы обладают своей собственной иерархией, не пересекающейся с классовой иерархией наследования. Это дает возможность реализовать один и тот же интерфейс в различных классах, никак не связанных по линии иерархии классового наследования. Интерфейсы являются аналогом механизма множественного наследования в C. Оператор interface Определение интерфейса сходно с определением класса, отличие состоит в том, что в интерфейсе отсутствуют объявления данных и конструкторов. Общая форма интерфейса приведена ниже:
interface имя { тип_результата имя_метода1(список параметров); тип имя_final1-переменной = значение; } У объявляемых в интерфейсе методов отсутствуют операторы тела. Объявление методов завершается символом;. В интерфейсе можно объявлять и переменные, при этом они неявно объявляются final - переменными. Это означает, что класс реализации не может изменять их значения. При объявлении переменных в интерфейсе их обязательно нужно инициализировать константными значениями. Ниже приведен пример определения интерфейса, содержащего единственный метод с именем callback и одним параметром типа int. interface Callback { void callback(int param); } Оператор implements - это дополнение к определению класса, реализующего некоторый интерфейс. class имя_класса [extends суперкласс] [implements интерфейс0 [, интерфейс1...]] { тело класса } Если в классе реализуется несколько интерфейсов, то их имена разделяются запятыми. Ниже приведен пример класса, в котором реализуется определенный нами интерфейс: class Client implements Callback { void callback(int p) { System.out.println("callback called with " + p); } } 25) Наследование для интерфейсов. Проблемы, связанные с множественным наследованием в C++. Один интерфейс может наследовать другой. Синтаксис наследования интерфейсов такой же, как и у классов. Когда в классе реализуется один интерфейс, наследующий другой, в нем должны быть реализованы все члены, определенные в цепочке наследования интерфейсов.
Таким образом, интерфейсы могут быть организованы в иерархии. Как и в иерархии классов, в иерархии интерфейсов, когда какой-то интерфейс расширяет существующий, он наследует все абстрактные члены своего родителя (или родителей). Конечно, в отличие от классов, производные интерфейсы никогда не наследуют саму реализацию. Вместо этого они просто расширяют собственное определение за счет добавления дополнительных абстрактных членов. Использовать иерархию интерфейсов может быть удобно, когда нужно расширить функциональность определенного интерфейса без нарушения уже существующих кодовых баз. В отличие от классов, один интерфейс может расширять сразу несколько базовых интерфейсов, что позволяет проектировать очень мощные и гибкие абстракции. -----------------------
C++ требует, чтобы программист указал элемент какого из родительских классов должен использоваться, то есть «Worker::Person.Age». C++ не поддерживает явно повторяемое наследование, так как отсутствует способ определить какой именно суперкласс следует использовать (смотри критику). C++, также, допускает создание единственного экземпляра множественного класса благодаря механизму виртуального наследования (например, «Worker::Person» и «Musician::Person» будут ссылаться на один и тот же объект).
Множественное наследование критикуется за следующие проблемы, возникающие в некоторых языках, в частности, C++: § семантическая неопределенность часто совокупно представляется как Проблема ромба(ситуация в объектно-ориентированных языках программирования с поддержкой множественного наследования, когда два класса B и C наследуют от A, а класс D наследует от обоих классов B и C. При этой схеме наследования может возникнуть неоднозначность: если метод класса D вызывает метод, определенный в классе A (и этот метод не был переопределен), а классы B и C по-своему переопределили этот метод, то от какого класса его наследовать: B или C) § отсутствует возможность явного многократного наследования от одного класса § порядок наследования изменяет семантику класса[источник не указан 914 дней] Множественное наследование в языках с конструкторами в стиле C++/Java усиливает проблему наследования конструкторов и последовательностей конструкторов, таким образом создавая проблемы с поддержкой и расширяемостью в этих языках. Объекты в отношениях наследования со значительно отличающимися методами конструирования довольно трудны для реализации в рамках парадигмы последовательности конструкторов. 26) Виртуальные базовые классы. Чтобы устранить дублирование объектов непрямого базового класса при множественном наследовании, этот базовый класс объявляют виртуальным. Для этого в списке базовых классов перед именем класса необходимо поместить ключевое слово virtual. Например, класс X будет виртуальным базовым классом при таком описании: class X {... f();... }; class Y: virtual public X {... }; class Z: virtual public X {...); class D: public Y, public Z {...); Теперь класс D будет включать только один экземпляр X, доступ х которому равноправно имеют классы Y и Z. Если мы хотим иметь одну копию полей данных, определенных в базовом классе, то промежуточные классы должны определять, что из унаследованного от общего родительского класса является виртуальным. Ключевое слово virtual показывает, что надкласс может появляться более одного раза в подклассах, порождаемых из определенного класса, но при этом нужно оставлять только одну его копию. Такое решение несовершенно. То, что в дочернем классе должна быть одна копия, необходимо указывать ни в нем, а в классах, от которых он порождается. Таким образом, нет возможности обеспечить гибкость при создании дочернего класса, в котором в одной ситуации мы бы хотели иметь одну копию, а в другой – две. При проектировании классов приходится жестко определить, будем ли мы в дальнейшем использовать их как виртуальные базовые классы или нет. 27) Наследование реализации.
Для того чтобы использовать функциональность методов предка для потомка, необходимо, чтобы структура данных потомка содержала все поля данных предка. Наследование функциональности обычно требует использования наследования состояния, поэтому эти два типа наследования можно объединить в один - наследование реализации. Такой механизм наследования используется для объектов в большинстве современных языков программирования, в том числе, в C++ и Java. Однако в распределенных системах наследование почти никогда не применяется. Простейший вариант наследования реализации предполагает использование концепции делегирования: объект имеет ссылку на своего предка (предков), а тело метода, унаследованного от этого предка, состоит из переадресации вызова по ссылке. Таким образом, вызов передается от потомков к предкам, пока не будет найден объект, который способен реально выполнить этот вызов. Несмотря на всю простоту и очевидность такого подхода, у него есть серьезный недостаток при использовании в распределенной системе: вызов передается от объекта к его предку. Но и этот предок, в свою очередь, может передавать вызов своему предку. Если иерархия наследования достаточно глубока, на один вызов метода приходится несколько последовательных удаленных вызовов. Подобные вызовы требуют значительных ресурсов, скорость работы системы может упасть в несколько раз. Следовательно, рассмотренный способ может оказаться очень неэффективным. Рассмотрим простейший пример. Ниже приведены три интерфейса на языке определения интерфейсов IDL [3]. Допустим, при создании объектов, реализующих данные интерфейсы, использовалось наследование реализации. Тогда вызов метода op_A() из объекта C будет переадресован в объект B, а оттуда — в объект A. Такая последовательность вызовов в распределенной системе потребует значительных затрат ресурсов и времени. 28) Вложенные классы в С++/С#. Вложенные классы в Java. Безымянные вложенные классы. Иногда некоторый класс играет чисто вспомогательную роль для другого класса и используется только внутри него. Сlass Outer{ int a; public: class Inner{ public: void test(Outer a){ a.a=-1;} } void f(){ Ineer in; in.test (*this) } } int main(){ Outer o; o.f(); Outer::Inner i; i.test(o); } Вложенный класс м. быть объявлен внутри отдельного метода внешнего класса. Внутренний класс можно использовать только в том методе в котором он объявлен. В Java внутренний класс имеет доступ к локальным константам. В java можно использовать: вложенные статические, влож. не статич, внутренние и безимянные вложенные классы Class Outer{ private int a; //влож статич. класс в java аналог влож в С++ public static class Inner{ public void test(Outer co){co.a=-1;} } и т.д. только Outer.Inner i; // ccылка i=new Outer.Inner();
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|