лекции (2015) (1160856), страница 11
Текст из файла (страница 11)
доглобального пространства (если оно есть) */●●●●●в C++ для функций: если дошли до верхнего пространства ???, то [да но, может дальше]?поищем в пространстве имен, в которых вписаны аргументыв C++ нет управления видимости, только доступав Java -- управления видимостью именно?(private -> видим только внутри класса)([также]? -- в Ада и Оберон, где нет классаов)в C++ видимость нарушается только определением одноименности? объекта ( при чемименно по имени, а не по сигнатуре)в C# почти также, с исключениямиclass X {public: void f() {...}}class Y: X {public: void f(int i) {...}public: void g() { … f(); …}}// произойдет ошибка отсутствия аргумента52Преобразование типов (неявное)Если T1 ковариантен T2 (T1 = T2 или Т1 наследник Т2) - преобразование допустимо.Т1 контравариантен классу Т2, если Т2 ковариантен классу Т1.Если T1 и Т2 инварианты (не коварианты и не контрварианты), можно придумать пользовательскоепреобразование1.
T2(T1) - Ада, модуло, оберон2. (Т2)<выражение типа T1> // C3. C++a. static_cast<T>(expr)b. dynamic_cast<T>(expr)c. const_cast<T>(expr)d. reinterpret_cast<T>(expr)Классы-оболочки. Автораспаковка в Java, C#Кроме базовых типов данных, в языке Java широко используются соответствующие классыоболочки (wrapper-классы) из пакета java.lang: Boolean, Character, Integer, Byte, Short, Long, Float,Double. Объекты этих классов могут хранить те же значения, что и соответствующие им базовыетипы.Компилятор умеет преобразовывать значения простых типов в экземпляры класса-оболочки(упаковка) и обратно (распаковка).C#Javaint i = 123;// The following line boxes i.object o = i;// АвтоупаковкаInteger integer = 9;o = 123;i = (int)o; // unboxing// Автораспаковкаint in = 0;in = new Integer(9);Запрет наследования (sealed в C#, final в Java, final в C++11)sealed в C#При применении к классу, модификатор sealed запрещает другим классам наследовать от этогокласса.
В приведенном примере класс B наследует от класса A, но никакие классы не могутнаследовать от класса B.Модификатор sealed можно использовать для метода или свойства, которое переопределяетвиртуальный метод или свойство в базовом классе. Это позволяет классам наследовать от вашегокласса, запрещая им при этом переопределять определенные виртуальные методы или свойства.class A {}sealed class B : A {} - Никакие классы не могут наследовать от B.53При применении sealed модификатора к методу или свойству его необходимо всегда использовать сoverride.Поскольку структуры неявно запечатаны, их нельзя наследовать.final в JavaКлючевое слово final может применяться к классам, методам или полям.
В применении к классу оноозначает, что данный класс не может подразделяться на подклассы. В применении к методу оноозначает, что данный метод не может быть заменен каким-либо подклассом. В применении к полю,оно означает, что в каждом конструкторе значение поля должно задаваться только один раз и послеэтого никогда не может изменяться.final метод не может быть перегружен или скрыт производными классами.Для классовДля методовpublic final class MyFinalClass {...}public class Base {publicvoid m1() {...}public final void m2() {...}public class ThisIsWrong extendsMyFinalClass {...} // forbiddenpublic staticvoid m3() {...}public static final void m4() {...}}public class Derived extends Base {public void m1() {...} // Ok, overriding Base#m1()public void m2() {...} // forbiddenpublic static void m3() {...} // OK, hiding Base#m3()public static void m4() {...} // forbidden}final в C++11Указывается только у виртуальной функции, обозначая что она не может быть переопределена впроизводных классах.struct A {virtual void foo() final; // A::foo is finalvoid bar() final; // Error: non-virtual function cannot be final};struct B final : A { // struct B is finalvoid foo(); // Error: foo cannot be overridden as it's final in A};struct C : B { // Error: B is final};Основные аргументы1.
Проектирование иерархии наследования очень сложно -> надо запечатать, если реально ненаследуем2. Эффективность - вызов через ?? таблицу дорого => если даже из Х вызывается виртуальнаяфункция, то так как она точно не будет замещена, то сложно снять виртуальность вызова3. Безопасность54Динамическое связывание методов. Полиморфизм. ТВМДинамическое связывание состоит в том, что метод, который нужно вызвать, определяется впроцессе выполнения, а не на этапе компиляции.С++, С# -> virtualJava - все методы являются виртуальнымиВ C# обязательно запись override перед заместителем.ТВМ - таблица вирутальных методов.В каждый полиморфный объект компилятор неявно помещает указательно на соответствующуюТВМ, хранящую адреса вирутальных методов.Строки ТВМ содержат адреса заместителей виртуальных методов: если метод замещен в классе, тозаписывается его адрес, если нет - заимствуется адрес заместителя из таблицы для базовогокласса.Пример:class A {public:virtual void f() {cout << “A::f”;}virtual void u() {cout << “A::u”;}void g() {cout << “A::g”;}};class B:public A {public:void f() {cout << “B::f”;}void g() {cout << “B::g”;}virtual void h() {cout << “B::h”;}};class C:public B {public:void f() {cout << “C::f “;}void u() {cout << “C::u”;}void h() {cout << “C::h”;}virtual void w() {cout << “C::w”;}};void P(A *pa, B & b) {pa->f(); pa->u(); pa->g();b.f(); b.u(); b.g(); b.h();delete pa;}int main() {B b;P(new A, b);cout << “-------------------------” << endl;C c;P(new B, c);return 0;}Результат работы следующийA::f A::u A::g B::f A::u B::g B::h--------------------------------------------55B::f A::u A::g C::f C::u B::g C::hУстройство ТВМ таблиц для данного примера.Объект типа класс *ТВМ для класса *AA::&f()A::&u()B::&f()A::&u()B::&h()C::&f()A::&u()B::&h()C::&w()BCПример для C# (для Java нужно убрать override и virtual)class Program {class А {public virtual void f(int x) {Console.WriteLine("A::f");}}class В : A {public override void f(int x) {Console.WriteLine("B::f");}}static void Main(string[] args) {A a;В b = new В ();b.f(l); // В ::fa = b;a.f (1); // B::fa = new A ();a.f (1); // A::f}}Издержки:1.
Дополнительные команды по извлечению адреса метода при виртуальном вызове.2. Дополнительная память для ТВМ563. Дополнительная память для указателя на ТВМ.Важно! Деструктор полиморфного объекта должен быть виртуальным.Алик ([969-981], done)Можно в наследованном классе написать new virtual f, тогда мы создадим виртуальный метод f,который начнет новую иерархию замещения (скрывает f из базового).В Java тоже появился override - специальный, стоит после функции, как подсказка.В C++ - final - нельзя замещать этот метод.
Как final на Java.Инкапсуляция. Уровни доступа в C#, Java. Управление видимостьюВ C# роль библиотеки играет понятие сборки (assembly), в Java - понятие пакета (package)Уровни доступа в С#1. public - члены доступны везде.2. internal - внутренний, доступный методам всех классов из этой же сборки.3. protected internal - доступен своим методам и методам производных классов (из любыхсборок), методам всех классов этой сборки.4. protected - защищенный, доступный только собственным методам и методам производныхклассов(из любых сборок)5.
private - закрытый, доступ можно получить только из кода того же класса или структурыУровни доступа в Java1. public - доступен методом любых классов, любых пакетов2. без названия (умолчательный), доступный методам всех классов из этого же пакета3. protected - защищенный, доступный только собственным методам, методам производныхклассов из любых пакетов и методам всех классов из этого же пакета.4. private - доступен только собственным методамВ C# и Java есть возможность управлять доступностью из сборки(пакета).Уровни доступа в C++1.
public2. protected3. privateC++, C#57Xошибка доступаXYYZ …{f();}private:Z …{f();}В Java дело обстоит иначе.X: public void f()Y: private void f()Z: {f();} ← вызов X::f→ Нужно поиск проводить по правилам для связи( → private не является заместителем, т. к. нельзя замещать то, что не видно)→ внутри Y вызов f() не будет виртуальным → можно виртуальность вызова снять.Заместитель не может ослаблять доступ.X:private virtual void f();public void g() { f(); }Y:private virtual void f();Если создать класс Z, унаследованный от Y и определить там функцию f, то вызов g приведет квызову f из Z, что и должно быть.в C# - если private, то virtual быть не может.C++→ Реализация событийной обработкиclass X{protected: virtual void OnRvent(){ … }private: void Work(){ OnRvent();}}58class Y: public X{protected: void OnRvent();}void Y::OnRvent(){// своя обработка// хотим вызвать функцию из базового классаthis->X::OnRvent(); // снятие виртуальности вызова}Заметим:X *px; px->X::f(); //снятие виртуальности тожеВ некоторых языках появляются дополнительные слова:Delphi: inherited.имя_методаC#: base.f() //как ссылкаJava: super.f()Javaclass Y extends X{Y() {super(...) //вызов конструктора базового }}С#class Y: X{Y(): base(...) {...}}Вложенные (внутренние) классы, их особенности в Java, C#, C++Это класс целиком определенный внутри другого класса.
Такие классы поддерживаются в C#, C++,Java.В Java существует 4 типа вложенных классов:● Статические вложенные классы. Декларируются внутри основного класса и обозначаютсяключевым словом static. Не имеют доступа к членам внешнего класса за исключениемстатических. Может содержать статические поля, методы и классы, в отличие от других типоввнутренних классов.● Внутренние классы.
Декларируются внутри основного класса. В отличие от статическихвложенных классов, имеют доступ к членам внешнего класса. Не могут содержать (но могутнаследовать) определение статических полей, методов и классов (кроме констант).● Локальные классы. Декларируются внутри методов основного класса. Могут бытьиспользованы только внутри этих методов. Имеют доступ к членам внешнего класса. Имеютдоступ как к локальным переменным, так и к параметрам метода при одном условии переменные и параметры, используемые локальным классом, должны бытьзадекларированы final.
Не могут содержать определение (но могут наследовать) статическихполей, методов и классов (кроме констант).● Анонимные классы. Декларируются внутри методов основного класса. Могут бытьиспользованы только внутри этих методов. В отличие от локальных классов, анонимныеклассы не имеют названия. Главное требование к анонимному классу - он долженнаследовать существующий класс или реализовывать существующий интерфейс. Не могут59содержать определение (но могут наследовать) статических полей, методов и классов (кромеконстант).Вложенные классы в C++, C#, Java имеют доступ ко всем защищенным и приватным полям класса,который их содержит.
В Java (в C#, C++ - не знаю) из объемлющего класса можно получить доступ кприватным полям вложенного класса.Aбстрактный класс - как интерфейс для наследованияТело виртуальной функции обязательно нужно задавать, т. к. ссылка на нее все равно есть в VTBL →ошибка.Таблица виртуальных методов инициализируется в конструкторе. В самом конструкторе еще нетзамещений. Кроме того, тут можно вызвать абстрактный метод, если ему задали реализацию.То есть можно задавать тела абстрактных методов.(по умолчанию - выдает диагностику и рушит программу)C#, Javaabstract class X{// ключевое слово abstract обязательноabstract void f(); // и здесь тоже}→ Абстрактный класс - если перед ним стоит abstract (но может не содержать abstract методов)(просто требуем от него унаследовать).Любой производный, где еще нет переопределения - обязательно с модификатором abstract.АТД (Абстрактный Тип Данных) - абстрагируется от реализации класса (только public методы)АТД (определение из учебника) – это тип, в котором внутренняя структура данных полностьюинкапсулирована.АК (Абстрактный Класс) - абстракция от реализации некоторых методов.Абстрактный класс – это класс, который предназначен исключительно для того, чтобы бытьбазовым классам.