Языки программирования, 17 лекция (от 31 ноября)

Материал из eSyr's wiki.

Перейти к: навигация, поиск

ЯП


Лектор сказал: Лекций 7, 9 ноября не будет


п3. Определение типов классами.


Тип данных = Множество операций + множество значений.


До этого определяли структуры, определяли операции, и связывали это модулем. Но при этом ощущается некоторая избыточность. Например, описываем пакет Стеки, определяем там тип Стек. Уже дублирование имён. Ображение: Стеки.Стек. Класс – дуален – и структура, и обёртка.

Класс по определению предназначен исключительно для ТД.

Каким обращом множество операций связывается со множеством значений.


Основные языки: Си++, Си шарп, Джава, Дельфи.


Дельфи – экле..тичный язык, там можно работать и в классич парадигме, и в ООП.


По синтаксису Дельфи далеко отстоит от святой троицы.


Члены класса:

  1. Члены-данные определяют структуру, множество значени

  2. Функции (процедуры) – оперделяются множество операций

Синтаксис:

Сишный

class Name {

описание членов

};


Синтаксически похоже на описание структуры, класс без процедур – та же структура.


Описание членов:

ТД – описание переменных

ЧФ – прототип


Мы рассм класс как ТД.


Примеров несть тому числа.


Класс является и модулем. Внутри класса может описано статические члены.


Внутри класса могут быть влооженнвые опеределния типов. - по аналогии с модулем.


Отличия других ЯП от С++ - в Си шарп то же самое, только кроме классов могут быть перечислимые типы. То же самое в Джаве – опеределение членов-функций, вложенных классов.


В чём отличия Си шарп Джавы, от С++:

Кроме прототипов функцйий могут быть опеределния классов.

Class X {

vois f()int i;

void g() {}

}


Это некий синтаксический сахар. Здесь идёт речь об inline – функциях

Inline – функции – функции, тело которых может вставлять в месте вызова.

Похожи на макросы. Отличие от макросов -

Типичный пример inline-функции – максимум:

inline T Max(T x, T y) {return x<y ? y : x; }

и вместо max(a,b) будет подставлен a<b ? a : b;

Очень похоже на соотв define. В чём основная разница -

Если поытаться описать такой макрос:

#define max(x,y) ((x)<(y)) ? (y) : (x);

Скобки нужны, чтобы вместо x и y

Внешне похоже, Но инлайн предпочтительнее. В inline типы должны совпадать. При вызове инлайн будет сделано преобразование типов, если нужно, или выдана ошибка в терминах вызова. Дефайн при ошибке будет ругаться в терминах макроподжстановки.

Инлайн функции хороши для современных суперскалярны процессоров. Срывает конвейер операция перехода, а любой вызов функции – нелокальный переход, и это может серьёзно уронить производительность. Inline – программист рекоммендует компилятору провести подстановку тела функции. В случае виртуальных функции инлайнятся функции очень редко, ибо во время компиляции можно не знать, что делать. Ещё не инлайнятся циклы, ибо там есть переходы. И сущ быстродействия это не даст. Зачем это нужно было Страуструпу? Это было и в Си, но в каждом отдельном компиляторе для этого были свои прагмы, и Страуструпу не нравилось, что это не стандартизовано.

А зачем Страуструпу понадобилось повышать быстродействие подобного рода функции? Это связано с ООП, в частности, с появлением geyters-setters. Например, если мы хотим сделать переменную read-only, то делаем только getter: T getA() {return a; } И его хорошо бы инлайнить.

Впервые понятие класса появилось в Симуле, и С. Обратил внимание на её неэффективность, так как там подобные функции не инлайнились. И вместо того, чтобы решать проблему в частности, он решил её глобально.

И был введен некоторый синтаксический сахар. Чтобы вместо того, чтобы писать

class X {

T GetA();

};

innline T X::GetA() { return a; }

молжно просто определить функцию прямо внутри класса, и тогда она будет считаться inline. Это было сделано для того... Есть физическое разделение интерфейса и реализации (хедеры и цппшники). И опеределние класса помещается в хедеры, а определение фукнции в реализацию. В С++ есть принцип РОРИ.

Но для inline это не катит, ибо компилятор должен знать оперелеление inline-функции, поэтому их надо помещать в хедер. Поэтому для инлайн-функции С. Разрешил такую возможность.

