лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей) (1160833), страница 11
Текст из файла (страница 11)
type Stack is private;
-
операции над stack
procedure Push(S:inout Stack; X:integer);
procedure Pop(S:inout Stack; X:out integer);
...
private
type Stack is record
top:integer :=0;
body:array(0..N) of integer;
end record;
end Stacks;
Это значит, что структура типа пользователю должна быть неизвестной. В языке Ада побочный эффект функции запрещён. Появляется дополнительная приватная часть в спецификации пакета — там нужно расписать реализацию этого типа, структуру типа данных.
Можно написать Push(s,1); Pop(s,x);
Нельзя написать S.top:=1;
Компилятор проведет распределение памяти и инициализацию. Однако создатели проиграли в количестве перетрансляции. Допустим, я захочу сменить реализацию типа Stack — сделать в виде списка → надо переписать приватную часть. В приватной части полностью описывается структура и вспомогательные структуры — для этого она и нужна. Недоступна программисту, но доступна компилятору, а использовать эту структуру программист не имеет права.
type Stack is access;
type link is record
next:Stack;
X:integer;
end record;
type Stack is access Link;
или type Stack is new PLINK; ?
Так как у нас поменялась реализация, необходимо перекомпилировать все модули. Полностью абстрактным типом данных он не является — к нему применимы операции присваивания и сравнения. В случае типа данных Stack эти операции безопасны, но это не везде так. Вводятся ограниченные приватные типы данных.
type T is limited private;
Полный интерфейс типа данных Stack должен обязательно иметь Init, Destroy и Copy.
В Обероне Клаус Вирт отказался от принципа РОРИ — определения и реализация находятся в одном месте — слиты в одну точку. Нужно понимать, что принцип разделения хорош для программистов-пользователей, разработчикам удобнее, чтобы они были в одном месте. Поэтому есть возможность генерировать псевдомодуль определения, которым и будет пользоваться программист-пользователь. Заметим, что для современных ЯП это уже тоже является основой.
Глава 6. Определение новых ТД с помощью классов.
класс = ТД
Классы Object, String и многие другие отличаются. Collection — стандартный класс, а Object и String встроены в компилятор, маскируются под классы, являясь стандартными типами данных.
Всё, что принадлежит данному классу, содержит this. В C# можно вводить операторы преобразования для соответствующего класса. В C++ это делается через операцию преобразования, которая является членом класса T1 и приводит к классу T2. Это не всегда удобно, потому что она чужая по отношению к классу T2. В С++ эту проблему решают с помощью друзей класса , в C# это статические функции члены класса — либо Т1, либо Т2. Понятие класса менее универсально, чем понятие модуля, поэтому и возникает столько проблем. В языках C# и Java отсутствует понятие глобальных данных и функций — можно работать только с классами, где особое отношение к статическим членам класса, однако класс такой нельзя наследовать, и выход был найден через понятие статического класса, что гораздо ближе к модулю. Но более универсальный не всегда означает удобный.
п.1. Члены классов.
Class имя [:наследования] {
список_членов_класса
}
- синтаксис С++ подобных языков.
В С#, Java, Delphi если родители напрямую не указаны, то есть тип Object, который является верхушкой иерархии.
type имя = class (наследования)
список_членов_класса
end;
- Delphi
Все эти языки поддерживают принцип РОРИ. В С++ можно описывать функцию внутри самого класса, можно и в другом месте. В Delphi запрещено помещать тело функции в описание или интерфейс. Членами класса могут быть члены-данные, члены-функции и члены-типы, частными случаями последних являются члены-классы.
Члены делятся на статические и нестатические.
Члены-данные:
нестатические члены-данные как обобщение понятия записи, обращение идёт по имени, в С++ есть →.
статические члены-данные — глобальные переменные, обращение по ::
Лекция 22.
Классы.
Члены – данные, функции, типы.
Статические и нестатические члены данных.
К нестатическим членам обращение производится следующим способом:
имя_ОбьектаДанных.имя_нестатического_члена
К статическим: имя_класса::имя_стат_члена.
M.h:
Class X{
Static int a;
};
M.cpp:
Чтобы отвести память под статический объект ‘a’, программист должен указать, где именно он отводит память.
Int X::a = -1; (определение)
(В .h нельзя это пихать, так как он может подключаться в нескольких .cpp файлах)
Члены функции. Статические. Нестатические.
-
Класс – область действия (мы можем ссылаться на соответствующие члены класса без использования соответствующего указателя). Когда говорим о функциях, то есть две области действия: область класса и блок функции (все что между {…}). Еще есть и формальные параметры – место их определения можно считать как еще одну область действия.
-
Неявный параметр
С++, С#, Java -> this
SmallTalk, Delphi -> self
Если убрать указатель из следующего кода:
This->body[this->top++] = x;
То можно получить более читабельный код:
body[top++] = x;
---------
int *pi;
int a;
pi = &a;
int X::stat = -1;
pi = &X::stat; (хранится адрес переменной)
Указатель на статический член класса – это тот же самый указатель, что и на другие глобальные переменные.
class X{
int i;
int j;
};
int X::* p;
p = &x::i; (это не указатель – хранится смещение)
p = &x::j;
Во всех языках допускается размещать константные и неконстантные объекты:
const int i;
Этот член является константой и может быть инициализирован прямо в определении (так как объект целого типа). Но такие члены могут быть только static, для уменьшения содержания лишних данных в объекте класса.
Типы.
class X{
class Y{ (обычно этот класс связан как-то с классом X)
(это обычный вложенный класс, имеет доступ к объектам класса X)
};
};
Например, в STL:
vector{
iterator{
};
};
В Java внутренний класс – нестатический вложенный класс. Статический класс – обычный вложенный класс.
class bankAccount
{
int amount;
class Action{
int k;
void f () {(обработка k и amount)}
}
void Deposit(int sum)
{
Action dep = new Action();
…
}
}
BankAccount X;
(возможно обращение) - BankAccount.Action
a = X.new Action();
C#
static
Нестатический класс тут соответствует статическому классу языка Java и обычного вложенного класса языка C++. Если класс статический, то он должен содержать только статические члены данных и статические функции.
Deplhi
В нем нет понятия вложенных классов и статических функций и процедур.
unit M;
interface
function GLB (a:integer) : integer;
type X = char;
var
i:integer;
constructor ();
procedure t;
implementation
end;
Пункт 2. Инкапсуляция в классах. Управление доступом. Управление видимостью.
C++
public (любая функция имеет доступ, весь мир имеет доступ)
private (члены-класса – да, остальные нет)
protected (функции члены-класса - да, функции члены производных классов – да, мир – нет)
T operator+(const T&, const T&); (более естественная форма – функция должна иметь доступ ко всем объектам, а так как она внешняя, то имеет только к public членам). А мы говорили, что данные лучше делать закрытыми, а делать функции которые возвращают значение или его меняют у соответствующих членов данных.
T T::operator+(const T&);
T& operator+ (const T& t){
T tmp = t1;
return tmp += t2;
}
friend void f(); //нельзя получить дружбу – выдается классом, дружба не наследуется, не транзитивна (друг друга не является другом).
Для Java вводится еще одна категория (члены-класса, функции члены производных классов, мир) – функции из того же пакета.
Public (++++)
Protected (+++-)
пакетный доступ (в С# internal) (свой +, производный +, пакетный +, остальные нет)
private (+---)
Лекция 23.
п.2 Инкапсуляция в классах
Схема разбиения на свой/чужой мир появилась с самого начала — это ключевое понятие ООП, а понятию группировки иначе как на классы Страуструп вначале не уделил достаточно внимания. Языки, в которых есть понятие модуля, естественным образом расширяют группировку имён — это и Delphi, и Java, и C#. В Java есть пакет — с помощью него мы управляем контекстом и одновременно это является единицей дистрибуции — взаимосвязанные классы мы помещаем в один пакет. В С# есть понятие namespace — единица группировки, однако дистрибуция — понятие сборки. На уровне самого языка понятие сборки не возникает, однако оно есть — когда всё собирается сборщиком, оно помещается именно в Assembly и всё, что находится в одной сборке, взаимосвязано между собой. В Java появляется пакетный доступ, а в C# - internal (внутренний), это означает что к помеченным членам имеют доступ все классы из соответствующей сборки. В C# умолчание — private, в Java — пакетный доступ. Получается 4 уровня: свой | наследник | взаимосвязанность | мир (чужой).
public + + + +
protected + + ? -
internal + ? + -
private + - - -
В языке Java protected одновременно является и пакетным, и можно ставить + . В C# protected,означает что доступ только из производных, но в C# есть двойной модификатор protected internal (связка или-или). Он является одновременно и защищенным, но если это правило доступа не приходит, то мы применяем internal. Правила доступа становятся сложнее, но зато понятия друга в этих языках нет. В Delphi есть ключевое слово public, private — смысл тот же, есть «по умолчанию» и есть protected.
Type MyClass=class
объявления по умолчанию
К этим объявлениям все описанные процедуры и функции из данного юнита ведут себя как public, к чужим юнитам — как private. Нет ключевого слова, поэтому они должны перечисляться в начале. Всё, что создаётся интегрированной средой, создаётся именно в поле «по умолчанию».
Абстрактный Тип Данных.
С точки зрения класса, в первом приближении, АТД это класс, в котором публичным являются только процедуры или функции. Мы должны избегать объявлять публичные данные. Есть процедура «рефакторинг», которое изменяет в лучшую сторону код программы, не меняя функциональность. Один из методов — если есть объявленный публичный член данных, надо его сделать либо защищенным, либо приватным. Чтобы сделать по-настоящему тип абстрактным, мы должны ещё позаботиться о смысле операции инициализация, копирование, присваивание. Заметим, что когда мы говорим об ООП, здесь возникает парадокс — фундаментальное понятие ООП пришло из другого типа программирования. Класс является ТД, а абстрактный класс не является АТД, это просто разные понятия, в то же время они связаны. Разновидностью абстрактного класса является понятие интерфейса, вот оно-то и связано с понятием АТД. Интерфейс это просто перечисление операций, которые можно применять. Второе замечание — всё-таки, что же означает protected? Оказывается, что довольно многие воспринимают protected в наивном смысле — если член класса помечен как protected это означает, что из любых функций членов класса осуществляется доступ.
class X{
private:
int i;
void f(){i=0;..}
void g(X& x){x.i=0;}