Языки программирования, 17 лекция (от 31 ноября)
Материал из eSyr's wiki.
(Содержимое страницы заменено на «== From Ebaums Inc to MurkLoar. == We at EbaumsWorld consider you as disgrace of human race. Your faggotry level exceeded any imaginab...») |
(Отмена правки № 1465 участника 131.173.32.97 (обсуждение)) |
||
Строка 1: | Строка 1: | ||
- | == | + | <P STYLE="margin-bottom: 0cm">ЯП</P> |
- | + | <P STYLE="margin-bottom: 0cm"><BR> | |
- | + | </P> | |
- | + | <P STYLE="margin-bottom: 0cm">Лектор сказал: Лекций 7, 9 ноября не | |
+ | будет</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">п3. Определение типов классами.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Тип данных = Множество операций + | ||
+ | множество значений.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">До этого определяли структуры, | ||
+ | определяли операции, и связывали это модулем. Но при этом ощущается | ||
+ | некоторая избыточность. Например, описываем пакет Стеки, определяем | ||
+ | там тип Стек. Уже дублирование имён. Ображение: Стеки.Стек. Класс – | ||
+ | дуален – и структура, и обёртка. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Класс по определению предназначен | ||
+ | исключительно для ТД.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Каким обращом множество операций | ||
+ | связывается со множеством значений.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Основные языки: Си++, Си шарп, Джава, | ||
+ | Дельфи.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Дельфи – экле..тичный язык, там | ||
+ | можно работать и в классич парадигме, и в ООП.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">По синтаксису Дельфи далеко отстоит от | ||
+ | святой троицы.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Члены класса:</P> | ||
+ | <OL> | ||
+ | <LI><P STYLE="margin-bottom: 0cm">Члены-данные определяют структуру, | ||
+ | множество значени</P> | ||
+ | <LI><P STYLE="margin-bottom: 0cm">Функции (процедуры) – | ||
+ | оперделяются множество операций</P> | ||
+ | </OL> | ||
+ | <P STYLE="margin-bottom: 0cm">Синтаксис:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Сишный</P> | ||
+ | <P STYLE="margin-bottom: 0cm">class Name {</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> описание членов</P> | ||
+ | <P STYLE="margin-bottom: 0cm">};</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Синтаксически похоже на описание | ||
+ | структуры, класс без процедур – та же структура.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Описание членов:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">ТД – описание переменных</P> | ||
+ | <P STYLE="margin-bottom: 0cm">ЧФ – прототип</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Мы рассм класс как ТД.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Примеров несть тому числа.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Класс является и модулем. Внутри класса | ||
+ | может описано статические члены.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Внутри класса могут быть влооженнвые | ||
+ | опеределния типов. - по аналогии с модулем.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Отличия других ЯП от С++ - в Си шарп то | ||
+ | же самое, только кроме классов могут быть перечислимые типы. То же | ||
+ | самое в Джаве – опеределение членов-функций, вложенных классов.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">В чём отличия Си шарп Джавы, от С++:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Кроме прототипов функцйий могут быть | ||
+ | опеределния классов.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Class X {</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> vois f()int i;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">void g() {}</P> | ||
+ | <P STYLE="margin-bottom: 0cm">}</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Это некий синтаксический сахар. Здесь | ||
+ | идёт речь об inline – функциях</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Inline – функции – функции, | ||
+ | тело которых может вставлять в месте вызова.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Похожи на макросы. Отличие от макросов | ||
+ | - | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Типичный пример inline-функции – | ||
+ | максимум:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">inline T Max(T x, T y) {return x<y ? | ||
+ | y : x; }</P> | ||
+ | <P STYLE="margin-bottom: 0cm">и вместо max(a,b) будет подставлен a<b | ||
+ | ? a : b;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Очень похоже на соотв define. В чём | ||
+ | основная разница - | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Если поытаться описать такой макрос:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">#define max(x,y) ((x)<(y)) ? (y) : | ||
+ | (x);</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Скобки нужны, чтобы вместо x и y</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Внешне похоже, Но инлайн | ||
+ | предпочтительнее. В inline типы должны совпадать. При вызове инлайн | ||
+ | будет сделано преобразование типов, если нужно, или выдана ошибка в | ||
+ | терминах вызова. Дефайн при ошибке будет ругаться в терминах | ||
+ | макроподжстановки.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Инлайн функции хороши для современных | ||
+ | суперскалярны процессоров. Срывает конвейер операция перехода, а | ||
+ | любой вызов функции – нелокальный переход, и это может серьёзно | ||
+ | уронить производительность. Inline – программист рекоммендует | ||
+ | компилятору провести подстановку тела функции. В случае виртуальных | ||
+ | функции инлайнятся функции очень редко, ибо во время компиляции можно | ||
+ | не знать, что делать. Ещё не инлайнятся циклы, ибо там есть переходы. | ||
+ | И сущ быстродействия это не даст. Зачем это нужно было Страуструпу? | ||
+ | Это было и в Си, но в каждом отдельном компиляторе для этого были | ||
+ | свои прагмы, и Страуструпу не нравилось, что это не стандартизовано. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">А зачем Страуструпу понадобилось | ||
+ | повышать быстродействие подобного рода функции? Это связано с ООП, в | ||
+ | частности, с появлением geyters-setters. Например, если мы хотим | ||
+ | сделать переменную read-only, то делаем только getter: T getA() | ||
+ | {return a; } И его хорошо бы инлайнить.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Впервые понятие класса появилось в | ||
+ | Симуле, и С. Обратил внимание на её неэффективность, так как там | ||
+ | подобные функции не инлайнились. И вместо того, чтобы решать проблему | ||
+ | в частности, он решил её глобально.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">И был введен некоторый синтаксический | ||
+ | сахар. Чтобы вместо того, чтобы писать</P> | ||
+ | <P STYLE="margin-bottom: 0cm">class X {</P> | ||
+ | <P STYLE="margin-bottom: 0cm">T GetA();</P> | ||
+ | <P STYLE="margin-bottom: 0cm">};</P> | ||
+ | <P STYLE="margin-bottom: 0cm">innline T X::GetA() { return a; }</P> | ||
+ | <P STYLE="margin-bottom: 0cm">молжно просто определить функцию прямо | ||
+ | внутри класса, и тогда она будет считаться inline. Это было сделано | ||
+ | для того... Есть физическое разделение интерфейса и реализации | ||
+ | (хедеры и цппшники). И опеределние класса помещается в хедеры, а | ||
+ | определение фукнции в реализацию. В С++ есть принцип РОРИ. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Но для inline это не катит, ибо | ||
+ | компилятор должен знать оперелеление inline-функции, поэтому их надо | ||
+ | помещать в хедер. Поэтому для инлайн-функции С. Разрешил такую | ||
+ | возможность.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">В С++ реализовано РОРИ, а в Си шарп и | ||
+ | Джаве – нет. Там если описали функцию-член, то обязаны указать | ||
+ | её реализацию, только если это не чисто виртуальная функция. То, что | ||
+ | это неудобно с точки зрения чтения, это да. Во время отладки это | ||
+ | удобно. А во время чтения средства систем разработки позволяют | ||
+ | вырезать интерфейс и смотреть на него. Сам язык не нагружается соотв | ||
+ | понятиями. Это характерно для совр ЯП, которые проектируются с учётом | ||
+ | того, что будут погружены в соотв среду.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Дельфи (идейно ближе всего к С++):</P> | ||
+ | <P STYLE="margin-bottom: 0cm">type X=class</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> объявление членов;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> i:integer;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> procedure P;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> function f(k:integer):integer;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">end;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Тут чётко РОРИ. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">В объявлении (спецификации) класса | ||
+ | только переменные и прототипы. Это в интерфейсной части. Правда, | ||
+ | может быть и в реализации, но это уже внутренние классы, только для | ||
+ | реализации.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Implementation</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> procedure X.P; begin ... end;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> function X.F ... begin ... end;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Небольшие синтаксические отличия:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Мы пока не говорили, каким образом | ||
+ | скрывается реализация. Во всех этих языках есть возможность | ||
+ | возможность экспорта членов, для этого используется модификатор | ||
+ | publiс, действие которого распротраняется на вс объявления после | ||
+ | него. В Джаве-Си шарп спецификатор перед каждым объявлением. Различия | ||
+ | в умолчаниях.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Си++: для классов по умолч прайвейт, в | ||
+ | структурах – private</P> | ||
+ | <P STYLE="margin-bottom: 0cm">C#: для классов и структур – | ||
+ | private</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Java, Delphi: в Джаве есть пакетный, в | ||
+ | Дельфи – для юнита – там доступ в перделах пакета-юнита | ||
+ | по умолчанию. Сами классы могут объединяться в модули – и это | ||
+ | модульный доступ.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Про доступ мы пока больше гововрить не | ||
+ | буду.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Множество операций+множество значений</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Множество операций – public | ||
+ | функции-члены</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Множество знач – Члены-данны, | ||
+ | хорошим тоном счиатеется их скрытие.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Сосредосточимся неа деталях:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Синтаксис обращения.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Члены локализоканы внутри класса. Есть | ||
+ | Класс Х, есть член х. Синтаксис доступа – Х.х. Этот синтаксис | ||
+ | взят из синтаксиса записи. Этот синтаксис просто обобщён на класс. ФЧ | ||
+ | отличаются тем, что им неявно передаётся ссылка/указатель на объект | ||
+ | класса. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Claas X {</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> void f() {...}</P> | ||
+ | <P STYLE="margin-bottom: 0cm">};</P> | ||
+ | <P STYLE="margin-bottom: 0cm">E а будет неявный параметр – | ||
+ | ссылка на объект. При вызове x.f(); юудет передана ссылка на х. И в | ||
+ | теле функции будет определёна переменная:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">C++ - указатель this</P> | ||
+ | <P STYLE="margin-bottom: 0cm">С# - ссылка this</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Java, Delphi – сслка Self</P> | ||
+ | <P STYLE="margin-bottom: 0cm">почему Страуструп не использовал self, | ||
+ | хотя оно использовалось в Smalltalk, который явл оперделяющим с тз | ||
+ | терминологии: Вилдимо, у него что-то там конфликтовало. В Си шарп | ||
+ | было взято по принципу чтобы не было похоже на Джаву.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">К члену класса обращаемся снаружи | ||
+ | только через имя объекта класса. Изнутри – можно через | ||
+ | this/self, а можно просто через имя члена. Класс образует свою зону | ||
+ | видимости – внутри членов класса можем обращаться к другим | ||
+ | просто по имени. Класс образует свою область видимости. И если есть | ||
+ | глобальное описание одноимённых переменных, то он и перекрываются. В | ||
+ | Джаве и Си# глобальные переменны еотстусттвуют, но там есть вложенные | ||
+ | пространства имён и та же проблема. Но this/self нужен, когда, | ||
+ | например, класс передаёт ссылку на себя. В Си шарп this испоьлзуется | ||
+ | в документации, когда</P> | ||
+ | <P STYLE="margin-bottom: 0cm">есть i, j как ячлен класса</P> | ||
+ | <P STYLE="margin-bottom: 0cm">void (int i) {int i; - нельзя, ибо | ||
+ | имена формальных параметры как бы локализованы внутри тела функции | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">}</P> | ||
+ | <P STYLE="margin-bottom: 0cm">тело функции – вложенная по | ||
+ | отношению класса область видимости, а что делать с объявлением в | ||
+ | классе? Везде закрывается, кроме Delphi, в Delphi просто нельзя так | ||
+ | делать, и это правильно. В остальных языках к члену может обратиться | ||
+ | через this/ | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Попробуе мпровести параллель между | ||
+ | определением в омдульных языках и в ОО.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Ада (Модула-2)</P> | ||
+ | <P STYLE="margin-bottom: 0cm">package M is</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> type T is ///;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> procedure P(X:T);</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> procedure P;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">X: Y;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">end M;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">package body M is</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> procedure G(x:T) is ... end G;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Z : Y;</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">ОО:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">class T {</P> | ||
+ | <P STYLE="margin-bottom: 0cm">public:</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> void P();</P> | ||
+ | <P STYLE="margin-bottom: 0cm">а что делать с P без параметров? Он | ||
+ | может обращаться к X, Z. Аналог – статические члены, им не | ||
+ | передаётся никакой неявный параметр.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> static void PP();</P> | ||
+ | <P STYLE="margin-bottom: 0cm">};</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Как обращаться к PP? А как к P без | ||
+ | параметров в Аду? Через имя модуля – T.P, в ОО – через | ||
+ | имя класса – T.PP(). Но в С++, и за это язык ругают, можно ещё | ||
+ | через объект класса – t.PP(), а через имя класса – | ||
+ | T::PP().</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Точка и два двоеточия не перегружаются.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Public:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Y X;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">private</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> void G();</P> | ||
+ | <P STYLE="margin-bottom: 0cm"> static Y Z;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">};</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Если брать модульный язык, где мы | ||
+ | описываем отдельно стоящие параметры и переменные, то в ОО мы и | ||
+ | описываем статически. Статические члены являются глобальными, но они | ||
+ | локализованы в имени типа, и до них мы добираемся через имя класса. | ||
+ | Класс – полный аналог модуля.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">В Си шарп и Джаве употр статич членов | ||
+ | очень часто, ибо это аналог глобальных членов и функции. У | ||
+ | программиста на С++ есть возможность вынести подобные функции на | ||
+ | глобаьлный уровень, а в Си шарп и Джаве эти понятия отсутствуют. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Зачем нужны статические данные:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Корнстанта. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Class Stack {</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Int top;</P> | ||
+ | <P STYLE="margin-bottom: 0cm">T body[N];</P> | ||
+ | <P STYLE="margin-bottom: 0cm">const int N; - глупость, потому что | ||
+ | будет для каждого своя, надо делать статик.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">}</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Статические функции нужны для того, | ||
+ | чтобы работать со статическими данными.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Статические члены существуют вне | ||
+ | зависимости от объектов класса. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Пример: пусть мы хотим располагать | ||
+ | объекты класса только в динамической памяти (актуально только для | ||
+ | С++). Можно ли это обеспечить? Легко. Оставить публичной одну функцию | ||
+ | – static T * MakeT() {return new T(); } Если сделать все | ||
+ | конструкторы приватными, то единственый споособ создать объект класса | ||
+ | – вызвать эту функцию.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Статические члены есть во всех | ||
+ | ОО-языках, так как некоторые ыещи можно проможедировать только ими.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Константы – часть объекта или | ||
+ | класса?</P> | ||
+ | <P STYLE="margin-bottom: 0cm">В Смаллтоке есть пноятие класс, и есть | ||
+ | понятие экземпляр. У класса есть методы, есть переменные. Переменные | ||
+ | в См. Делились на переменные экземпляра, которые были для каждого | ||
+ | экземпляра свои и переменнаяе класса, коотрые размещались в | ||
+ | единственном числе. Осталось обобщить на функции – к | ||
+ | нестатическим передаётся ссылка на объект.</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Любая ли константа должна быть | ||
+ | переменной класса? Не все. Одни принадлежат экземпляру, другие всему | ||
+ | классу. В Джаве для констант экз есть final. Где они | ||
+ | инициализируются? Есть кноструктор, который вызывается при создании | ||
+ | класса - и там инициализируются константы, только один раз. В Си | ||
+ | шарп – специали=ьный синтаксис конструктора – | ||
+ | инициализирующая часть.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Где инициализируются статические данные | ||
+ | – единственная возможность инициализировать статические данные | ||
+ | – в cpp. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Class X {</P> | ||
+ | <P STYLE="margin-bottom: 0cm">static const int i; - обязаны | ||
+ | инициализировать. В теле класса инициализацию сделать нельзя. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">};</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Вопрос размещения – из-за | ||
+ | раздельной трансляции компилятор не знает, где размещается | ||
+ | статические данные. Ибо он не знает, откуда взялся класс – из | ||
+ | инклюда или из текста. Поэтому, если описана статическая переменная, | ||
+ | программист должен указать её размещение. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Поэтому в цппшнике надо написать int | ||
+ | X::i = 0; таким образом явно указали размещение. А если в хедере – | ||
+ | то досявые-виндовые редакторы связей будут ругаться? Ld – | ||
+ | выдадет ворнинг. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Аналогом размещения функции члена для | ||
+ | фугкции – оперделение. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Пока мы не вызываем функции-не | ||
+ | обращаемся к переменным, ошибки нет, как только обратимся – | ||
+ | ошибку найдёт редактор связей.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Где будут инициализироваться | ||
+ | статические члены –</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">в Си шарп можно гибко управлять | ||
+ | порядком загрузкой модулей, поэтому можно предсказать порядок, в | ||
+ | котором будут выполняться конструкторы, посему в инициализации можно | ||
+ | писать что-то содержательное.</P> | ||
+ | <P STYLE="margin-bottom: 0cm">В Джаве есть статический блок | ||
+ | инициализации:</P> | ||
+ | <P STYLE="margin-bottom: 0cm">class X {</P> | ||
+ | <P STYLE="margin-bottom: 0cm">static int i = exp; - немедленная | ||
+ | инициализация справедлива и для Си шарп и дляДжавы</P> | ||
+ | <P STYLE="margin-bottom: 0cm">static { ... }</P> | ||
+ | <P STYLE="margin-bottom: 0cm"><BR> | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Одна из самых интересных созможностец – | ||
+ | наличие понятие конструктора. Есть понятие специальная функция – | ||
+ | про них есть дополнительная семантика, и компилятор знает, когда их | ||
+ | вызывать. Наиболее популярная – конструктор, в Си-подобных | ||
+ | языках синтаксис – имя функции совпадает с именем класса X().</P> | ||
+ | <P STYLE="margin-bottom: 0cm">Статические конструкторы – видно, | ||
+ | что это специальная функцияю Когда модуль загружается в память, | ||
+ | происходит вызов статических конструкторов (Си шарп-Джава), при этом | ||
+ | сверху вниз выполняется инициализация, потом статик-конструкторы. | ||
+ | Статический конструктор генерируется. И он обладает неукоторой | ||
+ | семантикой. | ||
+ | </P> | ||
+ | <P STYLE="margin-bottom: 0cm">Стандартная семантика – | ||
+ | вызыыаются конеструкторы базовых классов, инициализация | ||
+ | (конструкторы) подобъектов, после этого выаолняется тело | ||
+ | конструктора. У статических конструторов то же самое – вызовет | ||
+ | конструкторы базового класса, выполнены статические инициализаторы | ||
+ | членов, и потом тело.</P> |
Версия 17:19, 3 февраля 2008
ЯП
Лектор сказал: Лекций 7, 9 ноября не будет
п3. Определение типов классами.
Тип данных = Множество операций + множество значений.
До этого определяли структуры, определяли операции, и связывали это модулем. Но при этом ощущается некоторая избыточность. Например, описываем пакет Стеки, определяем там тип Стек. Уже дублирование имён. Ображение: Стеки.Стек. Класс – дуален – и структура, и обёртка.
Класс по определению предназначен исключительно для ТД.
Каким обращом множество операций связывается со множеством значений.
Основные языки: Си++, Си шарп, Джава, Дельфи.
Дельфи – экле..тичный язык, там можно работать и в классич парадигме, и в ООП.
По синтаксису Дельфи далеко отстоит от святой троицы.
Члены класса:
Члены-данные определяют структуру, множество значени
Функции (процедуры) – оперделяются множество операций
Синтаксис:
Сишный
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().
Статические конструкторы – видно, что это специальная функцияю Когда модуль загружается в память, происходит вызов статических конструкторов (Си шарп-Джава), при этом сверху вниз выполняется инициализация, потом статик-конструкторы. Статический конструктор генерируется. И он обладает неукоторой семантикой.
Стандартная семантика – вызыыаются конеструкторы базовых классов, инициализация (конструкторы) подобъектов, после этого выаолняется тело конструктора. У статических конструторов то же самое – вызовет конструкторы базового класса, выполнены статические инициализаторы членов, и потом тело.