типов данных: модули и классы.




Контейнер - средство создания новых типов данных. В некоторых языках контейнером выступают модули (Модула – 2, Ада, С#(namespace), Java(package)), в других - классы, структуры(С++, С). Delphi – гибрид, cодержащий как модули, так и классы

Понятие логического модуля. Использование модулей для определения

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

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

Один из важнейших ресурсов Ада – определение новых типов данных.

DEFINITION MODULE STACKS;

TYPE stack=RECORD

B: Array[0..N] of T;

TOP: [0..N];

END; //конец записи

PROCEDURE PUSH(VAR S: STACK; X: T);

PROCEDURE POP(VAR S:STACK; VAR X: T);

Пусть внутри модуля находятся процедуры

IsEmpty

IsFull

PROCEDURE Init(VAR S: stack);//(инициализация стека)

и переменная

VAR done: Boolean;//сообщающая последний результат операции над стеком

END STACKS;

В модуле определений (модуле реализации) соответствующие процедуры должны быть полностью описаны.

Переменная Done говорит нам о том, выполнилась ли какая-то операция над стеком или нет. В чем недостаток? Дело в том, что мы вынуждены экспортировать переменную на полный доступ – а это не есть хорошо. Выход из данной ситуации:

PROCEDURE Init*(VAR S:stack);

VAR DONE * BOOLEAN

PROCEDURE GetError(): Boolean;

Но тогда переменная DONE может быть испорчена. Функция Get Error, очевидно, должна возвращать значение переменной DONE. Но вызов функции – это нелокальный переход, и с точки зрения современной архитектуры он плох.

Импорт и экспорт имен. Видимость имен: непосредственная и

потенциальная. Управление видимостью. Области видимости и пространства

имен.

Модуль – набор взаимосвязанных ресурсов, которые служат для использования других модулях.

Видимость подразумевает возможность обращения с объектом, то есть, к примеру, знаем его имя и тип. непосредственно видимы в глобальном пространстве имен только модули. Имена полей из модуля видимы только потенциально.

Модуль-ресурсы = ОД, ТД, П/П.

Интерфейс = определение ресурсов + реализация.

Пространство имен в ООЯП заменено на модули.

М-2:

1)Главный модуль //Один

2)Библиотечный модуль

3)Локальный модуль //параллельное программирование.

Глобальное пространство имен => видимость для всех

Непосредственная видимость: имя использует ASIS.

Потенциальная видимость: имя с уточнением.

DEFINITION MODULE имя;

Определение ресурсов.

END имя;

IMPLEMENTATION MODULE имя;

Реализация всех процедур/функций из DEF + дополнительные ресурсы.

END имя;

TP, DELPHI:

init имя;

interface

implementation

End имя;

Все имена экспортируются в глобальное пространство имен ПОТЕНЦИАЛЬНО.

IMPORT M; //Первые в модуле => клиент.

IMPORT InOut; //Видимы потенциально.

InOut.WriteString(“counter”);

InOut.WriteInt(out);

InOut.Writeln;

FROM InOut Import Writeln, WriteInt.

Видны непосредственно!

uses список имен; //видимы непосредственно.

Оберон: оставлен только библиотечный модуль.

MODULE M;

ENDM;

* - экспорт имени.

//имя *

MODULE ST

TYPE STACK* = …

PROCEDUR PUSH* (VAR S: STACK); … PROCEDURE P…

END. => псевдомодуль.

DEFINITION ST;

TYPE STACK = …

PROCEDURE PUSH = …

END st.

=> IMPORT список_имен_модулей.//В Обероне только так.

=> ST.STACK //интерфейс.

Или FROM ST IMPORT R; => R//реализация.

Ада: пакет(спецификация, тело)

Спецификация:

package M is

Определение типов, переменных, констант, заголовков процедур

end M;

Тело:

package body M is

Реализация всех процедур и функций

end M;

package STANDART; //пакет стандартных имен.

Пользовательские пакеты встраиваются в STANDART.

Для подключения пакета используется оператор with: with <Имя пакета>. В случаях,

когда использование полной точечной нотации для доступа к ресурсам пакета обременительно, можно использовать инструкцию спецификатора использования контекста use. Это позволяет обращаться к ресурсам которые предоставляет данный пакет без использования полной точечной нотации, так, как будто они описаны непосредственно в этом же коде. Для обращения к функции из пакета используется оператор «.»: <имя пакета>.<имя функции>

