Тип параметра результирующей функции указатель на есть (...), то
есть, неизвестен (#8.4). Любое использование его является
зависимым от реализации, поскольку способ инициализации указателя
для вызова функции члена неопределен.
8.5.3 Производные классы
В конструкции
агрег идентификатор:public opt typedef-имя
typedef-имя должно означать ранее описанный класс, называемый
базовым классом для класса, подлежащего описанию. Говорится, что
последний выводится из предшествующего. На члены базового класса
можно ссылаться, как если бы они были членами производного класса,
за исключением тех случаев, когда имя базового члена было
переопределено в производном классе; в этом случае для ссылки на
скрытое имя может использоваться такая запись (#7.1):
typedef-имя :: идентификатор
Например:
- стр 285 -
struct base
{
int a;
int b;
};
struct derived : public base
{
int b;
int c;
};
derived d;
d.a = 1;
d.base::b = 2;
d.b = 3;
d.c = 4;
осуществляет присваивание четырем членам d.
Производный тип сам может использоваться как базовый.
8.5.4 Виртуальные функции
Если базовый класс base содержит (виртуальную) virtual (#8.1)
функцию vf, а производный класс derived также содержит функцию vf,
то вызов vf для объекта класса derived вызывает derived::vf.
Например:
struct base
{
virtual void vf ();
void f ();
};
struct derived : public base
{
void vf ();
void f ();
};
derived d;
base* bp = &d;
bp->vf ();
bp->f ();
Вызовы вызывают, соответственно, derived::vf и base::f для
объекта класса derived, именованного d. Так что интерпретация
вызова виртуальной функции зависит от типа объекта, для которого
она вызвана, в то время как интерпретация вызова невиртуальной
функции зависит только от типа указателя, обозначающего объект.
Из этого следует, что тип объектов классов с виртуальными
функциями и объектов классов, выведенных из таких классов, могут
быть определены во время выполнения.
- стр 286 -
Если производный класс имеет член с тем же именем, что и у
виртуальной функции в базовом классе, то оба члена должны иметь
одинаковый тип. Виртуальная функция не может быть другом (friend)
(#8.5.9). Функция f в классе, выведенном из класса, который имеет
виртуальную функцию f, сама рассматривается как виртуальная.
Виртуальная функция в базовом классе должна быть определена.
Виртуальная функция, которая была определена в базовом классе, не
нуждается в определении в производном классе. В этом случае
функция, определенная для базового класса, используется во всех
вызовах.
8.5.5 Конструкторы
Член функция с именем, совпадающим с именем ее класса, называется
конструктором. Конструктор не имеет типа возвращаемого значения; он
используется для конструирования значений с типом его класса. С
помощью конструктора можно создавать новые объекты его типа,
используя синтаксис
typedef-имя ( список_параметров opt )
Например,
complex zz = complex (1,2.3);
cprint (complex (7.8,1.2));
Объекты, созданные таким образом, не имеют имени (если
конструктор не использован как инициализатор, как это было с zz
выше), и их время жизни ограничено областью видимости, в которой
они созданы. Они не могут рассматриваться как константы их типа.
Если класс имеет конструктор, то он вызывается для каждого объекта
этого класса перед тем, как этот объект будет как-либо использован;
см. #8.6.
Консруктор может быть overload, но не virtual или friend.
Если класс имеет базовый класс с конструктором, то конструктор
для базового класса вызывается до вызова конструктора для
производного класса. Конструкторы для объектов членов, если таковые
есть, выполняются после конструктора базового класса и до
конструктора объекта, содержащего их. Объяснение того, как могут
быть специфицированы параметры для базового класса, см. в #8.6.2, а
того, как конструкторы могут использоваться для управления
свободной памятью, см. в #17.
8.5.6 Преобразования
Конструктр, получающий один параметр, определяет преобразование
из типа своего параметра в тип своего класса. Такие преобразования
неявно применяются дополнительно к обычным арифметическим
пробразованиям. Поэтому присваивание объекту из класса X допустимо,
если или присваиваемое значение является X, или если X имеет
конструктор, который получает присваиваемое значение как свой
единственный параметр. Аналогично конструкторы используются для
преобразования параметров функции (#7.1) и инициализаторов (#8.6).
Например:
- стр 287 -
class X { ... X (int); };
f (X arg)
{
X a = 1; /* a = X (1) */
a = 2; /* a = X (2) */
f (3); /* f (X (3)) */
}
Если для класса X не найден ниодин конструктор, принимающий
присваиваемый тип, то не делается никаких попыток отыскать
конструктор для преобразования присваиваемого типа в тип, который
мог бы быть приемлем для конструкторов класса X. Например:
class X { ... X (int); };
class X { ... Y (X); };
Y a = 1; /* недопустимо: Y (X (1)) не пробуется */
8.5.7 Деструкторы
Функция член класса cl с именем ~cl называется деструктором.
Деструктор не возвращает никакого значения и не получает никаких
параметров; он используется для уничтожения значений типа cl
непосредственно перед уничтожением содержащего их объекта.
Деструктор не может быть overload, virtual или friend.
Деструктор для базового класса выполняется после деструктора
производного от него класса. Как деструкторы используютя для
управления свободной памятью, см. объяснение в #17.
8.5.8 Видимость имен членов
Члены класса, описанные с ключевым словом class, являются
закрытыми, это значит, что их имена могут использоваться только
функциями членами (#8.5.2) и друзьями (см. #8.5.10), пока они не
появятся после метки public: . В этом случае они являются общими.
Общий член может использоваться любой функцией. Структура является
классом, все члены которого общие; см. #8.5.11.
Если перед именем базового класса в описании производного класса
стоит ключевое слово public, то общие члены базового класса
являются общими для производного класа; если нет, то они являются
закрытыми. Общий член mem закрытого базового класса base может быть
описан как общий для производного класса с помощью опиисания вида
typedef-имя . идентификатор;
в котором typedef-имя означает базовый класс, а идентификатор есть
имя члена базового класса. Такое описание может появляться в общей
части производного класса.
Рассмотрим
- стр 288 -
class base
{
int a;
public:
int b,c;
int bf ();
};
class derived : base
{
int d;
public:
base.c;
int e;
int df ();
};
int ef (derived&);
Внешняя функция ef может использовать только имена c, e и df.
Являясь членом derived, функция df может использовать имена b, c,
bf, d, e и df, но не a. Являясь членом base, функция bf может
использовать члены a, b, c и bf.
8.5.9 Друзья (friends)
Другом класса является функция не-член, которая может
использовать имена закрытых членов. Следующий пример иллюстрирует
различия между членами и друзьями:
class private
{
int a;
friend void friend_set (private*,int);
public:
void member_set (int);
};
void friend_set (private* p,int i) { p->a=i; }
void private.member_set (int i) { a = i; }
private obj;
friend_set (&obj,10);
obj.member_set (10);
Если описание friend отностися к перегруженному имени или
операции, то другом становится только функция с описанными типами
параметров. Все функции класса cl1 могут быть сделаны друзьями
класса cl2 с помощью одного описания
- стр 289 -
class cl2
{
friend cl1;
. . .
};
8.5.10 Функция операция
Большинство операций могут быть перегружены с тем, чтобы они
могли получать в качестве операндов объекты класса.
имя_функции_операции: operator op
op: + - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> <<= >>= == !=
<= >= && || ++ -- () []
Последние две операции - это вызов функции и индексирование.
Функция операция может или быть функцией членом, или получать по
меньшей мере один параметр класса. См. также #7.16.
8.5.11 Структуры
Структура есть класс, все члены которого общие. Это значит, что
struct s { ... };
эквивалентно
class s { public: ... };
Структура может иметь функции члены (включая конструкторы и
деструкторы).
8.5.12 Объединения
Объединение можно считать структурой, все объекты члены которой
начинаются со смещения 0, и размер которой достаточен для
содержания любого из ее объектов членов. В каждый момент времени в
объединеии может храниться не больше одного из объектов членов.
Объединение может иметь функции члены (включая конструкторы и
деструкторы).
8.5.13 Поля бит
Описатель члена вида
идентификатор opt: константное_выражение
определяет поле; его длина отделяется от имени поля двоеточием.
Поля упаковываются в машинные целые; они не являются альтернативой
слов. Поле , не влезающее в оставшееся в целом место, помещается в
следующее слово. Поле не может быть шире слова. На некоторых
машинах они размещаются справа налево, а на некоторых слева
направо; см. #2.6.
- стр 290 -
Неименованные поля полезны при заполнении для согласования внешне
предписанных размещений (форматов). В особых случаях неименованные
поля длины 0 задают выравнивание следующего поля по границе слова.
Не требуется аппаратной поддержки любых полей, кроме целых. Более
того, даже целые поля могут рассматриваться как unsigned. По этим
причинам рекомендуется описывать поля как unsigned. К полям не
может применяться операция получения адреса &, поэтому нет
указателей на поля.
Поля не могут быть членами объединения.
8.5.14 Вложенные классы
Класс может быть описан внутри другого класса. В этом случае
область видимости имен внутреннего класса его и общих имен
ограничивается охватывающим классом. За исключением этого
ограничения допустимо, чтобы внутренний класс уже был описан вне
охватывающего класса. Описание одного класса внутри другого не
влияет на правила доступа к закрытым членам и не помещает функции
члены внутреннего класса в область видимости охватывающего класса.
Например:
int x;
class enclose /* охватывающий */
{
int x;
class inner
{
int y;
f () { x=1 }
...
};
g (inner*);
...
};
int inner; /* вложенный */
enclose.g (inner* p) { ... }
В этом примере x в f ссылается на x, описанный перед классом
enclose. Поскольку y является закрытым членом inner, g не может
его использовать. Поскольку g является членом enclose, имена,
использованные в g, считаются находящимися в области видимости
класса enclose. Поэтому inner в описании параметров g относится к
охваченному типу inner, а не к int.
8.6 Инициализация