Лекция 12 (Лекции (2009) (Саша Федорова)), страница 2
Описание файла
Файл "Лекция 12" внутри архива находится в папке "Лекции (2009) (Саша Федорова)". Документ из архива "Лекции (2009) (Саша Федорова)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Онлайн просмотр документа "Лекция 12"
Текст 2 страницы из документа "Лекция 12"
Java: Класс Reference – метод get – всегда выделяет сильную ссылку, метод Clear – делает сильную ссылку слабой.
Есть класс WeakReference, производный от класса Reference.
Пример
Представим себе ситуацию, когда мы работает с неким файлом. Пусть существует класс
class DataFromFile{
public Dtaa read(){
d=rdData.get();
if(d!=NULL)
return Data;
else
{
rdData=new WeakReference(d);
}
}
};
Понятно, что в данной ситуации мы внутри класса азводим слабую ссылку и если объъект уже слабосильный, но еще не уничтожен, то метод get() вернет не NULL.
Существуют стратегии, при которых сборщик мусора упорядочивает слабыые ссылки по времени и в получившемся порядке уничтожает их. Проблема упорядочивания слабых ссылок по их «важности» по своей природе напоминает стратегию вытеснения страниц из курса Осей.
Вообще говоря, в Java очень много видов ссылок., в том числе «фантомные» ссылки и «легкодоступные» ссылки.
Преобразования
Третий вид специальных функций – преобразования. Неявые преобразования из одного типа в другой – те, что вставляются компилятором. В Java и Delphi нет возможности описания пользователем неявных преобразований(в Java нельзя перегружать стандартные операторы). В C++ и C# неявные преобразования, определяемые пользователем, разрешены.
Сколько существует арифметических типов данных? Как минимум, 6 знаковых(char, short, int, long, float, double) и столько же беззнаковых. И еще какие-то получчается, что нам понадобится по 20 перегружаемых операций для любого арифметического типа T.
T=>Complex
В некоторых сллучаях необходимо обратное преобразование:
Char * => String =>const char *
В таких случаях определяется оператор приведения к типу:
operator T();
допускающий(это только для С++ и C#) неявные преобразования.
В Java и Delphi перегрузку стандартных операций не допустили, видимо, считая ее излишней.
Перегружаемый операторы в C#:
-
базисный
-
арифметический
-
побитовый
[ ] в C# перегружать нельзя(в отличие от С++, в которых Страуструп решил проблему в общем случае). Общий синтаксис:
T operator *(операнд) {…………………}
Один из недостатков перегрузки операций – несимметричность операндов:
a * b => a.operator*(b)
Так перегружаются операции в C#:
static T operator *(T t1, T t2) {………………………} ;//в шарпе – только статический.
Аналогично и с операторами преобразования: они обызаны быть статическими члеами:
static operator T(X a){…………………}
Пример
class Vector{
T * body;
int size;
public:
vector(int sj){
Body = new T[size=sj];
};
«Неплоский» класс – это класс, который сожержит в себе ссылки на другие объекты.
Возникает неприятность:
vector(size)
vector v(20); //корректно
vector t(10); //корректно
v=t; //что происходит?
А если так:
v=1; //ошибки не будет: сработате оператор преобразования: у нас «случайно» получился конструктор преобразования.
Решение: если мы не хотим, чтобы наш конструктор «использовался» в подобных целях, то с помощью ключевого слова explicit конструктор может стать обычным конструктором. Вот так:
class Vector{
T * body;
int size;
public:
explicit vector(int sj){
Body = new T[size=sj];
};
Теперь наш конструктор может вызываться олько явно.
v=vector(1); //вот так правильно!
v=1; //а вот так уже нельзя
В C# explicit принято по умолчанию. Существует в C# и ключевое слово implicit – чтобы можно было писать (T)expr
Дополнительные возможности механизма классов
Характерны не для всех языков.
-
Свойства – прекрасно выражают дуализм операции и данных. Член класса, синтаксически с точки зрения использования выглядит как член данных. С точки зрения реализации представляется двумя функциями. Доступа: get(считывает значеия свойства) и set(устанавливает значение свойства).
В С++ такого понятия, как свойство, нет. Все данные по определению закрытые, а вместо операций для свойста есть геттеры и сеттеры.
Пример
RAD – Rapid Application Development. Обязательная штука, которая должна присутствовать – виртуальный интегратор ресурсов, пересчет всего в экранные координаты вложенных окон. В современых систмах работает интеграция: размер вложенного окна меняется при изменении рахмеров внутреннего окна, и пересчет координат – довольно сложная процедура.
Синтаксис свойства:
class X{
T prop{
get{ return value; } ;
set{…………………}
}
};
Слово value внутри блока-тела свойства является ключевым, оно равно значению параметра:
void setX(T value){ _X=value; }
X a = new X();
a.prop=t; //вызывается set{ value=t; }
T t= a.prop; //вызывается блок get.
-
published – ключевое слово, появившееся в ИСР(интегрированная среда разработки – пример – свойства кнопочек в WindowsForms). Данное свойство позволяет спрятать детали реализации, в случае надобности пользователь можт изменить значение свойства(как происходит выполнение этой операции, его волновать не должно, это наше дело).
-
Механизм индексаторов в C# - компенсирует отсутствие возможности перегрузки операции индексирования.
Синтаксис:
T this (I index){………………… }
-
Java – вложенные статические и нестатические классы
class Outer{
static class Inner{…….};
Нестатические воженные классы содержат ссылку this.Откуда берутся объекты inner?
Пример:
Inner in=this. new Inner();//если мы пишем, естественно, внутри класса Outer.
А так – вместо this может стоять ссылка на любой оъект класса Outer.
Outer invoice;
Inner in= invoice. New Inner();
В Java2 появилось понятие локального внутреннего класса.
void f(Object[ ] objs){
class Local implements Iterable{
int i;
Local(){ i=0; }
bool hasNext(){ return objs.Length }
};
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);
Анонимные делегаты свидетельствуют о том, что современные языки все более «тяготеют» к функциональным.
Примечание. Еще про делегаты – см. Лекцию 9.