Любой пакет можно вложить в другой.

STANDART

package M1 is

package M1.2 is

end M1.2; end M1;

package M2 is

package M2.1 is

package M2.2 is

end M2.2; end M2.1; end M2;

Тела вкладываются также, как и спецификации!

Более близкое описание скрывает менее близкое описание одинаковых имен. Как только возникает конфликт имен, имя становится видимым только через уточняющую форму, и непосредственная видимость исчезает.

Область действия любого имени начинается с его определения. Имена пакетов видимы непосредственно в той области, где они были объявлены.

В пакете М1 видимость обьектов из М12 потенциальная. А из внутренней области видимости мы имеем непосредственный доступ во все объемлющие структуры.

Неявный импорт: вместе с одним именем неявно импортируется другое.

переименование:

a renames b; //a теперь описано уже тут

Можно также переименовать операцию «+»:

function “+”(X, Y: vectors.Vector) return vector renames vectors;

Ада, Модула - 2 предоставляет программисту возможность осуществлять переименования. Переименование не создает нового пространства для данных. Оно просто создает новое имя для уже присутствующей сущности.

В С# using

В Java import имя_пакета;//ошибка-нельзя экспортировать пакет в Java!

import имя_класса; //Ok!

import имя_пакета .*;//Ok! – мы импортируем все имена из пакета

В Pascal, Delphi uses

В Оберон 2 переменную можно экспортировать только на чтение.

1.Сравните между собой конструкции "uses" в языке Delphi и "use" в

языке Ада (для чего применяются, сходства, отличия).

Конструкция языка Delphi «uses список_имен_модулей» служит для импорта всех имен, объявленных в интерфейсе модулей из списка. При этом импортированные имена становятся непосредственно видимыми (если нет конфликтов с именами из других модулей).

Конструкция языка Ада «use список_имен_пакетов» обеспечивает непосредственную видимость имен из спецификаций пакетов из списка (если нет конфликтов).

Сходство: конструкции обеспечивают непосредственную видимость имен из интерфейсов (спецификаций) при отсутствии конфликтов.

Различие: в Delphi «uses» импортирует имена из интерфейсов библиотечных модулей, в Аде импорт имен обеспечивается другими конструкциями, а «use» служит только для разрешения непосредственной видимости.

Модульность и технология программирования: проектирование

«сверху-вниз» и «снизу-вверх».

Существует два подхода к проектированию древовидной иерархии модулей:

1) сверху вниз(top-down- подход) - сначала проектируется модуль верхнего уровня, а затем мы опускаемся до более низких уровней.

2) снизу вверх(bottom-up- подход) – сначала проектируются самые нижние модули – они в данной иерархии инкапсулированы и ничего не знают о вышестоящих модулях. Подставляют виртуальные сервисы универсального характера, которые нужны всем.(P. S. Современные объектно-ориентированные системы в данном случае предлагают нам сетевые структуры.) на их базе строятся сервисные модули более высоких уровней – и так далее, пока не дойдем до единого главного модуля всей системы. Недостаток такого подхода: мы никогда точно не знаем, что нам понадобится в будущем.

Понятие класса. Класс как тип данных. Члены класса: функции,

данные. Статические и нестатические члены. Члены - вложенные классы.

Принципиальное отличие класса от модуля заключается в том, что класс – это тип данных, а модуль нет. Но во многих вещах они похожи.

Тип Данных = Структура Данных + Множество Операций над этими данными

В C#, Java всё является классами или находится в классах в качестве статических челнов.

синтаксис в C++.Java,C#:

class Name

{

….

Определение членов класса

…..

}

В Си++ допускается вынесение определений, т.е. В Си++ можно члены класса лишь объявлять. В Java, C# все определения должны быть внутри класса

Java,C#,

T x;

x = new T(«параметры конструктора»);

В первой строчке определяется ссылка на объект (выделяется память для хранения ссылки), место в динамической памяти под объект не отводится. Во второй непосредственно отводится место в динамической памяти («куче») для объекта и адрес

присваивается ссылке на объект.

C++.

T x;

T x(«параметры конструктора»);

T x = T(«параметры конструктора»);