В С++ реализовано РОРИ, а в Си шарп и Джаве – нет. Там если описали функцию-член, то обязаны указать её реализацию, только если это не чисто виртуальная функция. То, что это неудобно с точки зрения чтения, это да. Во время отладки это удобно. А во время чтения средства систем разработки позволяют вырезать интерфейс и смотреть на него. Сам язык не нагружается соотв понятиями. Это характерно для совр ЯП, которые проектируются с учётом того, что будут погружены в соотв среду.


Дельфи (идейно ближе всего к С++):

type X=class

объявление членов;

i:integer;

procedure P;

function f(k:integer):integer;

end;


Тут чётко РОРИ.

В объявлении (спецификации) класса только переменные и прототипы. Это в интерфейсной части. Правда, может быть и в реализации, но это уже внутренние классы, только для реализации.


Implementation

procedure X.P; begin ... end;

function X.F ... begin ... end;


Небольшие синтаксические отличия:

Мы пока не говорили, каким образом скрывается реализация. Во всех этих языках есть возможность возможность экспорта членов, для этого используется модификатор publiс, действие которого распротраняется на вс объявления после него. В Джаве-Си шарп спецификатор перед каждым объявлением. Различия в умолчаниях.

Си++: для классов по умолч прайвейт, в структурах – private

C#: для классов и структур – private

Java, Delphi: в Джаве есть пакетный, в Дельфи – для юнита – там доступ в перделах пакета-юнита по умолчанию. Сами классы могут объединяться в модули – и это модульный доступ.


Про доступ мы пока больше гововрить не буду.


Множество операций+множество значений

Множество операций – public функции-члены

Множество знач – Члены-данны, хорошим тоном счиатеется их скрытие.


Сосредосточимся неа деталях:

Синтаксис обращения.

Члены локализоканы внутри класса. Есть Класс Х, есть член х. Синтаксис доступа – Х.х. Этот синтаксис взят из синтаксиса записи. Этот синтаксис просто обобщён на класс. ФЧ отличаются тем, что им неявно передаётся ссылка/указатель на объект класса.

Claas X {

void f() {...}

};

E а будет неявный параметр – ссылка на объект. При вызове x.f(); юудет передана ссылка на х. И в теле функции будет определёна переменная:

C++ - указатель this

С# - ссылка this

Java, Delphi – сслка Self

почему Страуструп не использовал self, хотя оно использовалось в Smalltalk, который явл оперделяющим с тз терминологии: Вилдимо, у него что-то там конфликтовало. В Си шарп было взято по принципу чтобы не было похоже на Джаву.


К члену класса обращаемся снаружи только через имя объекта класса. Изнутри – можно через this/self, а можно просто через имя члена. Класс образует свою зону видимости – внутри членов класса можем обращаться к другим просто по имени. Класс образует свою область видимости. И если есть глобальное описание одноимённых переменных, то он и перекрываются. В Джаве и Си# глобальные переменны еотстусттвуют, но там есть вложенные пространства имён и та же проблема. Но this/self нужен, когда, например, класс передаёт ссылку на себя. В Си шарп this испоьлзуется в документации, когда

есть i, j как ячлен класса

void (int i) {int i; - нельзя, ибо имена формальных параметры как бы локализованы внутри тела функции

}

тело функции – вложенная по отношению класса область видимости, а что делать с объявлением в классе? Везде закрывается, кроме Delphi, в Delphi просто нельзя так делать, и это правильно. В остальных языках к члену может обратиться через this/


Попробуе мпровести параллель между определением в омдульных языках и в ОО.

Ада (Модула-2)

package M is

type T is ///;

procedure P(X:T);

procedure P;

X: Y;

end M;

package body M is

procedure G(x:T) is ... end G;

Z : Y;


ОО:

class T {

public:

void P();

а что делать с P без параметров? Он может обращаться к X, Z. Аналог – статические члены, им не передаётся никакой неявный параметр.

static void PP();

};

Как обращаться к PP? А как к P без параметров в Аду? Через имя модуля – T.P, в ОО – через имя класса – T.PP(). Но в С++, и за это язык ругают, можно ещё через объект класса – t.PP(), а через имя класса – T::PP().

Точка и два двоеточия не перегружаются.

Public:

Y X;

private

void G();

static Y Z;

};


