лекции (2011) (1160854), страница 7
Текст из файла (страница 7)
Один из недостатков перегрузки операций – несимметричность операндов:
a * b => a.operator*(b)
Так перегружаются операции в C#:
static T operator *(T t1, T t2) {………………………} ;//в шарпе – только статический.
Аналогично и с операторами преобразования: они обызаны быть статическими члеами:
static operator T(X a){…………………}
Итераторы и индексаторы.
Механизм индексаторов в C# - компенсирует отсутствие возможности перегрузки операции индексирования.
Синтаксис:
T this (I index){………………… }
Java – вложенные статические и нестатические классы
class Outer{
static class Inner{…….};
Нестатический блок-класс имеет ссылку на Outer.this
Inner in = this.newInner();//если мы пишем, естественно, внутри класса Outer.
А так – вместо this может стоять ссылка на любой оъект класса Outer.
Outer invoice;
Inner = invoice.newInner();
В Java2 появилось понятие локального внутреннего класса
Iterable f(object[] objs)
{
class Local: Iterable {int i; Local() {i = 0;}… if (i>objs) …}
return new Local(C);
}
Имеет мест доступ к локальным переменным функции, если в данном блоке не изменяются.
Iterable f(final Object{ } obj)
{
class Local Implements Iterable{
.................................
};
return new Local();
}
Такой класс не может быть сделан внешним.
Наличие локальных классов, заметим, позволяет более компактно записывать код.
Если локальные переменные final, то это значит, что они не могут менять своего значения в теле функции. сlosure(«захват» в переводе) – это замыкание блока. Означает, что локальные переменные блока остаются связанными, даже еси мы выходим из блока.
Делегат – прообраз функционального типа данных. Вызывать делегат – это значит по очереди выбрать все элементы из цепочки.
Пример.
delegate int Processor(int i);
Общий синтаксис:
delegate прототип функции
Наш делегат – это именно список функций.
В C# появились:
p = new delegate(int i) {……тело соответствующей функции………….}
Хитрость анонимных делегатов в том, что они тоже могут замыкать соответствующие переменные.
int k;
p = new delegate(int i){ return k+i; }
//переменная k попадает в замыкание, становится захваченной
Теперь p - это функция, которая к k прибавляет i.
int j = p(3);
k=1;
j=p(3);
Классы и стандартные библиотеки. Встроенные классы стандартной
библиотеки.
6. Инкапсуляция и абстрактные типы данных.
Понятие инкапсуляции. Единицы и атомы защиты. Понятие
абстрактного типа данных (АТД) и его достоинства.
Абстрактный тип данных = множество операций. (основной вид современного программирования)
Абстрактный тип данных - это набор процедур, объявленный в отдельном пакете. Здесь, оказывается, есть инкапсуляция:
-
единицы инкапсуляции: тип или экземпляр типа
-
атомы инкапсуляции: отдельные поля и члены типа или весь тип.
В большинстве языков программирования единица инкапсуляции – тип.
В языке Оберон есть защита отдельных членов, что позволяет по отдельности экспортировать отдельные поля.
Ада и Modula-2 инкапсулируют целиком весь тип: такая тактика вынуждает нас к полной инкапсуляции
Атомы инкапсуляции – минимально возможные данные, которые можно скрыть от пользователя. Для всех
класс – ориентированных языков атомы инкапсуляции – это поля класса, а таких языках как Ада, Модула – 2 – минимальным атомом является весь класс, то есть хороший программист на таких языках всегда использует абстрактные типы данных.
Инкапсуляция и логические модули. Управление видимостью.
Реализация АТД в модульных языках программирования (Ада, Оберон,
Модула).
М-2: скрытые ТД
DEFINITION MODULE STACKS;
FROM MYTYPES IMPORT T; //возможность получить доступ к типу, который описан в другом модуле
TYPE STACK; (*скрытый ТД*) //компилятор не знает, что это.
PROCEDURE PUSH
INIT
DESTROY
END STACKS.
DEF -> транслируется в SYM(таблица символов) и OBJ(реализация).
STACK ~ INTEGER или POINTER
TYPE STACK = POINTER TO STACKREC
STACKREC = RECORD … END
:= (shallow, copy), = (равно), # (не равно)
Ада 83:
приватный ТД (~скрытый ТД)
ограниченно приватный ТД
package stacks is type stack is private;
… - описание всех заголовков.
private
… - описание всех приватных структур данных.
:=, =, /=
ограниченно приватный:
type T is limited privaty;
… - оперции.
private type T is … ;
Инкапсуляция и классы. Управление видимостью и управление
доступом. Пространства имен и инкапсуляция. Реализация АТД с помощью
понятия класса.
Управление инкапсуляцией:
• Управление доступом – C++, C#, D
• Управление видимостью – Java
Управление видимостью – «private»-членов как бы просто нет для других классов, они «невидимы».
Управление доступом – все не скрытые (не переопределённые) члены видны, т.е. компилятор постоянно «знает» об их существовании, но при обращении проверяются права на доступ. При попытке обращения к недоступному члену выдаётся ошибка.
Три уровня инкапсуляции:
1.public
2.private
3.protected
«свой» - член данного класса
«чужой» - все внешние классы
«свои» - члены наследованных классов
public разрешает доступ всем
private разрешает доступ только «своему»
protected разрешает доступ «своим» и «своему»
Если требуется, чтобы доступ к приватным членам был не только у «своего», можно для этой этого объявить нужную дружественную конструкцию в теле класса:
friend «объявление друга»;// Можно писать сразу определение. Другом может быть
функция или целый класс
friend «прототип глобальной функции»
friend «прототип функции-члена другого класса»
friend class «имя класса-друга»;// Все методы этого класса становятся дружественными
В Delphi, C#, Java друзей нет
В них реализованы этот механизм реализован немного по-другому:
Delphi UNIT
Java package
C# assembly
В Java по умолчанию пакетный доступ. Это значит, что использовать класс может каждый класс из этого пакета. Если класс объявить как «public class …», то он будет доступен и вне пакета. Использовать класс – наследовать, создавать объекты.
C#:
Сборка – надъязыковое понятие в .NerFramework. Сборка представляет собой совокупность файлов + манифест сборки, Любая сборка, статическая или динамическая, содержит коллекцию данных с описанием того, как ее элементы связаны друг с другом. Эти метаданные содержатся в манифесте сборки. Манифест сборки содержит все метаданные, необходимые для задания требований сборки к версиям и удостоверения безопасности, а также все метаданные, необходимые для определения области действия сборки и разрешения
ссылок на ресурсы и классы.
Внутри сборки идёт разделение на пространства имён, которые содержат описания классов.
Для использования какого-либо пространства имён нужно сначала подключить сборку, содержащую его.
Пространство имён может быть «размазана» по нескольким сборкам.
В C# для членов классов имеются следующие квалификаторы доступа:
• public
• private // по умолчанию
• protected
• internal – член доступен только в классах из сборки
• internal protected – член доступен только в классах-наследниках, находящихся в сборке
Для самих классов:
• public – класс можно использовать в любых классах
• internal – класс можно использовать только в классах из его сборки (по умолчанию)
Delphi
type T = class
…. // здесь объявляются члены, видимые везде их данного модуля и не видимые
// других
public
….
protected
….
private
…..
end;
UNIT – единица дистрибуции
Принцип разделения определения, реализации и использования
(РОРИ). Эволюция принципа РОРИ в современных ЯП.
РОРИ – метод, когда реализация скрыта от пользователя. Пользователю доступен лишь интерфейс , а реализация инкапсулирована.
Тип данных = множество значений + множество операций
Абстрактный тип данных = множество операций
7. Модульность и раздельная трансляция
Виды трансляции. Физические модули. Программная и трансляционная
библиотеки. Раздельная трансляция: зависимая и независимая. Недостатки
независимой трансляции и способы их преодоления.
Особенности зависимой трансляции в современных ЯП. Одностороняя
и двустороняя связь модулей и раздельная трансляция.
Раздельная трансляция и пространства имен.
«раздельная независимая трансляция».
Есть: Си, Си++
Раздельная трансляция означает то, что программа разбивается на части — физические модули или единицы компиляции. Каждая единица может или обязана транслироваться отдельно от остальных.
Независимая раздельная трансляция означает то, что транслятор не обладает информацией об уже оттранслированных единицах и поэтому не может проверить корректность межмодульных связей.
двусторонняя связь между модулями при раздельной трансляции
есть: Ада
При односторонней связи (импорт-экспорт) модуль, экспортирующий имена, не зависит от импортирующих (клиентских) модулей. При двусторонней связи оба модуля зависят друг от друга. В языке Ада двусторонняя связь используется при раздельной трансляции вложенных модулей.
Вложенный модуль обозначается «заглушкой» во внешнем модуле:
procedure Outer is
–- заглушка
procedure Inner is separate;
. . .
end Outer;
При трансляции вложенный модуль снабжается заголовком, связывающим его с объемлющим модулем:
separate(Outer)
procedure Inner is
. . .
end Inner;
Связь «заглушка-заголовок» - пример двусторонней связи.
8. Исключительные ситуации и обработка ошибок
Понятие исключительной ситуации (ИС) и его эволюция. ИС и ошибки
в программах. Четыре аспекта рассмотрения ИС: определение,
возникновение, распространение и обработка. Воплощение этих аспектов в
современных ЯП.
8. Смоделируйте на языке Си++ функции void f() throw (E1,E2,E3) { g(); h(); } предполагая, что конструкция throw не допускается компилятором.
void f()
{
try {
g(); h();
} catch (E1){
throw;
} catch (E2){
throw;
} catch (E3){
throw;
} catch (...) {
unexpected();
}
}
Пример для языка Delphi:
if ptr = nil then
raise Exception.Create('Invalid pointer');
Два подхода к обработке ИС: семантика возобновления и семантика
завершения. Их сравнение. Семантика завершения и современные ЯП.
Семантика возобновления: после обработки исключения управление может вернуться непосредственно в точку, где возникло исключение (варианты: на следующий оператор или на любой оператор из того же блока).
Пример языка: Visual Basic.
Семантика завершения: после возникновения исключения блок, в котором оно возникло, обязательно завершается. Обработка исключения происходит в блоках, вызвавших блок с исключением.
Пример языка: Си++.
В языке Си++ реализована другая семантика: завершения, но в некоторых случаях семантика возобновления может быть смоделирована, например, в случае выделения возобновляемого ресурса (типа динамической памяти):
Resource GetResource() {
for (;;)
try {
Resource r = … // попытка получить ресурс, например
// выделить память
if (success) return r;
throw NoResourceException();
} catch (NoResourceException) {
// попытка найти дополнительные ресурсы (например,
// динамически собрать мусор)
if (!success) throw;
}
}
Свертка стека. Оператор try-finally.
Дополнительные особенности ИС: спецификация ИС, проверяемые и
непроверяемые ИС.
9. Наследование типов и классов
Концепция уникальности типов в традиционных языках и строгая
типизация в объектно-ориентированных языках. Понятие единичного
наследования. Единичное наследование в современных ЯП. Наследование и
модель представления объекта в памяти. Преобразование из производного
типа в базовый. Иерархии типов, статические и динамические типы в
объектно-ориентированных ЯП.
У ссылок и указателей появляется понятие динамического типа. (Статический тип определяется при объявлении. Собственно сами объекты данных обладают только статическим типом.) Динамический тип – это тип объекта, на который ссылка или указатель ссылаются в даный момент. Собственно объекты данных свой тип менять не могут.
С++: class Derived : [модификатор] Base {
// обьявление новых членов };
[модификатор] ::= {private, public, protected}
по умолчанию приватное. Чаще всего приватное наследование используется в написании интерфейсов. Public – не меняет модификатор доступа свойств наследуемого
класса в производном, protected – делает все публичные свойства наследуемого класса защищенными, а private – все свойства наследуемого класса делаем закрытыми (модификатор доступа private).
С#: class Derived : Base {
//определение новых членов }