например:

class X {
public:
void f();
};

class Y : private X {
public:
void f(int);
X::f; // ошибка: два описания f
};

    R.11.4 Друзья



Другом класса называется функция, которая не является членом класса,
но в которой можно использовать частные и защищенные члены этого
класса. Имя друга не принадлежит области видимости класса, и
дружественная функция не вызывается с помощью операций доступа к
членам ($$R.5.2.4), если только она не является членом другого
класса. Следующий пример показывает различие между членами и
друзьями:

class X {
int a;
friend void friend_set(X*, int);
public:
void member_set(int);
};

void friend_set(X* p, int i) { p->a = i; }
void X::member_set(int i) { a = i; }

void f()
{
X obj;
friend_set(&obj,10);
obj.member_set(10);
}

Если в описании friend использовано имя перегруженной функции
или операции, только функция, однозначно определяемая типами
формальных параметров, становится другом. Функция-член класса X
может быть другом класса Y, например:

class Y {
friend char* X::foo(int);
// ...
};

Можно объявить все функции класса X друзьями класса Y с помощью
спецификации-сложного-типа ($$R.9.1):

class Y {
friend class X;
// ...
};

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

class X {
enum { a=100 };
friend class Y;
};

class Y {
int v[X::a]; // Y друг класса X
};

class Z {
int v[X::a]; // ошибка: X::a недоступно
};

Если класс или функция, объявленные как друзья, не были описаны,
их имена попадают в ту же область видимости, что и имя класса,
содержащего описание friend ($$R.9.1).
Функция, появившаяся первый раз в описании friend, считается
эквивалентной функции, описанной как extern ($$R.3.3, $$r.7.1.1).
Если функция-друг определена в описании класса, она считается
функцией со спецификацией inline и к ней применимо правило
переноса определения функции для функций-членов ($$R.9.3.2).
Функция-друг, определенная в описании класса, относится на
лексическом уровне к области видимости этого класса. Для
функции-друга, определенной вне класса, это не так.
На описание friend не влияет указание спецификаций-доступа
($$R.9.2).
Понятие дружбы не является ни наследуемым, ни транзитивным.
Подтвердим это примером:

class A {
friend class B;
int a;
};

class B {
friend class C;
};

class C {
void f(A* p);
{
p->a++; // ошибка: C не друг класса A, хотя
// является другом друга класса A
}
};

class D : public B {
void f(A* p)
{
p->a++; // ошибка: D не друг класса A, хотя
// является производным друга класса A
}
};

    R.11.5 Доступ к защищенным членам



Друг или функция-член производного класса имеет доступ к защищенному
статическому члену базового класса. Друг или функция-член производного
класса могут получить доступ к защищенному нестатическому члену
одного из своих базовых классов только через указатель, ссылку или
объект производного класса (или любого класса, являющегося
производным по отношению к нему). Рассмотрим пример:

class B {
protected:
int i;
};

class D1 : public B {
};

class D2 : public B {
friend void fr(B*, D1*, D2*);
void mem(B*, D1*);
};

void fr(B* pb, D1* p1, D2* p2)
{
pb->i = 1; // недопустимо
p1->i = 2; // недопустимо
p2->i = 3; // нормально (обращение через D2)
}

void D2::mem(B* pb, D1* p1)
{
pb->i = 1; // недопустимо
p1->i = 2; // недопустимо
i = 3; // нормально (обращение через this)
}

void g(B* pb, D1* p1, D2* p2)
{
pb->i = 1; // недопустимо
p1->i = 2; // недопустимо
p2->i = 3; // недопустимо
}

    R.11.6 Доступ к виртуальным функциям



Правила доступа ($$r.11) к виртуальной функции определяются ее
описанием и на них не влияют правила доступа к к функции, которая
позднее будет подавлять ее. Приведем пример:

class B {
public:
virtual f();
};

class D : public B {
private:
f();
};

void f()
{
D d;
B* pb = &d;
D* pd = &d;

pb->f(); // нормально: B::f() общий член
// вызывается D::f()
pd->f(); // ошибка: D::f() частный член
}

Права доступа проверяются при самом вызове, используя тип выражения,
обозначающее объект, для которого вызывается функция-член (в примере
выше это B*). Доступ к функции-члену в классе, где она определена
(D в примере выше), в общем случае неизвестен.

    R.11.7 Множественный доступ



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

class W { public: void f(); };
class A : private virtual W { };
class B : public virtual W { };
class C : public A, public B {
void f() { W::f(); } // нормально
};

Поскольку W::f() доступно в C::f() по пути, связанному с общим
наследованием из B, обращение является законным.

    R.12 Специальные функции-члены



Некоторые функции-члены считаются специальными, поскольку они
влияют на то, как объекты класса создаются, копируются и уничтожаются,
и как значения одного типа преобразуются в значения другого типа.
Часто такие функции вызываются неявно.
Эти функции-члены подчиняются обычным правилам доступа ($$R.11).
Например, описание конструктора со спецификацией protected
гарантирует, что создавать объекты с его помощью смогут только
производные классы и друзья.

    R.12.1 Конструкторы



Конструктором называется функция-член, имя которой совпадает с именем
класса, он используется для построения значений, имеющих тип данного
класса. Если в классе есть конструктор, то каждый объект этого класса
перед произвольным использованием будет инициализироваться, см.
$$R.12.6.
Конструктор может вызываться для объекта со спецификацией const
или volatile. Сам конструктор нельзя описывать со спецификацией
const или volatile ($$R.9.3.1). Конструктор также не может иметь
спецификацию virtual или static.
Конструкторы не наследуются, однако, стандартные конструкторы
и конструкторы копирования при необходимости создаются транслятором
($$R.12.8). Такие конструкторы являются общими.
Стандартным конструктором для класса X является такой конструктор
класса X, который можно вызывать без параметров. Стандартный
конструктор для класса X будет создан только тогда, когда для класса
X не описано ни одного конструктора.
Конструктором копирования для класса X называется конструктор,
который вызывается для копирования объекта класса X, т.е. вызывается
с одним параметром типа X. Например, X::X(const X&) и
X::X(X&, int=0) являются конструкторами копирования. Конструктор
копирования создается только тогда, когда не описано ни одного
конструктора копирования.
Конструктор копирования для класса X не должен иметь в качестве
параметра объект типа X, например X::X(X) незаконное обращение.
Конструктор для массива элементов вызывается в порядке
возрастания адресов элементов ($$R.8.2.4).
Если у класса есть базовые классы с конструктором или члены,
являющиеся объектами с конструктором, их конструкторы вызываются
прежде, чем конструктор производного класса. В $$R.12.6.2 объясняется
как задаются параметры для таких конструкторов и как определяется
порядок их вызова.
Объект класса с конструктором не может быть членом объединения.
Для конструктора не нужно указывать никакого типа возвращаемого
значения, даже void. В операторе return в теле конструктора нельзя
указывать возвращаемое значение. Не допустима операция взятия
адреса конструктора.
Конструктор можно явно использовать для создания объектов его
типа с помощью следующей записи:
имя-класса ( список-выражений opt )
Приведем пример:

complex zz = complex(1,2.3);
print( complex(7.8,1.2) );

Объект, созданный таким образом является безымянным (если только
конструктор не использовался для инициализации поименованной переменной
как zz выше), а время его жизни ограничено выражением, в котором
он был создан, см. $$R.12.2.
В конструкторе можно вызывать функцию-член, см. $$R.12.7.

    R.12.2 Временные объекты



В некоторых ситуациях транслятору бывает необходимо или удобно
создавать временные объекты. Использование временных объектов
зависит от реализации. Если транслятору понадобился временный
объект типа класса с конструктором, он должен обеспечить вызов
конструктора для этого временного объекта. Аналогично, необходимо
вызывать деструктор для объекта класса, в котором описан
деструктор. Приведем пример:

class X {
// ...
public:
// ...
X(int);
X(X&);
~X();
};

X f(X);

void g()
{
X a(1);
X b = f(X(2));
a = f(b);
}

Здесь нужен временный объект для построения X(2), прежде чем
передать его функции f() с помощью X(X&). Альтернативное решение, -
построить объект X(2) в памяти, используемой для хранения параметра
при первом вызове f(). Помимо этого, временный объект может
понадобиться для хранения результата f(X(2)) прежде, чем копировать
его в объект b с помощью X(X&), и здесь возможно альтернативное
решение: хранить результат f(X(2)) в памяти для объекта b. С другой
стороны, существует много функций f(), для которых выполнение
выражения a=f(a) требует временного объекта или для параметра a,
или для результата f(a), чтобы избежать нежелательного использования
памяти, которой приписывается имя a.
Транслятор обязан гарантировать уничтожение временных объектов.
Точный момент уничтожения определяется реализацией. С временными
объектами можно производить только две операции: выбрать значение
объекта (неявно копируя его) для использования в другом выражении,
или взять ссылку на него. Если значение временного объекта получено,
он считается ненужным и может уничтожаться немедленно. Если на него
получена ссылка, то уничтожать его нельзя, пока существует ссылка.
Уничтожение должно произойти до выхода из области определенности,
в которой был создан временный объект.
Другой вид временных объектов обсуждается в $$R.8.4.3.

    R.12.3 Преобразования



Преобразования объектов класса можно задать с помощью конструкторов
или функций преобразования.
Такие преобразования, обычно называемые пользовательскими,
используются неявно в совокупности со стандартными преобразованиями
($$R.4). Например, функцию с формальным параметром типа X можно
вызывать не только с параметром типа X, но и параметром типа T,
если существует преобразование типа T в X. Пользовательские
преобразования применяются в тех же ситуациях, что и стандартные:
преобразование инициализаторов ($$R.8.4), параметров функции
($$R.5.2.2), возвращаемых функцией значений ($$R.6.6.3, $$R.8.2.5),
выражений фактических параметров ($$R.5), выражений, управляющих
циклом и выбором операторов ($$R.6.4,$$R.6.5) и явные операции
преобразования типа ($$R.5.2.3, $$R.5.4).
Пользовательские преобразования применяются только в случае
их однозначности ($$R.10.1.1, $$R.12.3.2). Преобразования проходят
проверку на соответствие правилам доступа ($$R.11). Как всегда
проверка доступа осуществляется после разрешения неоднозначности
($$R.10.4).
Применение преобразований при вызове функции рассматривается на
примерах, приведенных ниже, а также обсуждается в $$R.13.2.

    R.12.3.1 Преобразование с помощью конструктора



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

class X {
// ...
public:
X(int);
X(const char*, int = 0);
};

void f(X arg) {
X a = 1; // a = X(1);
X b = "Jessie"; // b = X("Jessie",0)
a = 2; // a = X(2)
f(3); // f(X(3))
}

Если в классе X нет конструктора, который допускает заданный тип,
не делается попытки найти какой-либо конструктор другого класса или
функцию преобразования для приведения заданного значения в значение
типа,допустимого для конструктора класса X, например:

class X { /* ... */ X(int); };
class Y { /* ... */ Y(X); };
Y a = 1; // недопустимо: преобразование Y(X(1))
// не применяется

    R.12.3.2 Функции преобразования



Функция-член класса X, имя которой имеет вид,

имя-функции-преобразования:
operator имя-типа-преобразования

имя-типа-преобразования:
список-спецификаций-типа opt операция-ptr opt

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

class X {
// ...
public:
operator int();
};

void f(X a)
{
int i = int(a);
i = (int)a;
i = a;
}

Здесь во всех трех операторах присваиваемое значение будет
преобразовываться с помощью функции X::operator int(). Пользовательские
преобразования не ограничиваются только использованием в присваивании
и инициализации, например:

void g(X a, X b)
{
int i = (a) ? 1+a : 0;
int j = (a&&b) ? a+b : i;
if (a) { // ...
}
}

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

class X {
// ...
public:
operator int();
};

class Y {
// ...
public:
operator X();
};

Y a;
int b = a; // недопустимо: преобразование
// a.operator X().operator int() не применяется
int c = X(a); // нормально: a.operator X().operator int()

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

class X {
public:
// ...
operator int();
};

class Y : public X {
public:
// ...
operator void*();
};

void f(Y& a)
{
if (a) { // ошибка: неоднозначность
}
}

    R.12.4 Деструкторы



Деструктором называется функция-член класса cl с именем ~cl, она
используется для уничтожения значений типа cl непосредственно перед
уничтожением объекта, содержащего их. Деструктор не имеет формальных
параметров и для него нельзя задать тип возвращаемого значения
(даже void). Нельзя применять операцию взятия адреса для деструктора.
Можно вызывать деструктор для объектов со спецификацией const или
volatile, но сам деструктор нельзя описывать с этими спецификациями
($$R.9.3.1). Деструктор не может быть и статическим.
Деструкторы не наследуются. Если базовый класс или член имеют
деструктор, а сам производный класс - нет, то создается стандартный
деструктор, который вызывает деструкторы базовых классов и членов
производного класса. Такие созданные деструкторы имеют спецификацию
public.
Тело деструктора выполняется прежде деструкторов для объектов,
являющихся членами. Деструкторы для нестатических объектов, являющихся
членами, выполняются прежде, чем деструкторы для базовых классов.
Деструкторы для невиртуальных базовых классов выполняются прежде,
чем деструкторы для виртуальных базовых классов. Деструкторы для
невиртуальных базовых классов выполняются в порядке, обратном их
описанию в производном классе. Деструкторы виртуальных базовых
классов выполняются в порядке, обратном появлению их при обходе
снизу и слева-направо ацикличного направленного графа базовых
классов. Здесь "слева-направо" означает порядок появления имен
базовых классов, который был при описании их в производном классе.
Деструкторы для элементов массива вызываются в порядке,
обратном вызовам при их построении.
Деструктор может быть виртуальным.
В деструкторе можно вызывать функцию-член, см. $$R.12.7.
Объект класса с деструктором не может быть членом объединения.
Деструкторы вызываются неявно в следующих случаях:
(1) когда исчезают из области видимости объекты auto ($$R.3.5)
или временные объекты ($$R.12.2, $$R.8.4.3);
(2) при завершении программы ($$R.3.4) для построенных статических
объектов ($$R.3.5);
(3) благодаря обращению к операции delete ($$R.5.3.4) для объектов,
созданных с помощью операции new ($$R.5.3.3);
(4) при явном вызове.
Когда деструктор вызывается операцией delete, то он освобождает
память для самого большего из производных классов ($$R.12.6.2) того
объекта, который использовал операцию delete() ($$R.5.3.4),
например:

class X {
// ...
public:
X(int);
~X();
};

void g(X*);

void f() // общий случай
{
X* p = new X(111); // размещение и инициализация
g(p);
delete p; // освобождение и удаление
}

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

void* operator new(size_t, void* p) { return p; }

void f(X* p);

static char buf[sizeof(X)];

void g() // редкий, специальный случай
{
X* p = new(buf) X(222); // размещение в buf[] и инициализация
f(p);
p->X::~X(); // удаление
}

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

int* p;
// ...
p->int::~int();

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

    R.12.5 Свободная память



Когда создается объект с помощью операции new, для получения свободной
памяти вызывается (неявно) функция operator new() ($$R.5.3.3).
Если функция operator new() не может выполнить запрос, она
возвращает 0.
В классе X функция X::operator new() является статическим членом,
даже если она не описана явно как static. Первый ее параметр должен
иметь тип size_t, - зависящий от реализации целочисленный тип,
который определен в стандартном заголовочном файле <stddef.h>, и
она должна возвращать значение типа void*, например:

class X {
// ...
void* operator new(size_t);
void* operator new(size_t, Arena*);
};

Правила выбора подходящей функции operator new() обсуждаются
в $$R.5.3.3.
В классе X функция X::operator delete() является статическим
членом, даже если она не описана явно как static. Первый ее параметр
должен быть типа void* и можно добавлять второй параметр типа
size_t. Она не может возвращать какое-либо значение и тип
возвращаемого значения должен быть void, например:

class X {
// ...
void operator delete(void*);
};

class Y {
// ...
void operator delete(void*, size_t);
};

В каждом классе можно описать только одну функцию operator delete(),
значит эта функция не может быть перегруженной. Глобальная функция
operator delete() имеет единственный параметр типа void*.
Если функция описана с двумя формальными параметрами, она вызывается
с двумя параметрами, второй из которых показывает размер удаляемого
объекта. Передаваемый размер определяется с помощью деструктора
(если он есть) или по типу (статическому) указателя на удаляемый
объект. Операция пройдет корректно, если тип указателя, заданного
как фактический параметр, будет совпадать с типом объекта (а не будет,
к примеру, просто типом указателя на базовый класс) или, если
этот тип является типом указателя на базовый класс с виртуальным
деструктором.
Для массивов объектов типа класс используются глобальные функции
operator new() и operator delete() ($$R.5.3.3, $$R.5.3.4).
Поскольку функции X::operator new() и X::operator delete()
статические, они не могут быть виртуальными. Функция
operator delete(), которая вызывается из деструктора для освобождения
памяти, выбирается по обычным правилам областей видимости,
например:

struct B {
virtual ~B();
void* operator new(size_t);
void operator delete(void*);
};

struct D : B {
~D();
void* operator new(size_t);
void operator delete(void*);
};

void f()
{
B* p = new D;
delete p;
}

В этом примере память для объекта класса D выделяется с помощью
D::operator new(), а благодаря наличию виртуального деструктора,
освобождается с помощью D::operator delete().

    R.12.6 Инициализация



Объект класса без конструкторов, без частных или защищенных членов,
без виртуальных функций и без базовых классов можно инициализировать
с помощью списка инициализаторов ($$R.8.4.1). Объект класса с
конструктором должен инициализироваться или иметь стандартный
конструктор ($$R.12.1). Стандартный конструктор используется для
объектов, которые не проходят явной инициализации.

    R.12.6.1 Явная инициализация



Объекты классов с конструкторами ($$R.12.1) можно инициализировать
списком выражений, заключенным в скобки. Этот список считается
списком фактических параметров для вызова конструктора, производящего
инициализацию. Иначе, в качестве инициализатора задается с помощью
операции = одно значение. Оно используется как фактический параметр
для конструктора копирования. Обычно можно обойтись без вызова
конструктора копирования, например:

class complex {
// ...
public:
complex();
complex(double);
complex(double,double);
// ...
};

complex sqrt(complex,complex);

complex a(1); // инициализация вызовом
// complex(double)
complex b = a; // инициализация копированием `a'
complex c = complex(1,2); // конструктор complex(1,2)
// вызывается complex(double,double)
// и копируется в `c'
complex d = sqrt(b,c); // вызывается sqrt(complex,complex),
// результат копируется в `d'
complex e; // инициализация вызовом конструктора
complex f = 3; // complex(3), вызывается
// complex(double) и результат
// копируется в `f'

Перегрузка операции присваивания = не оказывает влияние на
инициализацию.
Инициализация, происходящая при передаче фактических параметров
и при возврате из функции, эквивалентна инициализации вида

T x = a;

Инициализация, происходящая в выражении операции new ($$R.5.3.3) и
при инициализации базовых классов и членов, эквивалентна
инициализации вида

T x(a);

Для массивов объектов класса с конструкторами используются при
инициализации ($$R.12.1) конструкторы как и для одиночных объектов.
Если оказалось, что инициализаторов в списке меньше, чем элементов
массива, используется стандартный конструктор ($$R.12.1). Если его
нет, список инициализаторов должен быть полным. Приведем пример:

complex cc = { 1, 2 }; // ошибка: необходимо
// использовать конструктор
complex v[6] = { 1,complex(1,2),complex(),2 };

Здесь v[0] и v[3] инициализируются значением complex::complex(double),
v[1] инициализируется complex::complex(double,double), а v[2],
v[4] и v[5] инициализированы complex::complex().
Объект класса M моет быть членом класса X в одном из следующих
случаев:
(1) M не имеет конструктора;
(2) M имеет стандартный конструктор;
(3) X имеет конструктор и каждый из них задает инициализатор-ctor
($$R.12.6.2) для члена M.
В случае 2 при создании составного объекта вызывается стандартный
конструктор. Если член составного объекта имеет деструктор, то он
вызывается при уничтожении составного объекта.
Конструкторы для нелокальных статических объектов вызываются в
том порядке, в каком они идут в тексте программы, деструкторы
вызываются в обратном порядке, см. также $$R.3.4, $$R.6.7,
$$R.9.4.


    R.12.6.2 Инициализация членов и базовых классов



В определении конструктора можно задать инициализацию прямых
базовых классов и членов, не наследуемых из базовых классов. Это
особенно полезно для тех объектов, констант и ссылок, для которых
различаются семантики присваивания и инициализации. Конструкция
инициализатор-ctor имеет вид

инициализатор-ctor:
: список-инициализаторов-членов

список-инициализаторов-членов:
инициализатор-члена
инициализатор-члена , список-инициализаторов-члена

инициализатор-члена:
полное-имя-класса ( список-выражений opt )
идентификатор

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

struct B1 { B1(int); /* ... */ };
struct B2 { B2(int); /* ... */ };

struct D : B1, B2 {
D(int);
B1 b;
const c;
};

D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4)
{ /* ... */ }
D d(10);

В начале инициализируются базовые классы в порядке их описания
(независимо от порядка инициализаторов-членов), затем по той же
схеме инициализируются члены, и наконец выполняется тело D::D()
($$R.12.1). Порядок описания выдерживается для того, чтобы гарантировать,
что вложенные объекты и члены будут уничтожаться в порядке, обратном
их инициализации.
Особый случай представляют виртуальные базовые классы. Они создаются
прежде, чем любой невиртуальный базовый класс и в том же порядке,
в каком появляются при обходе снизу и слева-направо ацикличного
направленного графа базовых классов. Порядок "слева-направо" - это
тот, в котором имена базовых классов задаются при описании в
производном классе.
Полным называется объект, который не является вложенным объектом,
представляющим некоторый базовый класс. Класс такого объекта
называют наибольшим производным классом объекта. Все вложенные
объекты виртуальных базовых классов инициализируются с помощью
конструктора наибольшего производного класса. Если в конструкторе
наибольшего производного класса не задан инициализатор-члена для
виртуального базового класса, тогда этот виртуальный базовый класс
должен иметь стандартный конструктор,либо не иметь никакого
конструктора. Всякий инициализатор-члена для виртуального базового
класса, заданный не в конструкторе класса полного объекта, игнорируется.
Приведем пример:

class V {
public:
V();
V(int);
// ...
};

class A : public virtual V {
public:
A();
A(int);
// ...
};

class B : public virtual V {
public:
B();
B(int);
// ...
};

class C : public A, public B, private virtual V {
public:
C();
C(int);
// ...
};

A::A(int i) : V(i) { /* ... */ }
B::B(int i) { /* ... */ }
C::C(int i) { /* ... */ }

V v(1); // use V(int)