Языки программирования, 20 лекция (от 16 ноября)
Материал из ESyr's Wiki pages.
ЯП 16.11.06
Защита
Видимость / Доступ
Модульные языки (Ада, Модула 2) – есть управление видимостью.
В ОО языках – управление доступом.
Если какой-то член невидимый,
Разница между видимостью и доступом проявляется при наследочвании:
X:
private:
i
Y:
public
i
Представим, что i – виртуальная функция. Если i в. ф. , то если бы был подход на уровне функции, то его было бы вообще не видно. И с этой тз представим что i вирт функция, из и можно получить доступ к приватной ф-ции i. Такое в C++ запрещено.
При множественном наследовании работают правла доступа, а не видимости.
Подход на уровне видимости давал бы отждественность i.
При подзоде на уровне доступа возникает конфликт, ибо непонятно, а каком имени идёт речь.
Сначала определяют имена.
В модульных языках там естественным образом подход управления видимости.
Последнее замечание:
полностью проблему защиты решить на уровне языка нельзя. Защита идёт не от взлома, а от дурака. Так как есть произвольный доступ. Можно преобразовать тип указателя неконтроллируемо. Ибо мы можем сделать структуру, у которй все поля открыты ,то можно преобразовать в неё и вперёд.
В Джаве-Си шарп преобразования контроллируемые. Поэтому просто так сделать нельзя.
На уровне языка полностью защититься нельзя.
Если есть gets, то можно написать тест, который ламает программу. И язык, который позволяет функции типа gets, ненадёжен.
Инкапсуляция данных
Подход, связанный с Логическим Модулем (ЛМ)
Для начала возьмём самы простой язык, Модула-2. Там единица защиты – весь тип.
Modukla-2
DEFINITION MODULE M
TYPE T = ...;
...
END M.
IMPLEMENTATION MODULE M;
ENDM.
полностью скрытый тип данных – OPAQUE, для него мы указываем только его имя. И видим только то, что описано в DefINITION.
TYPE Stack
PROCEDURE Init(VAR S: Stack);
PROCEDURE Destroy(VAR S: Stack);
PROCEDURE Pop ...
Скрытый ТД выглядит почти как АТД. Различия связаны с механизмом раздельной трансляции. В М2 библиотечне модули служат единицами компиляции. То есть нельзя вместе неск мобъявления и реализацию.
Для того, чтобы откомпилировать модульский файл, то нужно знать только модули, которые он импортирует.
IMPORT P.name FROM P;
IMPORT NAME;
Язык сконструирован так, что ему нужен только модуль определений.
Сразу возникает тривиальный вопрос – пишу IMPORT Stacks, и в этом модуле аоперделена некоторая перемменная
VAR S : Stacks.Stack; - сколько байтов отводить под S?
Для тогро, чтобы упростить язык, срытый тип данных может быть либо указателем, либо совместимый с ним (INTEGER)
х86 – дурацкая архитектура. Ни одного нормального приложения без ошибок там написано не было.
Это накладывает ограничение на реализацию стека.
В Модуле-2 есть файл, который называется IO. Там есть скрытый ТД файл, и там он – целое число – дескриптор.
Для стека можно сделать примерно точно также – завести таблицу стеков. И фактически, мы сами реализуем алгоритм распределения дин памяти. Поэтому чаще используется динамический.
Как следствие, к скрытым типа данных неявно применимы присваивание и сравнение на равенство-неравенство. Присваивание очень коварно.
Теперь понятно, что Init и Destroy тоже обязательны.
//Лектоор говорил, что Модула-2 заточена под 16-битные архитектуры, но при этом, из этой лекции следует, что интегер 32-битный.
В Обероне проблемы, связанные с этим, решены. Как в Обероне-2 скрыть?
TYPE Stack* =
RECORD
BODY: ARRAY N OF T;
TOP: INTEGER;
END;
PROCEDURE PUSH*(...);
В соответствующем модуле определений можно увидеть, что стек - пустая запись.
DEFINITION Stacks;
TYPE Stack* =
RECORD
END;
В С++ если исправить какой-нибудь хедер (поправить комментарий), который включается везде (например, stdafx.h), то это влечёт полный ребилд проекта. В Дельфы если исправить, то делается только ребилд модуля, там делается побитовое сравнение скомпилированного модуля.
Компилятор Оберона на Обероне – 4000 строк.
В Обероне, так как отказались от разделения опр и реализации то проблема исчезла.
Поскольку компилятор всё-таки знает структуру класса, то и ту же операцию присваивания можно сделать. Отменить её, кстати, нельзя.
Как проблему решали создатели языка Ада:
Там есть конструкция, которая полностью отвечает АТД.
В Аде полностью реализован РОРИ. Модуль и реализация могут быть разделены физически.
Приватный тип данных:
type T is private;
операции над Т;
сразу какая проблема – как размещать память. Заметим, что если мы реализуем стек в виде записи, т в Аде есть приятная возможность:
record
top : integer = 1;
end record;
Инициализировать при размещении записи.
Возникает парадокс – с одной стороны надо скрыть структуру, с другой компиляторы надо видеть. Но это решается просто, так как скрывается структура от пользователя.
Выход – структура типа описывается в определении.
package P is
type T is private;
...
private
определения всех приватных типов из Р
end P;
Все приватные типы должны быдь определны.
Эта приватная часть доступна только компилятору, но программист, даже если их и видит, использовать не может.
Стек:
package Stacks is
type Stack is private;
процедуры работы со стеком (заголовки);
private
type Stack is access;
type LINK is record
b:T;
next : Stack;
end record;
type Stack is access Link;
end Stacks;
Здесь все опр типов, необходимые для реализации.
Проблема:
Та же, если в случае когда нет разделение опред и реализ. Представим, что мы сменили реализацию. Что нужно в Модуле сделать, чтобы проект заработал снова: перекомпилировать полностью модуль стек и пересобрвть проект. В Аде: перекомпилировать всё, что использует соотв единицу компиляции. Везде, где есть объявление стека, нужно перетранслировать всё.
Побочным эффектом гибкости является необх перетрансляции.
Современные языки игнорируют проблемы, связанные с перетрансляцией. Архитектура совр языков (D, С#, J) настолько проста и органична, что транслируются они быстро.
Приватные ТД не совсем АТД, так как к ним применимы опреац присваивания и сравнения (если структура типа подразумевает эти операции). В Аде, в отличие от многих джругих языков, массивы и записи одного типа можно сравнивать, если к каждому элементу применимы такие операции. Если массив из Т, и к Т применимо сравнение, то и к массиву применимо сравнение. Аналогично и к записи. То есть обобщаются не только операции присваивания, но и сравнения. Также можно сравнивать на больше, меньше, больше равно, меньше равно. Но больше только если все элементы больше.
А настоящий АТД – только явно определённые операции.
Но если мы хотим совсем абстрагироваться, то надо сделать limited private, к нему неприминимы ни присваивание, ни сравнение. Например, это полезно для ссылочных типов, чтобы делать при необх глубокое копирование. Ещё один пример – если нужна уникальность объекта.
Ограниченный приватный ТД – настоящий АТД.
Заметим, что больше никак защиту типа ослабить нельзя.
Тем самым Ада заставляет программировать в приватных или ограниченных приватных ТД. Аналогично в Модуле нужно использовать скрытые типы данных.
Классовые ЯП
Там ни о какой видимости не идёт речь.
В Классовых ЯП речь идёт об управлении доступах, модификаторах.ю
С++:
public – любая структруа имеет доступ
private – только члены класса
И тут речь об управлении доступ – мы видим приватные члены, но ничего с ними не можем сделать.
protected – доступ членам и потомкам.
Эта схема достаточно простоа, но влечёт проблемы.
Например, некотрые операции хочется сделать внешними. Например, жвуместный плюс.
Такая модель либо предоставвяляет полный доступ из вне, либо полностью его закрывает. В нккотрых случаях это неудобно.
Решать эту проблему можно либо с помощью внутренних классов (iterator в STL – iterator - вложенный класс для каждого контейнера)
В С++ эта проблекма решена с помощью механизма друзей. Функция-друг имеет полный доступ к внутренним членам. Друзья объявляются внутри класса. Но при этом друг моего друга не мой друг.
С++ истинный наследник языка С, и етсь возможность меньше писать (друзей можно внутри класса не отлько объявлять, но и определять).
Что такое АТД на С++ - публичными являются только функции-члены данного класса.
Абстрактный класс как может быть АТД, так может и не быть. Это ортогональные понятия.
В С++ присваивание неявно переопределяется, но присваивание можно переопределить и запретить.
Интерфейс – набор публичных членов. Но есть особое понятие интерфейса.
В совр ЯП (Джава, шарп, Дельфы). Подход похожий, но есть свои особые. Механизм публичных членов есть. Есть public, private, protected. Синтаксич сахар: в джаве и си шарп должны ставить модификатор перед каждым членом. Проблема друзей – технологическая.
С# - namespace – пространства имён. Аналог джавовского пакета.
Во всех этих языках есть спец. Способ доступа: С шарп – internal, Джава – доступ по умолчанию. В Си шарп по умолчанию доступ приватный. В структурах по умоланию тоже. В джаве пакетный доступ – с тз доступа члены класса с пакетным доступом разрежается доступ из всех классов из того же пространства имён. Если в Си шарп перед классом стоит internal, то все члены доступны в перделах пространства имён. Аналогично в джае, если не указывать спецификатор доступа, и это логично.
Для класса в си шарп если стоит private перед классом, то это значит internal.
Модификатор доступа перед классом более сильный.
Java:
import P.X
class X extends Y {
void F() - доступ к закрытым членам X
internal гарантирует, что чужой дядя со своими немытыми (грязными – 7:9) руками в мои члены не влезет.
... соответствующий соответственно...
С тз ограничений доступа понятно, что означает internal protected – можно использовать в дочерних классах в пространстве имён.
private -> internal protected -> internal -> protected -> public
В Джаве:
private -> (по умолчанию) -> protected -> public
Дельфи:
всё то же самое. Только умолчание хитрое.
type X = class
... объявления членов без модификатора – у этих членов и только у них внутренний доступ. Для функций-членов из этого юнита они публичны.
Языки Программирования
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 29
Календарь
чт | вт | чт | вт | чт | вт | чт | вт | чт | вт | |
Сентябрь
| 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 | 19 |