Выражение-инремента выполняет приращения одного или нескольких цикловых счетчиков

Turbo C++ Programer`s guide
Первый символ должен являться буквой или символом подчеркивания Тело функции, представляющее собой коды, выполняемые при вызове функции Выражение-приведения это пустой (null) указатель Е1 является указателем, а Е2 - константой типа пустого указателя Выражение-инремента выполняет приращения одного или нескольких цикловых счетчиков Вызвать конструктор тем же образом, что и обычную функцию, нельзя. Вызов деструктора допустим только с полностью квалифицированным именем Инициализатор конструктора (см. "инициализатор-конструктора" в описании синтаксиса декларатора класса в таблице Или 16). Ноль означает по умолчанию десятичную Мы будем обозначать все семейство математических сопроцессоров 80x87 термином "сопроцессор" В режиме эмуляции 80Х87 циклический переход в регистрах, а также ряд других особенностей 80х87 не поддерживается GrOk No error Нет ошибки Кроме того, обратите внимание на то, что функция прерывания выполняет выход с помощью команды IRET (возврата из прерывания) Как идентифицировать диагностические сообщения Что происходит, когда доступ к компоненту объекта типа объединения происходит при помощи компонента другого типа Печатаемые диагностические сообщения и поведение при завершении функции assert
668870
знаков
13
таблиц
0
изображений

3. Выражение-инремента выполняет приращения одного или нескольких цикловых счетчиков.

4. Выражение "оператор" (возможно, пустое) вычисляется, после чего управление возвращается на шаг 2.

Если какие-либо из опциональных элементов опущены, то вместо них должны быть заданы соответствующие точки с запятой:

for (;;) (* // то же, что и for(;1;)

// вечный цикл

*)

C++: Правила С для операторов for применимы и в С++. Однако, выражение-инициализации в С++ может также включать в себяи объявление. Контекст объявленного идентификатора продолжается до конца данного оператора, не далее. Напрмер,

for (int i = 1; i < j; ++i)

(*

if (i ...) ...// здесь обращаться к значению i можно *)

if (i ...)// а здесь нельзя : i вне контекста

Операторы перехода

При выполнении оператор перехода происходит безусловная передача управления в некоторую точку программы. Существует четыре таких оператора: break, continue, goto и return.

Операторы break

Синтаксис следующий:

break;

Оператор break можно использовать только внутри операторов итерации (while, doи for - циклы) или с оператором switch. Он прекращает выполнение итерации или оператора switch. Поскольку операторы итерации и оператор switch могут комбинироваться и иметь любуюглубину вложенности, следует обращать особое внимание на то, чтобы оператр break выполнял выход именно из нужного цикла или оператора switch. Правило состоит в том, что оператор break заканчивает выполнение ближайшего к нему объемлющего цикла итерации или оператора switch.

Операторы continue

Синтаксис следующий:

continue;

Оператор continue может быть использован только внутри оператора итерации; он передает управление на проверку условия циклов while и do, либо на выражение инкремента цикла for.

При вложенностициклов итерации оператор continue считается принадлежащим ближайшей объемлющей итерации.

Операторы goto

Синтаксис следующий:

goto метка;

Оператор goto передает управление оператору, имеющему указанную "метку" (См. "Операторы с метками" на стр.92 оригинала), который должна находиться в пределах той же функции.

С++: В С++ допустимо обойти объявление с явным или неявным инициализатором, если это объявление не находится во внутреннем блоке, который также обходится.

Операторы return

Если тип возврата функции не равен void, то тело функции должно содержать как минимум один оператор return следующего формата:

return выражение-возврата;

где выражение-возвратадолжно быть типа type или типа, преобразуемого к типу, заданному type, при присвоении. Значение выражениявозврата и есть значение, возвращаемое данной функцией. Выражение, вызывающее функцию, вида func(список-действительных-аргументов) является значением rvalue типа type, а не именующим (lvalue) значением.

t = func(arg); // так можно

func(arg) = t; /* в С так нельзя; в С++ так можно, если типом возврата func является ссылка */

(func(arg))++; /* в С так нельзя; в С++ так можно,если типом возврата func является ссылка */

Выполнение вызова функции заканчивается, когда встретился оператор return; если оператор return отсутствует, то выполнение "проваливается" к последней закрыващей фигурной скобке тела функции.

Если тип возврата void, то оператор return можно записать как:

(*

...

return;

*)

без выражения-возврата, либо оператор return вообще может быть опущен.

С++

В целом, С++ является над-множеством языка С. Это означает, что, вообще говоря, можно компилировать программы С в среде С++, однако компилировать программы С++ в среде С при наличии в них каких-либо специфических для С++ конструкций нельзя. Некоторые ситуации требуют специального внимания. Одна и та же функция func, дважды объявленная в С с различными значениями аргументов, вызовет ошибку повторения имен. Однако, в С++ func интерпретируется как перегруженная функция - а то, допустимо это или нет,зависит от других обстоятельств. Общие вопросы программирования на С++ см. в главе 5, "Основы С++" документа "Начало работы". Глава 6, "Краткий справочник по С++" в том же документе поможет вам быстро понять, как именно работают конструкции языка.

Хотя С++ вводит новые ключевые слова и операции для работы с классами, некоторые средства С++ применимы вне контекста классов. Сначала мы рассмотрим тиенно эти, используемые незвисимо от классов средства, а затем займемся спецификой работы с классами и связанными с ними механизмами.

Ссылки

Установка ссылок при помощи указателей и обращение по ссылкам рассматриваются на стр.80 оригинала.

Типы ссылок Turbo C++ тесно связаны с типами указателей. Типы ссылок С++ служат для создания алиасов объектов и позволяют передачу аргументов функциям по ссылке. Традиционно С передает аргументы только по значению. С++ позводяет передавать аргументыкак по значению, так и по ссылке.

Простые ссылки

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

int i = 0;

int &ir = i; // ir является алиасом i

ir = 2; // то же, что i = 2

В данном примере создается именующее значение ir, являющееся алиасом i, при условии, что инициализатор имеет тот же тип, что и ссылка. Выполнение операций с ir имеет тот же результат, что и выполнение их с i. Напрмер, ir = 2 присваивает 2 переменной i, а &ir возвращает адрес i.

Аргументы типа ссылки

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

void func1 (int i);

void func2 (int &ir); // ir имеет тип "ссылка на int"

...

int sum=3;

func1(sum); // sum передается по значению func2(sum) // sum передается по ссылке

Переданный по ссылке аргумент sum может быть изменен прямо в func2. Напротив, func1 получает только копию аргумента sum (переданного по значению), поэтому сама переменная sum функцией func1 изменена быть не может.

При передаче фактического аргумента x по значению соответствующий формальный аргумент в функции принимает копию x. Любые изменения этой копии в теле функции не отражаются на самом значении x. Разумеется, функция может возвратить значение, которое затем может быть использовано для изменения x, но самостоятельно изменить напрямую параметр, переданный ей по значению, функция не может.

Традиционный метод С для изменения x состоит в использовании в качестве фактического аргумента &x, то есть адреса x, а не самого значения x. Хотя &x передается по значению, функция получает доступ к x благодаря тому, что ей доступна полученная копия&x. Даже если функции не требуется изменять значения x, тем не менее полезно (хотя это чревато возможностью нежелательных побочныхэффектов) передавать &x, особенно если x представляет собой большую по размерам структуру данных. Передача x непосредственно по значению ведет к бесполезным затратам памяти на копирование такой структуры данных.

Сравним три различных реализации функции treble:

Реализация 1

int treble_1(n)

(*

return 3*n;

*)

...

int x, i = 4;

x = treble_1(i); // теперь x = 12, i = 4

...

Реализация 2

void treble_2(int* np)

(*

*np = (*np)*3;

*)

...

treble_2(int &i); // теперь i = 12

Реализация 3

void treble_3(int& n) // n имеет тип ссылки

(*

n = 3*n;

*)

...

treble_3(i); // теперь i = 36

Объявление формального аргумента type& t (или, что эквивалентно, type &t) устанавливает t как имеющую тип "ссылки на тип type". Поэтому при вызове treble_3 с действительным аргументом i, i используется для инициализации формального аргумента ссылки n. Следовательно, n играет роль алиаса i, и n = 3*n также присваивает i значение 3*i.

Если инициализатор представляет собой константуили объект нессылочного типа, то Turbo C++ создаст временный объект, для которого ссылка действует как алиас:

int& ir = 6; /* создается временный объект int, с именем алиаса ir, который получает значение 6 */

float f;

int& ir2 = f; /* создается временный объект int, с именем алиаса ir2, f перед присвоением преобразовывается */

ir2 = 2.0 // теперь ir2 = 2, но f остается без изменений

Автоматическое создание временных объектов позволяет выполнять преобразования ссылочных типов, если формальные и фактические аргументы имеют различные (но совместимые для привоения) типы. При передаче по значению, разумеется, проблем с преобразованием типов меньше, поскольку перед присвоением формальному аргументу копия фактического аргумента может быть физически изменена.

Операция доступа к контексту

Операция доступа к контексту (или разрешения контекста) :: (два двоеточия подряд) позволяет осуществлять доступ к глобальному (или с продолжительностью файла) имени даже в том случае, когда это имя скрыто локальным переобъявлением этого имени (см.стр.29).:

int i; // глобальная переменная i

...

void func(void);

(*

int i=0; // локальная i скрывает глобальную i i = 3; //

эта i - локальная

::i = 4; // эта i - глобальная

printf ("%d",i); // будет напечатано значение 3

*)

Приведенный кодработает также, если i является статической переменной уровня файла.

При использовании с типами классов операция :: имеет другой смысл; это рассматривается далее в данной главе.

Операции new и delete

Операцииnew и delete выполняют динамическое распределение и отмену распределения памяти, аналогично, но с более высоким приоритетом, нежели стандартныке библиотечные функции семейства malloc и free (См. справочник по библиотеке).

Упрощенный синтаксис:

указатель-имени = new имя <инициализатор-имени>;

delete указатель-имени;

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

new пытается создать объект с типом "имени", распределив (при возможности) sizeof(имя) байт в свободной области памяти (которую также называют "кучей"). Продолжительность существования в памяти данного объекта - от точки его создания и до тех пор, пока операция delete не отменит распределенную для него память, либо до конца работы программы.

В случае успешного завершения new возвращает указатель нового объекта. Пустой указатель означает неудачное завершение операции (например, недостаточный объем или слишком большая фрагментированность кучи). Как и в случае malloc, прежде чем пытаться обращаться к новому объекту, следует проверить укеазатель на наличие пустого значения. Однако, в отличие от malloc, new сама вычисляет размер "имени", и явно указывать операцию sizeof нет необходимости. Далее возвращаемый указатель будет иметь правильный тип, "указатель имени", без необходимости явного приведения типов.

name *nameptr // name может иметь любой тип, кроме функции

...

if (!(nameptr = new name)) (*

errmsg("Недостаточно памяти для name");

exit (1);

*)

// использование *nameptr для инициализации объекта new name

...

delete nameptr; // удаление name и отмена распределения

// sizeof(name) байтов памяти

new, будучи ключевым словом, не нуждается в прототипе.

Операция new с массивами

Если "имя" это массив, то возвращаемый new указатель указывает на первый элемент массива. При создании с помощью new многомерных массивов следует указывать все размерности массива:

mat_ptr = new int[3][10][12]; // так можно

mat_ptr = new int[3][][12]; // нельзя

mat_ptr = new int[][10][12]; // нельзя

::operator new

При использовании с объектами, не являющимися классами, new вызывает стандартную библиотечную подпрограмму, global ::operator new. Для объектов классов типа "имя" может быть определена специальная операция имя::operator new. new, применительнок объектам класса "имя", запускает соответствующую операцию имя::operator new; в противном случае используется стандартная операция ::operator new.

Инициализаторы с операцией new

Другим преимуществом операции new по сравнению с malloc является опциональный инициализатор (хотя calloc очищает его распределение к нулю). При отсутствии явных инициализаторов объект, создаваемый new, содержит непредсказуемые данные ("мусор"). Объекты, распределяемые new, за исключением массивов, могут инициализироваться соответствующим выражением в круглых скобках:

int_ptr = new int(3);

Массивы классов с конструкторами инициализируются при помощи конструктора-умолчания (см. стр.115 оригинала). Определяемая пользователем операция new с задаваемой пользователем инициализацией играет ключевую роль в конструкторах С++ для объектов типа класса.

Классы

Классы С++ предусматривают создание расширений системы предопределенных типов. Каждый тип класса представляет собой уникальное множество объектов и операций (правил), атакже преобразований, используемых для создания, манипулирования и уничтожения таких объектов. Могут быть объявлены производные классы, наследующие компоненты одного или более базовых (порождающих) классов.

В С++ структуры и объединения рассматриваются как классы с определенными умолчаниями правил доступа.

Упрощенный, в "первом приближении", синтаксис объявления класса следующий:

ключ-класса имя-класса

<:базовый-список>[<список-компонентов>]

Ключ-класса - это class, struct или union.

Опциональный базовый-список перечисляетбазовый классили классы, из которогоимя-класса берет (или наследует) объекты и правила. Если объявлены некоторые базовыеклассы, то класс "имя-класса" называется производным классом (см.стр.110, "Доступ к базовым и производным классам"). Базовый список содержит спецификаторы доступа по умолчания и опциональные их переопределения, которые могут модифицировать права доступа производного класса к компонентам базовых классов(см. стр.108 оригинала, "Управление доступом к компонентам").

Опциональный список-компонентов объявляет компоненты класса (данные и функции) для имени-класса с умолчаниями и переопределениями спецификаторов доступа, которые могут влиять на то, какие функции к каким компонентам класса могут иметь доступ.

Имена классов

Имя-класса это любой идентификатор, уникальный в пределах своего контекста. В классах структур и в объединениях имя-класса можетбыть опущено (см. "Структуры и типы, определяемые пользователем (typedef) без тегов" на стр.65 оригинала).

Типы классов

Объявление создает уникальный тип, тип класса

"имя-класса". Это позволяет вам объявлять последующие объекты класса (или вхождения класса) данного типа, а также объекты, являющиеся производными от этого типа (такие как указатели, ссылки, массивы объектов "имя-класса" и т.д.):

class X (* ... *);

X x, &xr, *xptr, xarray[10];

/* четыре объекта: типа X, ссылка на X, указатель на X и масив элементов типа X */

struct Y (* ... *);

Y y, &yr, *yptr, yarray[10];

// С позволяет иметь

// struct Y y, &yr, *yptr, yarray[10];

union Z (* ... *);

Z z, &zr, *zptr, zarray[10];

// С позволяет иметь

// union Z z, &zr, *zptr, zarray[10];

Отметим различие между объявлением структур и объединений в С и С++: в С ключевые слова struct и union обязательны, но в С++ они нужны только в том случае, когда имена классов, Y и Z, скрыты (см. следующий раздел).

Контекст имени класса

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

struct S (* ... *);

int S(struct S *Sptr);

void func(void)

(*

S t; // недопустимое объявление: нет ключа класса // и функции S в контексте struct S s; // так можно: есть уточнение ключем класса

S(&s); // так можно: это вызов функции

*)

С++ позволяет также неполное объявление класса:

class X;// еще нет компонентов !

struct Y;

union Z;

Неполные объявления позволяют некоторые ссылки к именам классов X, Y или Z (обычно ссылки на указатели объектов классов) до того, как классы будут полностью определены (см. "Объявление компонентов структуры" на стр.65 оригинала). Разумеется, прежде чем вы сможете объявить и использовать объекты классов, вы должны выполнить полное объявление классов со всеми их компонентами.

Объекты классов

Объекты классов могут быть присвоены (если не было запрещено копирование), переданы как аргументы функции, возвращены фунукцией (за некоторыми исключениями) и т.д. Прочие операции с объектами и компонентами классовмогут быть различными способами определены пользователем, включая функциикомпоненты и "друзья", а также переопределение стандартных функций и операций при работе с объектами конкретного класса. Переопределение функций и операций называется перегрузкой. Операции и функции, ограниченные объектами конкретного класса (или взаимосвязанной группы классов) называются функциям-компонентами данного класса. С++ имеет механизм, позволяющий вызвать то же имя функции или операции для выполнения другой задачи, в зависимости от типа или числа аргументов или операндов.

Список компонентов класса

Опциональный список-компонентов представляет собой последовательность объявлений данных (любого типа, включая нумераторы,битовыеполя и другие классы) и объявлений и определений функций, каждое из которых может иметьопциональные спецификаторы класса памяти и модификаторы доступа. Определенные таким образом объекты называются компонентами класса. Спецификаторы класса памяти auto, extern и register в данном случвае недопустимы. Компоненты могут быть объявлены со спецификаторами класса памяти static.

Функции-компоненты

Функция, объявленная без спецификатораfriend, называется функцией-компонентом класса. Функции, объявленные с модификатором friend, называется "функцией-другом".

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

Ключевое слово this

Не-статические функции компонентов работают с объектами типа класса, с которыми они вызываются. Например, если xэто объект класса X, а f это функция-компонент X, то вызов функци x.f() работает с x. Аналогичным образом, если xptr есть указатель объекта X, то вызов функции xptr->() работает с *xptr. Откуда f может знать, с каким x работать? С++ передает f указатель на x, называемый this. this передается как скрытый аргумент при вызове не-статических функций-компонентов.

Ключевоеслово this представляет собой локальную переменную, доступную в теле не-статической функции-компонента. this не требует объявлений, и на него редко встречаются явные ссылки в определении функции. Однако, оно неявно используется в функции для ссылки ккомпонентам. Если, например, вызывается x.f(y), где y есть компонент X, то this устанавливается на &x, а y устанавливается на this->y, что эквивалентно x.y.

Встраиваемые функции (inline)

Функция-компонента может быть объявленав пределах своего класса, но определена где-либо в другом месте. И наоборот, функция-компонента может быть и объявлена, и определена в своем классе, и тогда она называется встраиваемой функцией. (Некоторые примеры таких функций показаны в главе 5 документа "Начало работы").

В некоторых случаях Turbo C++ может уменьшить затраты времени на вызов функции, подстанавливая вместо вызова функции непосредственно компилированный код тела функции. Этот процесс, называемый встраиваемым расширением тела функции, не влияет на контекст имени функции или ее аргументов. Встраиваемое расширение не всегда полезно и желательно. Спецификатор inline представляет собой запрос (или требование) компилятору, в котором вы сообщаете, что встраиваемые расширения желательны. Как и в случае спецификатора класса памяти register, компилятор может либо удовлетворить, либо проигнорировать ваше пожелание.

Явные или неявные запросы inlineлучше всего резервировать для небольших по объему и часто вызываемых функций, таких как функции типа operator, реализующих перегруженные операторы. Например, следующее объявление класса:

int i; // global int

class X (*

public:

char* func(void) (* return i; *) // inline по умолчанию char* i;

*);

эквивалентно

inline char* X::func(void) (*return i; *)

func определяется "вне" класса с явным спецификатором inline. Переменная i, возвращаемая func, есть char* i класса X - см. раздел, посвященный контексту компонентов на стр.107 оригинала.

Статические компоненты

Спецификатор класса памяти static может быть использован в объявлениях компонентов данных и функций-компонентов класса. Такие компоненты называются статическими и имеют свойства, отличные от свойств не-статических компонентов.В случае не-статических компонентов для каждого объекта класса "существует" отдельная копия; в случае же статических компонентов существует только одна копия, а доступ к ней выполняется без ссылки на какой-либо конкретный объект класса. Если х это статический компонент класса Х, то к нему можно обратиться как Х::х (даже если объекты класса Х еще не созданы). Однако, можно выполнить доступ к х и при помощи обычных операций доступа к компонентам. Например, в виде y.x и yptr->x, гдеy это объект класса X, а yptr это указатель объекта класса X, хотя выражения y и yptr еще не вычислены. В частности, статическая функция-компонент может быть вызвана как с использованием специального синтаксиса вызова функций компонентов, так и без него.

class X (*

int member_int;

public:

static void func(int i, X* ptr);

*);

void g(void);

(*

X obj;

func(1, &obj); // ошибка, если где-нибудь еще

// не определена глобальная func()

X::func(1, &obj); // вызов static func() в X // допустим только для статических функций

obj.func(1, &obj); // то же самое (допустимо как для стати// ческих, так и не-статических функций)

*)

Поскольку статическая функция-компонент может вызываться без учета какого-либо конкретногообъекта, она не имеет указателя this. Следствие из этого таково, что статическая функция-компонент не имеет доступа к не-статическим компонентам без явного задания объекта при помощи .или ->. Например, сучетомобъявлений, сделанных впредыдущем примере, func может быть определена следующим образом:

void X%%func(int i, X* ptr)

(*

member_int = i; // на какой объект ссылается

// member_int? Ошибка !

ptr->member_int = 1; // так можно: теперь мы знаем! *)

Без учета встраиваемых функций,статические функции-компоненты глобальных классов имеют внешнийтип компоновки. Статические функции-компоненты не могут являться виртуальными функциями. Недопустимо иметь статическую и не-статическую функции-компоненты с одинаковыми именами и типами аргументов.

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

Статические компоненты класса,объявленного локальным по отношению к некоторой функции, не имеют типа компоновки и не могут быть инициализированы. Статические компоненты глобального класса могут быть инициализированы подобно обычным глобальным объектам, но только вконтексте файла. Статические компоненты подчиняются обычным правилам доступа к компонентам класса, за исключением того, что они могут быть инициализированы.

class X (*

...

static int x;

...

*); int X::x = 1;

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

- уменьшения числа видимых глобальных имен

- того, чтобы сделать очевидным, какие именно статические объекты какому классу принадлежат

- разрешения управления доступам к их именам.

Контекст компонента

Выражение X::func() в примере, приведенномна стр.106 оригинала, используетимя класса Xс модификатором контекста доступа, обозначающим, что func, хотя и определена "вне" класса, в действительности является функцией-компонентом Х и существует в контексте Х. Влияние Х:: распространяется на тело определения этой функции. Это объясняет, почему возвращаемое функциейзначение i относится к X::i, char* i из Х, а не к глобальной переменной int i. Без модификатора Х:: функция func представляла бы обычную, не относящуюся к классу функцию, возвращающую глобальную переменную int i.

Следовательно, все функции-компоненты находятся в контексте своего класса, даже если они определены вне этого класса.

К компонентам данных класса Х можно обращаться при помощи операций выбора . и -> (как и в структурах С). Функциикомпоненты можновызывать также при помощи операций выбора (см. "Ключевое слово this на стр.105 оригинала). Например,

class X (*

public:

int i;

char name[20];

X *ptr1;

X *ptr2;

void Xfunc(char *data, X* left, X* right); // определение // находится не здесь

*);

void f(void);

(*

X x1, x2, *xptr=&x1; x1.i = 0; x2.i = x1.i; xptr->i = 1; x1.Xfunc("stan", &x2, xptr);

*)

Если m является компонентом или базовым компонентом класса Х, то выражение Х::m называется квалифицированным именем; оно имеет тот же тип, что и m, и является именующим выражением только в том случае, если именующим выражением является m. Ключевой момент здесьсостоит в том, что даже если имя класса Х скрыто другим именем, квалифицированное имя Х::m тем не менее обеспечит доступ к нужному имени класса, m.

Компоненты класса не могут быть добавлены к нему в другом разделе вашейпрограммы. Класс Х не может содержать объекты класса Х, но может содержать указатели или ссылки на объекты класса Х (отметим аналогию с типами структур и объединений С).

Управление доступом к компонентам

Компоненты класса получают атрибуты доступа либо по умолчанию (взависимости от ключакласса и местоположения объявления), либо при использовании какого-либо из спецификаторов доступа: public, private или protected. Значение этих атрибутов следующие:

public Компонент может быть использован любой функцией.

private Компонент может быть использован только функциями компонентами и "друзьями" класса, в котором он объявлен.

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

Спецификаторы доступа не влияют на объявления функций типа friend. (См. раздел "Друзья" классов" на стр.112 оригинала).

Компоненты класса по умолчанию имеют атрибут 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.


Информация о работе «Turbo C++ Programer`s guide»
Раздел: Информатика, программирование
Количество знаков с пробелами: 668870
Количество таблиц: 13
Количество изображений: 0