В этих определениях выделяется место не под ссылку на объект, а под сам объект (не в динамической памяти). Чтобы выделить место в динамической памяти, нужно использовать операцию «new»

синтаксис в Delphi:

type T =

class (наследование)

обявление членов класса

end;

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

• Члены-данные

• Члены-типв

• Члены-функции (методы)

Чем члены-функции отличаются от обычных функций?

Такой функции при вызове всегда передаётся указатель «this»(в Delphi «self») на объект в памяти, от имени которого вызывается функция.

Dephpi

x: T

x – ссылка, её ещё надо проинициализировать.

В Delphi формальные параметры функций-членов находятся в той же области видимости, что и все остальные члены класса и, следовательно, не могут с ними совпадать.

В Delphi членов-типов нет.

Статические члены

SmallTank class variable

instance variable

class variable – члены-данные класса, которые принадлежат всем экземплярам класса.

instance variable – принадлежат экземплярам класса, у экземпляра своя instance variable.

С точки зрении Си++ статические члены классов отличаются от глобальных только областью видимости.

class T

{

….

static int x;

static void f();

……

}

T t;

t.x;//операция доступа

T::x//операция разрешения видимости

t.f();//операция доступа

//или, что тоже самое

T::f() //операция разрешения видимости

Более того, статическим члены данные могут существовать даже тогда, когда нет ни одного экземпляра класса.

Видимость статических членов потенциальная и снять ее, в отличие от модулей, нельзя.

В C#,Java,Delphi обращение к статическим членам происходит только через тип класса.

В статических функциях нет ths/self => в них нельзя использовать нестатические члены класса, т.к. по умолчанию все обращения к нестатическим членам идут через указатель ths/self

В C#, Java статические члены используются намного чаще, чем в Си++, Delphi, т.к. в C#, Java нет глобальных функций и переменных:

public class Name

{

….

public static void Main(string [] args)

….

}

Вложенные типы данных (классы)

class Outer

{

….

class Inner //Inner – обычный класс, но с ограниченной областью

{ //видимости

…..

};

….

};

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

Все классы являются статическими членами. Ко всем именам правила прав доступа применяются одинаково, т.е. специфика имени не участвует в разрешении прав доступа.

C#

В C# имеется понятие «статического класса»

В C# статический класс – это особый вид класса, внутри которого есть только статические члены-функции и только статические члены-данные.

static class Mod

{

public static void f () { ….;}

public static int i;

}

Статический класс – служит контейнером для статических членов. От статических классов нельзя наследовать. Статические члены – это переменные, которые принадлежат классу целиком.

Нельзя создавать объекты статических классов. Статические классы подчёркивают их чисто модульную природу.

Без «static» - обычный класс в понятии Си++. На вложенность классов не влияет.

Статические члены – набор ресурсов, независимых от какого-либо экземпляра.

Java

Статический импорт – импорт всех статических членов класса. Часто применяется к математическим функциям.

Статические классы в Java:

public class Outer

{

….

public static class Inner //Тоже самое, что и в C#, C++ без «static»

{

…..

};

….

};

Это сделано для того, что доступ к Inner был через Outer

Декларируются внутри основного класса и обозначаются ключевым словом static. Не имеют доступа к членам внешнего класса за исключением статических. Может содержать статические поля, методы и классы, в отличие от других типов внутренних классов в языке Java.

Внутренние классы в Java: без «static»

Декларируются внутри основного класса. В отличие от статических внутренних классов, имеют доступ к членам внешнего класса, например «Outer.this». Не могут содержать определение (но могут наследовать) статических полей, методов и классов (кроме констант).

Понятие специальных функций-членов. Проблема инициализации

объектов и способы ее решения. Конструкторы, деструкторы, операторы

Using и try-finally.

Функции-члены, обладающие семантикой обычных функций-членов, о которых компилятор имеет дополнительную информацию.

Конструктор – порождение, инициализация

