Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ООП.doc
Скачиваний:
22
Добавлен:
08.11.2018
Размер:
1.4 Mб
Скачать
  1. Области видимости

Одним из основополагающих принципов объектно-ориентированного программирования является инкапсуляция – сведение кода и данных воедино, причем прямой доступ к данным нежелателен: их чтение и обновление должно производиться посредством вызовов соответствующих методов. В модели объектов языка Object Pascal существует механизм, позволяющий ограничить доступ к составным частям объекта, называемый области видимости.

Поля методы и свойства объекта могут относиться к одной из пяти групп, отличающихся областями видимости:

  • личные – private;

  • защищенные – protected;

  • общие – public;

  • опубликованные – published;

  • пятая группа automated рассматриваться не будет.

Директива, определяющая видимость указывается перед описанием элемента класса и относится ко всем элементам класса до следующей директивы, определяющей область видимости.

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

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

Поля, свойства и методы, объявленные public, не имеют ограничений на видимость. Они доступны из других подпрограмм и методов объектов, как в данном модуле, так и во всех прочих, ссылающихся на него (через директиву uses и имя модуля).

Рассмотрим пример. В первом модуле объявим класс, в состав которого входят три метода, имеющие разную область видимости, и одну процедуру, не являющуюся методом класса. Любые вызовы в пределах одного модуля допустимы:

unit first; interface type TSomeObj1 = class private procedure Method1; protected procedure Method2; public procedure Method3; end;

procedure Test1;

var SomeObj1: TSomeObj1;

implementation procedure TSomeObj1.Method1; begin ... end;

procedure TSomeObj1.Method2; begin Method1; ... end;

procedure TSomeObj1.Method3; begin Method2; ... end;

procedure Test1; begin SomeObj1 := TSomeObj1.Create; SomeObj1.Method1; //допустимо SomeObj1.Method2; //допустимо SomeObj1.Method3; //допустимо SomeObj1.Free; end;

end.

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

unit second;

interface uses First;

type TSomeObj2 = class(TSomeObj1) procedure Method4; end;

procedure Test2;

implementation var SomeObj1: TSomeObj1; SomeObj2: TSomeObj2;

procedure TSomeObj2.Method4; begin // Method1; //не допустимо, поскольку область //видимости private а модуль другой

Method2; //допустимо, поскольку область видимости // protected, а текущая процедура – метод класса-потомка

Method3; //допустимо, поскольку область видимости public, end;

procedure Test2; begin SomeObj1 := TSomeObj1.Create;

// SomeObj1.Method1; //не допустимо, поскольку область //видимости private а модуль другой

// SomeObj1.Method2; //не допустимо, поскольку область //видимости protected а это метод класса, //объявленного в другом модуле

SomeObj1.Method3; //допустимо, поскольку область //видимости public

SomeObj1.Free; SomeObj2 := TSomeObj2.Create;

// SomeObj2.Method1; //не допустимо, поскольку область //видимости private а модуль другой

SomeObj2.Method2; //допустимо, поскольку область //видимости protected а это метод класса, объявленного //в этом же модуле, не смотря на то, что сам метод //объявлен в классе-предке в другом модуле

SomeObj2.Method3; //допустимо, поскольку область //видимости public

SomeObj2.Free; end;

end.

Если бы процедура Test2 была объявлена в третьем модуле, метод Method2 в ней был бы недоступен и для класса TSomeObj2, так как он доступен только в том модуле, где этот класс определен.

Область видимости, определяемая четвертой директивой – published, предназначена для интерфейса визуального проектирования Borland Delphi. С этой директивой объявляются те свойства объекта, являющегося компонентом Borland Delphi, которые будут видны не только во время исполнения приложения, но и из среды разработки. Публиковать можно свойства большинства типов, за исключением свойств типа массив и старого типа real (real48). Все свойства компонентов, доступные через инспектор объектов, являются их опубликованными свойствами. Во время выполнения такие свойства общедоступны, т.е. имеют ту же область видимости, что и объявленные public.

Если элемент класса объявлен без указания спецификатора видимости, он будет иметь ту же видимость, что и предыдущий. Если спецификатора видимости не имеет элемент, стоящий первым в описании класса, он либо будет иметь видимость published, если для этого класса или его предка установлен флаг компиляции {$M+}, в ином случае такой элемент будет иметь видимость public.

Для лучшей читаемости рекомендуется организовывать элементы класса в группы по их видимости, то есть расположить вместе все элементы с видимостью private, далее все элементы с видимостью protected и так далее. При таком способе группировки каждая директива, обозначающая видимость, употребляется только один раз и показывает начало новой «секции» определения класса. Типичная декларация класса имеет вид:

type

TMyClass = class(TObject) private ... //личные элементы класса protected ... //защищенные элементы класса public ... //общие элементы класса published ... //опубликованные элементы класса end;

Области видимости private, protected, public, published упорядочены по возрастанию видимости. В классах-потомках можно повысить видимость его элементов, но не понизить. Рассмотрим, как это правило действует по различным типам элементов класса.

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

При виртуальном или динамическом перекрытии методов можно повышать видимость от protected к public. Понятно, что методы с видимостью private в общем случае не видны в классе-потомке, и делать их виртуальными или динамическими бессмысленно. При понижении видимости виртуальных и динамических методов компилятор выдаст предупреждение и оставит видимость неизменной.

