46019 (665326), страница 18
Текст из файла (страница 18)
Компоненты класса по умолчанию имеют атрибут private, поэтому для переопределенияданного объявления спецификаторы доступа public или protected должны задаваться явно.
Компоненты struct по умолчанию имеют атрибут public, но вы можете переопределитьэто умолчаниепри помощи спецификаторов доступа public или protected.
Компоненты union поумолчанию имеют атрибут public; переопределить его нельзя. Все три спецификатора доступа задавать для компонентов объединения недопустимо.
Модификатор доступа по умолчанию или заданный для переопределения атрибута доступа остается действительным для всех последующих объявлений компонентов, пока не встретится другой модификатор доступа. Например,
class X (*
int i; // X::i по умолчанию privte
char ch; // так же, как и X::ch
public:
int j; // следующие два компонента - public
int k;
protected:
int l; // X::l - protected
*);
struct Y (*
int i; // Y::i по умолчанию public
private:
int j; // Y::j - private
public:
int k; // Y::k - public
*);
union Z (*
int i; // public по умолчанию, других вариантов нет
double d;
*);
Спецификаторы доступа могут быть перечислены и сгруппированы в любой удобной последовательности.Можно сэкономить место при набивке программы, объявив все компоненты private сразу, и т.д.
Доступ к базовым и производным классам
При объявлении производного класса D вы перечисляете базовые классы В1, В2 ... в разделяемом запятой базовом-списке:
ключ-класса D:базовый-список (**)
Поскольку сам базовый класс может являтьсяпроизводным классом, то вопрос об атрибуте доступа решается рекурсивно: вы отслеживаете его до тех пор, пока не доберетесь до "самого" базового класса, породившего все остальные.
D наследует всем компонентамбазовых классов. (Переопределенные компоненты базовых классов наследуются, ипри необходимостидоступк нимвозможен при помощи переопределений контекста). D может использовать только компоненты базовых классов с атрибутами public и protected. Однако, что будут представлять собойатрибуты доступа унаследованных компонентов с точки зрения D? D может понадобиться использоватьpublicкомпонент базового класса, но при этом сделать его для внешних функций private. Решение здесь состоитв том, чтобыиспользовать спецификаторы доступа в базовом-списке.
При объявлении D вы можете задать спецификатор доступа public или private перед классами в базовом-списке:
class D : public B1, private B2, ... (*
...
*)
Задать в базовом-списке protected нельзя. Объединения не могут содержать базовых классов и не могут сами быть использованы в качестве базовых классов.
Эти модификаторы не изменяют атрибутов доступа базовых членов с точки зрениябазового класса, хотя и могут изменить атрибуты доступа базовых компонентов с точки зрения производных классов.
Умолчанием является private, если D есть объявление класса class, и public, если D есть объявление структуры struct.
Производный класс наследует атрибуты доступа базового класса следующим образом:
базовый класс public: компоненты public базового классастановятся членами public производного класса. Компоненты pro- tected базового класса становятся компонентами protected производногокласса. Компоненты private базового класса остаютсядля базового класса private.
базовый класс private: и public, и protected компоненты базового классастановятся private компонентами производного класса.Компоненты private базового класса остаются для базового класса private.
В обоих случаях отметим, что компоненты private базового класса былии остаются недоступными дляфункций-компонентов производного класса, пока в описании доступа базового класса не будут явно заданы объявления friend. Например,
class X : A (* // умолчание для класса - private A
...
*)
/* класс X является производным от класса А */
class Y : B, public C (* // переопределяет умолчание для С
...
*)
/* класс Y является производным (множественное наследование) от B и C. По умолчанию - private B */
struct S : D (* // умолчание для struct - public D
... /* struct S - производная от D */
*)
struct T : private D, E (* // переопределяет умолчание для D // E по умолчанию public
...
*)
/* struct T является производной (множественное наследование) от D и T. По умолчанию - public E */
Действие спецификаторов доступа в базовом списке можно скорректировать при помощи квалифицированного-имени вобъявлениях public или protected для производного класса. Например,
class B (*
int a; // по умолчанию private
public:
int b, c;
int Bfunc(void);
*);
class X : private B (* // теперь в Х a, b, c и Bfunc - private int d; // по умолчанию private. Примечание: f
// в Х недоступна
public:
B::c; // c была private; теперь она public
int e;
int Xfunc(void);
*);
int Efunc(X& x); // внешняя по отношению к В и Х
Функция Tfunc может использовать только имена с атрибутом public, например c, e и Xfunc.
Функция Xfunc в X является производной от private B, поэтому она имеет доступ к:
- "скорректированной-к-типу-public" c
- "private-относительно-Х" компонентам В:b и Bfunc
- собственным private и public компонентам: d, e и Xfunc.
Однако, Xfunc не имеет доступа к "private-относительно- B" компоненту a.
Виртуальные базовые классы
При множественном наследованиибазовый класс не может быть задан в производном классе более одного раза:
class B (* ... *);
class D : B, B (* ... *): // недопустимо
Однако, базовый класс может быть передан производному классу более одного раза косвенно:
class X : public B (* ... *)
class Y : public B (* ... *)
class Z : public X, public Y (* ... *) // допустимо
В данном случае каждый объект класса Z будет иметь два подобъекта класса В. Если это вызывает проблемы, к спецификатору базового класса может бытьдобавлено ключевое слово virtual. Например,
class X : virtual public B (* ... *)
class Y : virtual public B (* ... *)
class Z : public X, public Y (* ... *)
Теперь В является виртуальным базовым классом,а класс Z имеет только один под-объект класса В.
"Друзья" классов (friend)
Друг F класса X это функция или класс, которые, не являясь функцией-компонентом Х, имеют тем не менее полные права доступа к компонентам Х private и protected. Во всех прочих отношениях F это обычная с точки зрения контекста, объявлений иопределений функция.
Поскольку F не является компонентом Х, она не лежит в контексте Х и поэтому не может вызываться операциями выбора x.F и xptr->F (где x это объект Х, а xptr это указатель объекта Х.
Если в объявлении или определении функции в пределах класса Х используется спецификатор friend, то такаяфункция становится "другом" класса Х.
Функция-"друг", определенная в пределах класса, подчиняется тем же правилам встраивания, что и функции-компоненты класса (см. "Встраиваемые функции" на стр.105 оригинала). Функции-"друзья" не зависят от их позиции в классе или спецификаторов доступа. Например,
class X (*
int i; // private относительно Х
friend void friend_func(X*, int);
/* friend_func не является private, хотя она и объявлена
в разделе private */
public:
void member_func(int);
*);
/*объявления;для обеих функций отметим доступ к private int i*/
void friend_func(X* xptr, int a) (* xptr->i = a; *)
void X::member_func(int a) (* i = a; *)
X xobj;
/* отметим различие в вызовах функций */
friend_func(&xobj, 6);
xobj.member_func(6);
Вы можете сделать все функции класса Y друзьями класса Х в одном объявлении:
class Y; // неполное объявление
class X (*
friend Y;
int i;
void member_funcX();
*);
class Y; (*
void friend_X1(X&);
void friend_X2(X*);
...
*);
Функции, объявленные в Y, являются друзьями Х, хотя они и не имеютспецификаторовfriend. Они имеют доступ к компонентам Х private, таким как i и member_funcX.
Отдельные функции-компоненты класса Хтакже могут быть друзьями класса Y:
class X (*
...
void member_funcX();
*)
class Y (*
int i;
friend void X::member_funcX();
...
*);
"Дружба" классов не транзитивна: если X друг Y, а Y друг Z, это не означает, что X - друг Z. Однако, "дружба" наследуется. Конструкторы и деструкторы
Существует несколько специальных функций компонентов, определяющих, каким образом объектыкласса создаются, инициализируются, копируются и разрушается. Констукторы и деструкторы являются наиболее важными изних. Они обладают большинством характеристик обычных функций-компонентов - вы должны объявить и определить их в пределах класса, либо объявить их в классе, но определить вне его - однако, они обладают и некоторыми уникальными свойствами.
1. Они не имеют объявлений значений возврата (даже void).
2. Они не могут быть унаследованы, хотя производный класс может вызывать конструкторы и деструкторы базового класса.
3. Конструкторы, как и большинство функций С++, могут иметь аргументы по умолчанию или использовать списки инициализации компонентов.
4. Деструкторы могут иметь атрибут virtual, но конструкторы не могут.
5. Вы не можете работать с их адресами:
main()
(*
...
void *ptr = base::base; // недопустимо
...
*)
6. Если конструкторы и деструкторы не были заданы явно, то они могут быть сгенерированы Turbo C++; во многих случаях они также могут быть запущены при отсутствии явного вызова в вашей программе. Любой конструктор или деструктор, создаваемый компилятором, должен иметь атрибут public.
7. Вызвать конструктор тем же образом, что и обычную функцию, нельзя. Вызов деструктора допустим только с полностью квалифицированным именем.
8.
(*
...
X *p;
...
p->X::-X();// допустимый вызов деструктора
X::X();// недопустимый вызов конструктора
...
*)
9. При определении и разрушении объектов компилятор выполняет вызов конструкторов и деструкторов автоматически.
10.Конструкторы и деструкторы при необходимости распределения объекту памяти могут выполнять неявные вызовы операций new и delete.
11.Объект с конструктором или деструктором не может быть использован в качестве компонента объединения.
Если класс Х имеет один или более конструкторов, то один из них запускается всякий разпри определении объекта х класса Х. Конструктор создает объект х и инициализирует его. Деструкторы выполняют обратный процесс,разрушая объекты класса, созданные конструкторами.
Конструкторы активизируются также при создании локальныхили временных объектов данного класса;деструкторы активизируются всякий раз, когда эти объекты выходят из контекста. Конструкторы
Констукторы отличаются от прочих компонентов функций тем, что имеют то же самое имя, что и класс, к которому они относятся.При создании или копировании объекта данного класса происходит неявный вызов соответствующего конструктора.
Конструкторы глобальных переменных вызываются до вызова функции main. При использовании стартовой директивыpragmaдля инсталлирования некоторой функции до функции main, конструкторы глобальных переменных вызываются до стартовых функций.
Локальные объекты создаются, как только становится активным контекст переменной.Конструктор также запускается при создании временного объекта данного класса.
class x
(*
public:
X(); //конструктор класса Х
*);
Конструктор класса Х не может принимать Х как аргумент:
class X (*
...
public
X(X); // недопустимо *)
Параметры конструкторы могут быть любого типа, за исключением класса, компонентомкоторого является данный конструктор. Конструктор может приниматьв качестве параметра ссылку на свой собственный класс; в таком случае он называется конструктором копирования.Конструктор, не принимающийпараметров вообще, называется конструктором поумолчанию. Далее мы рассмотрим конструкторы по умолчанию; описание же конструктора копирования начинается со стр.116 оригинала.
Конструктор по умолчанию
Конструктором по умолчанию для класса Х называется такой конструктор, который не принимает никаких аргументов: Х::Х(). Если длякласса несуществует конструкторов, определяемых пользователем, то Turbo C++ генерирует конструктор по умолчанию. При таких объявлениях, как Х х, конструктор по умолчанию создает объект х.
Важное примечание:
Как и все функции, конструкторымогут иметь аргументы по умолчанию. Например, конструктор
X::X(int, int = 0)