Лекция 07 (лекции (2002))
Описание файла
Файл "Лекция 07" внутри архива находится в папке "лекции (2002)". Документ из архива "лекции (2002)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Онлайн просмотр документа "Лекция 07"
Текст из документа "Лекция 07"
Записи
На прошлой лекции мы рассмотрели массивы. Если массив - это декартово произведение D*…*D, то запись это D1*…*Dn. В отличие от массива, у которого основная операция – индексирование [ ], у элементов записи есть операция name.id, которая применима слева к объекту типа запись, а справа – к имени поля. Синтаксис записи во всех языках практически одинаков. Если мы заключаем набор объявлений переменных в скобки, то эта совокупность объявлений считается локализованной в записи, и доступ к ним может быть только по имени записи. На Паскале это выглядит так
{ record
…
end
}
В основном записи имеют одну и ту же семантику во всех ЯП. Есть только небольшие отличия в ряде ЯП.
Запись понятие старое. Современная точка зрения на ТД – то, что ТД должны характеризоваться множеством значений и множеством операций. Запись в чистом виде – это множество значений (структура данных) без операций. Поэтому необходимы в современных ЯП специальные механизмы, которые позволяют объединить структуру данных и операции.
Первый класс языков – это языки модульного типа. Это такие языки как Ада, Модула 2, Оберон и Оберон 2. В них есть специальная конструкция модуль, которая как раз и объединяет вместе запись – структуру данных и набор операций.
Подход, который используется в С++ несколько другой. Там само понятие записи обобщается таким образом, чтобы служить одновременно и модулем и структурой данных. В этом языке очень удачно произведена концепция расширение понятия записи до понятия класса. Т.е. в языке С++ как и в С остались записи. Тем не менее структура в языке С++ является одновременно и классом. Единственное отличие записи от класса в этом языке состоит в том, что у нас по умолчанию доступ ко всем элементам записи публичен. Т.е.
struct S{
…
};
абсолютно эквивалентно
class S{
public
…
};
Спецификатор доступа public делает все члены класса публичными.
Чем структура языка С отличается от структуры языка С++? В С++ имя S является обозначением типа и, следовательно, мы можем объявлять переменные типа S.
S x;
В языке С этого делать нельзя. В этом языке соответствующее имя является так называемым тэгом структуры. В С такую переменную надо объявлять
struct S x;
То же самое распространяется и на некоторые другие типы данных в языке С. Например объединения, перечисления и т.д..
Все имена, которые вводятся таким образом, не являются обычными именами данных, именами функций. Они принадлежат своему отдельному пространству. Имена данных в языке С могут быть структурированы. Например
void f {
int x; { double x; …}
}
Т.е. пространство имен в языке С является блочно структурированным, как и во многих других блочных языках. Пространство имен структур не является структурированным вообще. Оно представляет собой одну глобальную таблицу имен. Мы можем описать структуру
struct S {
struct C { … }
…
}
Видимым является не только имя структуры S, но и имя структуры C. Теперь мы можем писать
struct S x;
struct C i;
По-хорошему, надо бы считать структуру С вложенной. Как имена соответствующих объектов данных, которые описаны внутри структуры S, являются локализованными внутри S, так резонно считать, что и структура С локализована внутри структуры S.
В С++ структура – это класс, а класс может иметь вложенный класс, причем имя вложенного класса должно быть локализовано. Беда в том, что тем, что имена структур составляют свое собственное пространство имен, программисты часто пользовались на языке С. Постоянная ситуация: есть структура struct S и есть функция, которая эту структуру возвращает S( ){ }. В языке С подобного рода ситуации возможны потому, что тэг структуры и имя функции S находятся в разных пространствах имен, которые могут пересекаться. Подобного рода структуры мы наблюдаем в стандартных заголовках ОС UNIX. Есть struct stat, есть функция stat( ), которая выдает struct stat. Страуструп не мог составить язык таким образом, чтобы в этом языке не компилировался стандартный заголовочный файл ОС UNIX. Именно это требование было основным с точки зрения совместимости двух языков. Программы можно переделать. Например, заставить программиста прототипировать все функции. Это необходимо на языке С++ хотя бы потому, что в нем появился новый ссылочный ТД, и следовательно передача параметра по ссылке. Изменить программы на языке С так, чтобы они компилировались на языке С++ достаточно просто. Если программа написана на ANSI C, то она будет компилироваться и на С++. А стандартные заголовки ОС переделывать никто не будет. Поэтому совместимость по объявлениям должна быть полной. Было принято компромиссное, но в то же время удачное решение. Имена структур могут совпадать с именами функций. В этом случае считается, что имя функции закрывает имя типа, но к имени этого типа возможен доступ с помощью struct S. Обращаться ко вложенным структурам на языке С++ нельзя. Но оказалось, что подобного рода прием программисты используют достаточно редко.
В С++ все, что можно делать с классами, можно делать и со структурами: добавлять туда члены, управлять доступом, видимостью и т.д..
Несколько по-другому поступили создатели других языков программирования. Например, в языке Delphi тоже нужно было обеспечить совместимость с Турбо Паскалем. Объекты Delphi происходят из шестой и седьмой версии Паскаля, которые уже были объектно-ориентированными. В Турбо Паскале есть обычное понятие записи. Понятие класса в Delphi никакого отношения к записям не имеет. Т.е. вместо того, чтобы расширять старое понятие, придавая ему новую семантику, создатели языка Delphi оставили старое понятие записи. А то, что называется классом, вначале в Турбо Паскале имело название object. А в Delphi используется ключевое слово class. Слово object в Delphi осталось только для совместимости. Одно из основных отличий класса от обычных записей на языке Delphi в том, что все классы имеют ссылочную семантику, а записи – нет. Записи имеют обычную семантику размещения данных, т.е. они не будут размещаться в динамической памяти, если, конечно, мы их не разместим явно.
В языке Java, для которого проблема совместимости отсутствовала, понятия записи отсутствует. Но в ряде случаев понятие структуры оказывается достаточно эффективным. Классический пример:
Class Point {
…
int x,y;
};
В языке Java класс имеет только ссылочную семантику, то массив
Point [ ] arr = new Point [100];
выглядит так: каждый элемент массива есть Point, а каждый Point – ссылка. Причем в начальный момент этот массив проинициализирован нулевыми ссылками. Поэтому необходимо еще провести инициализацию каждого элемента массива:
for (int i= 0; i<100; i++)
arr[i]= new Point;
Мы получаем накладные расходы, как по времени, так и по памяти. Тип данных точка состоит всего из двух полей, а все остальное – операции. И от этого класса никто ни чего скорее всего наследовать не будет, это просто некоторый рабочий класс. Получается, что половина выделяемой памяти лишняя.
В языке С++ такой проблемы нет. Либо мы в языке С++ описываем массив из указателей, либо массив из структур, и тогда все эти структуры размещаются единым блоком памяти. Такое решение более эффективно.
Создатели языка C# пошли на некоторый компромисс. Радикальный подход языка Java, когда у нас нет понятия записи, а есть только понятие класса, а классы во всех современных языках имеют исключительно ссылочную природу, в некоторых случаях оказывается накладным. В языке С++ таких проблем нет, поскольку классы не имеют ссылочной семантики. Создатели C# хотели, с одной стороны, оставить ссылочную семантику класса, а с другой стороны, сделать так, чтобы в некоторых случаях программист мог управлять эффективностью распределения памяти. Поэтому в языке C# появилось понятие структуры
struct {
…
};
которое как и в языке С++ идентично понятию класса,
class{
…
};
Но в классе доступ по умолчанию приватный, а в структуре доступ по умолчанию так называемый внутренний. Кроме того, структура не имеет ссылочной семантики. Если мы Point описали как структуру в языке C#, то arr будет размещаться единым блоком памяти.
Кроме этого, структуры, в отличие от классов мы не имеем права выводить из других классов. Считается, что структура всегда неявно выведена из базисного ТД object, из которого явно или неявно выведены все остальные классы C#. От структуры ничего нельзя наследовать, т.е. структура создается только как оконечный класс. Есть еще ряд отличий, например, в структурах нельзя писать свой конструктор по умолчанию. Т.е. структура в языке C# - ограниченный вид класса, который нужен для того, чтобы ликвидировать некоторую неэффективность, которая связана с чисто ссылочной семантикой класса.
Объединения.
В старых языках возникает еще одно понятие, которое тесно связано с понятием записи. Это понятие объединения. Они появились одновременно с записями. Есть два вида объединений – размеченные и неразмеченные. Пример неразмеченного объединения – объединение в языке С и, следовательно в языке С++
union …{
…
}
Объединение – отождествление нескольких различных структур записи. Зачем оно нужно? Традиционные ЯП поддерживают в той или иной степени концепцию уникальности типа. Основной ее пункт гласит, что каждый ОД имеет только один тип, и этот тип не может меняться. В соответствии с этой концепцией у нас множество объектов делятся на непересекающиеся классы эквивалентности, которые мы и называем ТД. Преимущество этой концепции в надежности, т.к. возникает возможность контроля. Основной недостаток в том, что при такой концепции труднее становится моделировать поведение реального мира. Мы сталкиваемся с тем, что каждый объект реального мира играет различные роли. Например, человека можно рассматривать с одной стороны как, некоторую абстрактную запись в некоторой БД, а с другой стороны его можно рассматривать как некоторый живой организм со своими свойствами. Книга - с одной стороны некоторый информационный объект, который хранится в БД библиотеки. Про нее хранится название, автор, рубрика, кодировка, ключевые слова. Но с другой стороны книга является и физическим объектом, у которого есть объем, вес и т.д.. Подобного рода информация может пригодиться, когда обсуждается вопрос переезда библиотеки из одного здания в другое. Каким образом объединить эти роли – книга, как физический объект и как информационный объект? Каждая роль отражается каким-то ТД. Т.е. набор атрибутов, который описывает книгу как физический объект один, а набор атрибутов, который описывает книгу как информационный объект другой. Какой тип данных выбрать для передачи в процедуру, обрабатывающую данные типа книга. Проблема конфликтности ролей, т.е. то, что в реальном мире объекты играют разные роли, а в классических ЯП эта роль может быть только одна, и она описывается своим ТД, - это так называемая Янус-проблема. Чем ближе программная модель к модели реального мира, тем сильней обостряется эта проблема. Например, самой адекватной моделью пользовательского интерфейса в данный момент является событийная модель. В системе происходят некоторые события. Например, есть внешние события, такие как прерывание от таймера. И система должна реагировать на эти события. Разумеется, ТД события является ключевым в подобного рода системах. Что должен описывать ТД события? С клавиатурным событием мы связываем информацию о том, какая клавиша нажата, нажата она или отпущена и т.д.. С событием прерывание по таймеру связано время поступления события. С событием нажатия кнопки мыши связна информация о том, какая кнопка нажата, нажата она или отжата. С событием движения мыши связана информация о координатах. Фактически получается, что любое внешнее событие описывается своим набором параметров. Общее у них только время поступления. У нас ТД события является объединением разных типов. У всех событий есть общая часть – время поступления, а все остальное разное.
В ООЯП эта проблема решается с помощью наследования. У нас есть некоторый базовый класс, а далее мы из него выводим остальные классы.
Event
KBEvent MouseEvent TimerEvent