При перекрытии свойств возможно повышение области видимости. Например, свойство, объявленное protected, в классе-потомке может стать public или published. Естественно, что из области private достать свойство не представляется возможным. Понижение видимости при перекрытии свойств невозможно и при попытке понизить видимость, компилятор оставит ее неизменной. То есть, свойство, объявленное published не может стать public в классе-потомке, а объявленное public не может стать private или protected.

    1. Области видимости в С++

Элементы класса в С++ могут иметь следующие спецификаторы доступа:

public: – элементы, следующие за этим ключевым словом доступны из любой точки программы, имеющей ту же область действия, что и определение класса;

private: – элемент может быть использован только функциями-элементами и «друзьями» класса, в котором он объявлен. Понятие «друзей» класса будет рассмотрено ниже;

protected: – то же, что и private, но элемент может быть использован функциями элементами и друзьями классов, производных от объявленного класса.

Директива, определяющая видимость, указывается перед объявлением элемента класса и относится ко всем элементам класса до следующей директивы области видимости. Элемент класса, объявленный до первого спецификатора доступа, будет иметь область видимости по умолчанию. Если класс объявлен как class, то по умолчанию его элементы имеют область видимости private. Если класс объявлен как struct или union, то его элементы, объявленные без указания области видимости, являются общедоступными, то есть имеют область видимости public.

class CBase { ... //элементы могут быть доступны только из методов класса private: ... //элементы могут быть доступны только из методов класса protected: ... //элементы могут быть доступны из методов класса //и его потомков public: ... //элементы доступны из любого места программы };

При объявлении производного класса можно задать спецификаторы доступа public или private перед базовыми классами. Использовать в этом случае спецификатор protected нельзя. Эти модификаторы не изменяют атрибутов доступа базовых элементов с точки зрения базового класса, но могут изменить атрибуты доступа базовых элементов с точки зрения производных классов.

Если базовый класс при объявлении производного класса был указан без спецификатора доступа, то используется значение спецификатора по умолчанию. Если производный класс объявлен как class, то спецификатор по умолчанию имеет значение private, если как struct, то public. Класс, объявленный как union, не может участвовать в иерархии наследования.

Таким образом, производный класс наследует атрибуты базового класса следующим образом:

Базовый класс объявлен со спецификатором public

элемент базового класса

элемент производного класса

public

public

protected

protected

private

не доступен

Базовый класс объявлен со спецификатором private

элемент базового класса

элемент производного класса

public

private

protected

private

private

не доступен

При этом имеет место понижение, а не повышение видимости.

В обоих случаях элементы private базового класса были и остаются недоступными для методов производного класса, если в описании базового класса производный класс не будет задан как дружественный.

Рассмотрим пример изменения видимости при наследовании:

class CBase { private: void f1(); //доступна только из методов класса protected: void f2(); //доступна из методов класса и его потомков public: void f3(); //доступна из любого места программы };

class CDerived1: CBase { public: void f4(); //доступна из любого места программы };

class CDerived2: CDerived1 { public: void f5(); //доступна из любого места программы }; ...

void CDerived1::f4() { //f1(); //не может быть вызвана, т.к. private f2(); //может быть вызвана, т.к. protected f3(); //может быть вызвана, т.к. public }

void CDerived2::f5() { //f1(); //не может быть вызвана, т.к. private //f2(); //не может быть вызвана, т.к. область видимости //изменилась на private //f3(); //не может быть вызвана, т.к. область видимости //изменилась на private f4(); //может быть вызвана т.к. public }

void main(void) { CBase base; CDerived1 derived; // base.f1();//не может быть вызвана, т.к. private // base.f2();//не может быть вызвана, т.к. protected base.f3(); //может быть вызвана, т.к. public //derived.f1();//не может быть вызвана, т.к. private //derived.f2();//не может быть вызвана, т.к. private //derived.f3();//не может быть вызвана, т.к. private }

Рассмотрим далее понятие «друзей» класса. «Друг» класса Х – это функция или класс, которые, не являясь элементом Х, имеют полные права доступа к private и protected элементам Х. Во всех прочих отношениях это обычная функция или класс. Поскольку «друг» класса не является его элементом, он не может быть вызван через имя объекта этого класса.

Для того чтобы объявить функцию «другом» класса Х, следует указать ее имя в объявлении класса Х со спецификатором friend. Для того чтобы объявить класс Y «другом» класса Х, также следует указать его имя в объявлении класса Х со спецификатором friend, тогда все методы Y будут друзьями Х, хотя сами они не имеют спецификатора friend. Также можно сделать отдельные методы класса Y друзьями класса Х. В этом случае в описании класса Х со спецификатором friend указывается имя метода класса Y с указанием имени класса.

«Друзья» могут быть объявлены в любом месте описания класса и не зависят от спецификаторов доступа:

class CY;

class CX { friend void f1(CX*, int); //объявили функцию f1 «другом» //класса CX private: int i; public: void f2(int); ... friend CY; //объявили класс CY «другом» класса CX };

class CY { ... friend void CX::f2(int); //объявили метод f2 класса CX //«другом» класса CY }; void f1(CX* x, int i) { ... //реализация функции f1 //она не является методом класса CX и не получает указателя //this, но она имеет доступ к защищенным элементов объектов //этого класса например через полученный указатель CX* x : x->i = 0; }

«Дружба» классов не транзитивна: если X друг Y, а Y друг Z, это не означает, что X друг Z. Также «дружба» не наследуется.