Похожие работы

Скачать
14945
0
7

... к сожалению, обратное утверждение не верно. C++ Builder содержит инструменты, которые при помощи drag-and-drop действительно делают разработку визуальной, упрощает программирование благодаря встроенному WYSIWYG - редактору интерфейса и пр. Delphi — язык программирования, который используется в одноимённой среде разработки. Сначала язык назывался Object Pascal. Начиная со среды разработки Delphi ...

Скачать
225728
6
0

... ориентированы на 32 разрядные шинные архитектуры компьютеров с процессорами 80386, 80486 или Pentium. Фирма Novell также подготовила варианты сетевой ОС NetWare, предназначенные для работы под управлением многозадачных, многопользовательских операционных систем OS/2 и UNIX. Версию 3.12 ОС NetWare можно приобрести для 20, 100 или 250 пользователей, а версия 4.0 имеет возможность поддержки до 1000 ...

Скачать
49224
0
9

... завдання поширюється на розробку системи обліку зареєстрованих автомобілів в ДАІ, призначеної для збору, зберігання, а також полегшення для доступу та використання інформації. Програма з обліку зареєстрованих автомобілів в ДАІ, представляє собою, перехід від паперових носіїв інформації до електронних. Система обліку зареєстрованих автомобілів значно допоможе працівникам ДАІ з обліку, аналізу та ...

Скачать
132529
1
5

... меньше времени и ответ клиенту агентство может дать уже в день подачи заявки. Каждая турфирма разрабатывает индивидуальный образец листа бронирования. Согласно Федеральному Закону «Об основах туристской деятельности в Российской Федерации» (гл. IV, ст. 9) – это конкретный заказ туриста или лица, уполномоченного представлять группу туристов, туроператору на формирование туристского продукта. ...

0 комментариев


Наверх