Если брать модульный язык, где мы описываем отдельно стоящие параметры и переменные, то в ОО мы и описываем статически. Статические члены являются глобальными, но они локализованы в имени типа, и до них мы добираемся через имя класса. Класс – полный аналог модуля.


В Си шарп и Джаве употр статич членов очень часто, ибо это аналог глобальных членов и функции. У программиста на С++ есть возможность вынести подобные функции на глобаьлный уровень, а в Си шарп и Джаве эти понятия отсутствуют.


Зачем нужны статические данные:

Корнстанта.

Class Stack {

Int top;

T body[N];

const int N; - глупость, потому что будет для каждого своя, надо делать статик.

}

Статические функции нужны для того, чтобы работать со статическими данными.


Статические члены существуют вне зависимости от объектов класса.


Пример: пусть мы хотим располагать объекты класса только в динамической памяти (актуально только для С++). Можно ли это обеспечить? Легко. Оставить публичной одну функцию – static T * MakeT() {return new T(); } Если сделать все конструкторы приватными, то единственый споособ создать объект класса – вызвать эту функцию.


Статические члены есть во всех ОО-языках, так как некоторые ыещи можно проможедировать только ими.


Константы – часть объекта или класса?

В Смаллтоке есть пноятие класс, и есть понятие экземпляр. У класса есть методы, есть переменные. Переменные в См. Делились на переменные экземпляра, которые были для каждого экземпляра свои и переменнаяе класса, коотрые размещались в единственном числе. Осталось обобщить на функции – к нестатическим передаётся ссылка на объект.


Любая ли константа должна быть переменной класса? Не все. Одни принадлежат экземпляру, другие всему классу. В Джаве для констант экз есть final. Где они инициализируются? Есть кноструктор, который вызывается при создании класса - и там инициализируются константы, только один раз. В Си шарп – специали=ьный синтаксис конструктора – инициализирующая часть.

Где инициализируются статические данные – единственная возможность инициализировать статические данные – в cpp.

Class X {

static const int i; - обязаны инициализировать. В теле класса инициализацию сделать нельзя.

};

Вопрос размещения – из-за раздельной трансляции компилятор не знает, где размещается статические данные. Ибо он не знает, откуда взялся класс – из инклюда или из текста. Поэтому, если описана статическая переменная, программист должен указать её размещение.

Поэтому в цппшнике надо написать int X::i = 0; таким образом явно указали размещение. А если в хедере – то досявые-виндовые редакторы связей будут ругаться? Ld – выдадет ворнинг.

Аналогом размещения функции члена для фугкции – оперделение.

Пока мы не вызываем функции-не обращаемся к переменным, ошибки нет, как только обратимся – ошибку найдёт редактор связей.

Где будут инициализироваться статические члены –


в Си шарп можно гибко управлять порядком загрузкой модулей, поэтому можно предсказать порядок, в котором будут выполняться конструкторы, посему в инициализации можно писать что-то содержательное.

В Джаве есть статический блок инициализации:

class X {

static int i = exp; - немедленная инициализация справедлива и для Си шарп и дляДжавы

static { ... }


Одна из самых интересных созможностец – наличие понятие конструктора. Есть понятие специальная функция – про них есть дополнительная семантика, и компилятор знает, когда их вызывать. Наиболее популярная – конструктор, в Си-подобных языках синтаксис – имя функции совпадает с именем класса X().

Статические конструкторы – видно, что это специальная функцияю Когда модуль загружается в память, происходит вызов статических конструкторов (Си шарп-Джава), при этом сверху вниз выполняется инициализация, потом статик-конструкторы. Статический конструктор генерируется. И он обладает неукоторой семантикой.

Стандартная семантика – вызыыаются конеструкторы базовых классов, инициализация (конструкторы) подобъектов, после этого выаолняется тело конструктора. У статических конструторов то же самое – вызовет конструкторы базового класса, выполнены статические инициализаторы членов, и потом тело.


Языки Программирования


01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28


Календарь

чт вт чт вт чт вт чт вт чт вт
Сентябрь
  05 07 12 14 19 21 26 28
Октябрь
  03 05 10 12 17 19 24 26 31
Ноябрь
02 14 16 21 23 28 30
Декабрь
05 07 12 14

Материалы к экзамену
Сравнение языков программирования


Эта статья является конспектом лекции.
Личные инструменты
Разделы