Деструктор – уничтожение (В Java и C# не деструкторов, вместо это можно сделать собственный метод Destroy())

У конструктора нет возвращаемого значения.

Т.к. все объекты в C# и Java и Delphi размещаются в динамической памяти, то в этих языках обязательна операция явного размещения объектов:

X = new X(); // В Си++ X * a = new X;

Синтаксис конструкторов и деструкторов:

C++. C#, Java

class X

{

X(«параметры»);// В С# и Java обязательно определение тела

}

 

Delphi

type X = class

constructor Create; // Имя конструктора произвольное

destructor Destroy; // имя деструктора произвольное

end;

…..

a:X;

….

a:= X.Create;

В C++, C#, Java конструкторы не наследуются, но могут автоматически генерироваться компилятором по определённым правилам.

Классификация конструкторов:

1.Конструктор умолчания X();

2.Конструктор копирования X(X &); X(const X &);

3.Конструктор преобразования X(T); X(T &); X(const T &);

В классах из простанства имён System платформы.NetFramework не определены конструкторы копирования. В С++ автоматически могут генерироваться конструктор умолчания и конструктор копирования

Вместо этого, если это предусмотрено проектировщиками, имеется метод clone();

Java, C#, D – в базовом типе object есть конструктор Create и деструктор Destroy. Соответственно здесь ничего создавать не надо, они уже есть и наследуются (если).

Конструктор умолчания

X a; - подразумевается вызов конструктора по умолчанию (неявно).

X a(); - нельзя, т.к. это прототип функции.

X* px = new X; - нельзя в Java и С#, в С++ - можно.

X* px = new X(); //В C# и Java можно только так

Java, C#: Понятие КУ остается.

Есть понятие инициализация объектов:

class X

{

Z z = new Z(); // Z z; - значение неопределенно.

Int I = 0; //простые инициализации можно выполнять непосредственно в коде самого класса

}

Вызов конструктора базового класса в Java может быть только первым оператором тела конструктора. Если первый оператор отличен от вызова super, то компилятор автоматически вставляет super();//вызов конструктора умолчания базового класса.

Пример на Java:

class A

{

public A(int I) { … }

...

}

class B extends A

{

public B() { super(0); … }

...

}

Пример на C#:

class A

{

public A(int I) { … }

...

}

class B: A

{

public B(): base(0) { … }

...

}

Существует статический конструктор, который вызывается 1 раз до первого использования и до первого обращения к любым членам класса. (С#)

static X() {…………}; //полная форма статического конструктора по умолчанию в языке C#

Java

static{…………….}; //аналог статического конструктора в Jav

M-2, Ада:

Init(); – явная инициализация.

Destroy();

Для определения собственного конструктора и деструктора достаточно было переопределить эти функции.

Конструктор копирования

С++

void f(X x); // передача фактического параметра функции по значению

X f() {return X();}

X a = b; //Вызов КК. ~ X a(b); «Инициализация присваиванием»

(1) X(X&);

(2) operator=(X&)

Если мы не копируем объект, то конструкции (1) и (2) не нужны. Описав прототип этих функций, но не определив их, мы запрещаем копировать объект данного класса. Когда отсутствует и прототип, и объявление, конструктор копирования генерируется автоматически.

С#: object.

В классе Object(общего предка для всех классов), есть защищенный метод MemberwiseClone, возвращающий копию объекта.

По умолчанию копировать нельзя, но в произвольном классе можно самим переопределить.

ICLoneable ￿ Clone();

Java:

В этой точки зрения наиболее адекватно проблема решена в Javа. Там существует 4 уровня поддержки копирования.

Интерфейс-маркер – по определению пустой интерфейс(не содержит членов).

Интерфейс – это просто набор методов. Он определяет некий контракт, говорящий о том, что если класс поддерживает некий интерфейс, он должен реализовывать определенный набор методов. А если интерфейс пустой, то все его члены-методы «зашиты» в компилятор.

Интерфейс называется сloneable, когда он пустой.

В Java был введен пустой интерфейс cloneable, содержащий метод Clone(), осуществляющий побитовое поверхностное копирование.

protected object Clone();

Уровни поддержек:

· Полная поддержка копирования – возможность явной реализации. Класс X реализует интерфейс Cloneable:

Сlass X: Cloneable{

//Тут мы должны написать:

public X Clone();

//Допускается также:

public Object Clone();

Метод Clone() может использовать любые члены класса(и приватные тоже.)

};

· Возможна и другая ситуация: полный запрет копированияя: при попытке скопировать объект выбрасываем исключение. Подменяем соответсвующий защищенный метод clone():

class X{

protected Object Clone(){ throw CloneNotSupportedException; }

………………………………….

};

· Условная поддержка: элементы, которые копируются, могут быть под полным запретом.

Пример: коллекция умеет себя копировать, а элементы, из которых она состоит – нет.

class X: Cloneable{

public X Clone throwing CloneNotSupportedException

{

//Для каждого элемента коллекции вызывается метод Clone();

};

…………………………………………………..

};

· Еще одна ситуация – когда мы не наследуем метод Clone()

Метод копирования нельзя переопределять.

D: inherited Create; //inherited – вызов соответствующего конструктора.

Деструктор – функция, которая вызывается автоматически, когда уничтожается объект.

С++, D, C# - ОО модель.

C++

delete p;

Отличие С++ от Delphi – в нем происходит автоматический вызов деструктора.

Общая проблема - в процессе функционирования объекты получают некий ресурс.

В С++ и Delphi мы всегда контролируем, когда ресурсы освобождаются.

Специальные функции в Delphi:

X:=I.Create();

X.Create();

X.Free

C#, Java – Автоматическая сборка мусора.

При динамической сборке мусора сложно определить, когда объект больше не используется.

Image.FromFile(fname);

Image.SaveToFile(fname);

Освободить файл можно, когда мы указываем, что с ним не буде работать.

Т.е. сборка мусора здесь не поможет.

=> C#, Java:

try

{

} finally {…}

D:

try

{

} finally

End

Такая вещь, как finally, очень важна. Она будет выполнена независимо от того, как кончился блок.(Это необходимо, так как в C# и в Delphi нету вызова конструктора по умолчанию в конце блока)

IDispose - общий интерфейс освобождения памяти. (C#) Данный метод вызывает финализатор объекта и ставит его в очередь на уничтожение, обеспечивая выполнение деструктора

Dispose();

try { im = … } finally {im.Dispose;}

Вводится специальная конструкция:

using(инициализатор) //инициализатор – T x=expr; x=expr;

блок

~

try {инициализатор} finally {x.Dispose;}

С#, Java: object

Учитывая то, что в Java есть сборщик мусора, там не существует деструктора. В классе Object существует защищенный метод:

protected void finalize();

public void Close();

Есть методики, которые позволяют возродить уничтоженный объект. Но finalize – полностью его удаляет (нельзя вызывать дважды).

В случае, если класс на протяжении своего существования должен освобождать ресурсы не один раз, он обязан содержать метод Close(), который будет это делать. Метод Dispose() вызывается один раз, а close должен быть запрограммирован таким образом, чтобы можно было вызывать его много раз.

Close() – ресурсы освобождены.

В Java метод finalize() вызывается сборщиком мусора. В C# существует деструктор – тонкая обертка для финализзатора finalize().

C#: ~X(){…} – нельзя вызывать явно.

System.Object.finalize – можно вызвать явно.

Сбощик мусора:

mark_and_sweep

Живые и мертвые объекты (ссылки).

Есть стек, в нем ссылки на объекты. Если живой, то помечаем и заносим в таблицу живых объектов, остальные – мертвые, они-то и уничтожаются.

Можно построить КЭШ объектов. Если объект мертвый, то нм нужен он. Но он еще не утилизирован (не успели).

Strong reference – на живой объект.

Weak reference – объект готовится к уничтожение, не пока еще не нуничтожен.

Преобразование типов и классы. Явные и неявные преобразования.

Управление преобразованиями в современных ЯП: проблемы и способы их

решения.

Неявные (автоматически вставленные компилятором).

Int ￿ long

А может ли их задавать пользователь.

В Java и Delphi нет возможности описания пользователем неявных преобразований (в Java нельзя перегружать стандартные операторы).

С++, C# – неявные преобразования, задаваемые пользователем разрешены.

Ф: v = expr – можно считывать различные типы данных.

C#:

«Неплоский» класс – это класс, который сожержит в себе ссылки на другие объекты.

Class Vector

{

T* body;

int size;

public:

Vector(int sz) {body = new[size = sz];}

}

Vector v(20); //ok

Vector t(10);

v = t;

v =1; ~ v = Vector(1); // ошибки не будет: сработате оператор преобразования: у нас «случайно» получился конструктор преобразования.

Решение: если мы не хотим, чтобы наш конструктор «использовался» в подобных целях, то с помощью ключевого слова explicit конструктор может стать обычным конструктором. Вот так:

explicit vector(int sj){Body = new T[size=sj];}

Теперь наш конструктор может вызываться только явноV = Vector(1);…

В C# explicit принято по умолчанию. Существует в C# и ключевое слово implicit.

Cвойства (properties).

«Свойство» - это член класса, который с точки зрения обращения к нему выглядит как член-данное, но с точки зрения реализации представлен двумя методами, один из которых возвращает значение свойства, а второй — устанавливает его значение. При этом один из методов может отсутствовать, делая недоступной соответствующую операцию над свойством.

В С++ нет. Все данные по определению закрытые, а вместо операций для свойста есть геттеры и сеттеры.

Есть в Delphi, C#.

Пример для языка Delphi – целое свойство Prop:

type PropSample = class

private

procedure SetPropVal(V: integer);

function GetPropVal:integer;

public

property Prop: integer read SetPropVal write GetPropVal;

end;

published//все опубликованные свойства появляются в интегрированной среде разработки.

property X: T read_x; write_x;

С#:

class X

{

T prop {get{…} set{…}} //value является зарезервированным в set.

}

X a = new X();

a.prop = t1;

T t2 = a.prop;

Классы и перегрузка имен. Перегрузка встроенных знаков операций.

В Java нет перегрузки.

С++: T operator+(T x1, T x2);

В Си++ typedef задаёт синоним типа, а не создаёт новый тип.

typedef int a;

void f(a x)

void f(int x) Перегрузки не будет, т.к. «a» не новый тип(=> будет ошибка)

Ада: function “+” (x1, x2: T) return T;

Перегружаемый операторы в C#:

· базисный

· арифметический

· побитовый

[ ] в C# перегружать нельзя(в отличие от С++, в которых Страуструп решил проблему в общем случае). Общий синтаксис:

T operator *(операнд) {…………………}

Один из недостатков перегрузки операций – несимметричность операндов:

a * b => a.operator*(b)

Так перегружаются операции в C#:

static T operator *(T t1, T t2) {………………………};//в шарпе – только статический.

Аналогично и с операторами преобразования: они обызаны быть статическими члеами:

static operator T(X a){…………………}

Итераторы и индексаторы.

Механизм индексаторов в C# - компенсирует отсутствие возможности перегрузки операции индексирования.

Синтаксис:

T this (I index){………………… }

Java – вложенные статические и нестатические классы

class Outer{

static class Inner{…….};

Нестатический блок-класс имеет ссылку на Outer.this

Inner in = this.newInner();//если мы пишем, естественно, внутри класса Outer.

А так – вместо this может стоять ссылка на любой оъект класса Outer.

Outer invoice;

Inner = invoice.newInner();

В Java2 появилось понятие локального внутреннего класса

Iterable f(object[] objs)

{

class Local: Iterable {int i; Local() {i = 0;}… if (i>objs) …}

return new Local(C);

}

Имеет мест доступ к локальным переменным функции, если в данном блоке не изменяются.

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);

Классы и стандартные библиотеки. Встроенные классы стандартной

библиотеки.

6. Инкапсуляция и абстрактные типы данных.

Понятие инкапсуляции. Единицы и атомы защиты. Понятие

абстрактного типа данных (АТД) и его достоинства.

Абстрактный тип данных = множество операций. (основной вид современного программирования)

Абстрактный тип данных - это набор процедур, объявленный в отдельном пакете. Здесь, оказывается, есть инкапсуляция:

· единицы инкапсуляции: тип или экземпляр типа

· атомы инкапсуляции: отдельные поля и члены типа или весь тип.

В большинстве языков программирования единица инкапсуляции – тип.

В языке Оберон есть защита отдельных членов, что позволяет по отдельности экспортировать отдельные поля.

Ада и Modula-2 инкапсулируют целиком весь тип: такая тактика вынуждает нас к полной инкапсуляции

Атомы инкапсуляции – минимально возможные данные, которые можно скрыть от пользователя. Для всех

класс – ориентированных языков атомы инкапсуляции – это поля класса, а таких языках как Ада, Модула – 2 – минимальным атомом является весь класс, то есть хороший программист на таких языках всегда использует абстрактные типы данных.

И нкапсуляция и логические модули. Управление видимостью.

Реализация АТД в модульных языках программирования (Ада, Оберон,

Модула).

М-2: скрытые ТД

DEFINITION MODULE STACKS;

FROM MYTYPES IMPORT T; //возможность получить доступ к типу, который описан в другом модуле

TYPE STACK; (*скрытый ТД*) //компилятор не знает, что это.

PROCEDURE PUSH

INIT

DESTROY

END STACKS.

DEF -> транслируется в SYM(таблица символов) и OBJ(реализация).

STACK ~ INTEGER или POINTER

TYPE STACK = POINTER TO STACKREC

STACKREC = RECORD … END

:= (shallow, copy), = (равно), # (не равно)

Ада 83:

приватный ТД (~скрытый ТД)

ограниченно приватный ТД

￿

 

package stacks is type stack is private;

… - описание всех заголовков.

private

… - описание всех приватных структур данных.

:=, =, /=

ограниченно приватный:

type T is limited privaty;

… - оперции.

private type T is …;

Инкапсуляция и классы. Управление видимостью и управление

доступом. Пространства имен и инкапсуляция. Реализация АТД с помощью

понятия класса.

Управление инкапсуляцией:

• Управление доступом – C++, C#, D

• Управление видимостью – Java

Управление видимостью – «private»-членов как бы просто нет для других классов, они «невидимы».

Управление доступом – все не скрытые (не переопределённые) члены видны, т.е. компилятор постоянно «знает» об их существовании, но при обращении проверяются права на доступ. При попытке обращения к недоступному члену выдаётся ошибка.

Три уровня инкапсуляции:

1.public

2.private

3.protected

«свой» - член данного класса

«чужой» - все внешние классы

«свои» - члены наследованных классов

public разрешает доступ всем

private разрешает доступ только «своему»

protected разрешает доступ «своим» и «своему»

Если требуется, чтобы доступ к приватным членам был не только у «своего», можно для этой этого объявить нужную дружественную конструкцию в теле класса:

friend «объявление друга»;// Можно писать сразу определение. Другом может быть

функция или целый класс

friend «прототип глобальной функции»

friend «прототип функции-члена другого класса»

friend class «имя класса-друга»;// Все методы этого класса становятся дружественными

В Delphi, C#, Java друзей нет

В них реализованы этот механизм реализован немного по-другому:

Delphi UNIT

Java package

C# assembly

В Java по умолчанию пакетный доступ. Это значит, что использовать класс может каждый класс из этого пакета. Если класс объявить как «public class …», то он будет доступен и вне пакета. Использовать класс – наследовать, создавать объекты.

C#:

Сборка – надъязыковое понятие в.NerFramework. Сборка представляет собой совокупность файлов + манифест сборки, Любая сборка, статическая или динамическая, содержит коллекцию данных с описанием того, как ее элементы связаны друг с другом. Эти метаданные содержатся в манифесте сборки. Манифест сборки содержит все метаданные, необходимые для задания требований сборки к версиям и удостоверения безопасности, а также все метаданные, необходимые для определения области действия сборки и разрешения

ссылок на ресурсы и классы.

Внутри сборки идёт разделение на пространства имён, которые содержат описания классов.

Для использования какого-либо пространства имён нужно сначала подключить сборку, содержащую его.

Пространство имён может быть «размазана» по нескольким сборкам.

В C# для членов классов имеются следующие квалификаторы доступа:

• public

• private // по умолчанию

• protected

• internal – член доступен только в классах из сборки

• internal protected – член доступен только в классах-наследниках, находящихся в сборке

Для самих классов:

• public – класс можно использовать в любых классах

• internal – класс можно использовать только в классах из его сборки (по умолчанию)

Delphi

type T = class

 

…. // здесь объявляются члены, видимые везде их данного модуля и не видимые

// других

public

….

protected

….

private

…..

end;

UNIT – единица дистрибуции

Принцип разделения определения, реализации и использования

(РОРИ). Эволюция принципа РОРИ в современных ЯП.

РОРИ – метод, когда реализация скрыта от пользователя. Пользователю доступен лишь интерфейс, а реализация инкапсулирована.

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

Абстрактный тип данных = множество операций

7. Модульность и раздельная трансляция

Виды трансляции. Физические модули. Программная и трансляционная

библиотеки. Раздельная трансляция: зависимая и независимая. Недостатки



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: