Справочное руководство по C++ — страница 22 из 41

Деструктором называется функция-член класса 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)

A a(2); // use V(int)

B b(3); // use V()

C c(4); // use V()

Инициализатор-члена вычисляется в области видимости конструктора, в котором он появился. Например, в следующем фрагменте

class X {

 int a;

public:

 const int& r;

 X()::r(a) {}

};

X::r инициализируется для каждого объекта класса X ссылкой на X::a.

R.12.7 Конструкторы и деструкторы