15 Special member functions [special]

15.4 Destructors [class.dtor]

В объявлении деструктора declarator- это function declarator форма

ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

где ptr-declaratorсостоит исключительно из id-expression, необязательной attribute-specifier-seqи необязательной окружающих скобок, и id-expressionимеет одну из следующих форм:

Не class-nameдолжно быть typedef-name. Деструктор не должен принимать аргументы ([dcl.fct]). Каждый decl-specifierиз decl-specifier-seq декларации деструктора (если таковые имеются) должны бытьfriend,inlineили virtual.

Деструктор используется для уничтожения объектов своего типа класса. Адрес деструктора не принимается. Деструктор может быть вызван для , или объекта. и семантика ( ) не применяются к разрушаемому объекту. Они перестают действовать, когда запускается деструктор .constvolatileconst volatileconstvolatile[dcl.type.cv]most derived object

[ Note: Объявление деструктора, не имеющего a, noexcept-specifier имеет такую ​​же спецификацию исключения, как если бы оно было неявно объявлено ([except.spec]). ]end note

Если у класса нет деструктора, объявленного пользователем, деструктор неявно объявляется какdefaulted. Неявно объявленный деструктор является inline public членом своего класса.

Деструктор по умолчанию для класса X определяется как удаленный, если:

  • X - подобный объединению класс, который имеет вариантный член с нетривиальным деструктором,

  • любой потенциально созданный подобъект имеет тип класса M (или его массив) и M имеет удаленный деструктор или деструктор, недоступный из деструктора по умолчанию,

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

Деструктор тривиален, если он не предоставлен пользователем и если:

  • деструктор нетvirtual,

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

  • для всех нестатических членов данных своего класса, которые относятся к типу класса (или его массиву), каждый такой класс имеет тривиальный деструктор.

В противном случае деструктор non-trivial.

Деструктор, который установлен по умолчанию и не определен как удаленный, - implicitly defined это когда он естьodr-used или когда он явно используется по умолчанию после его первого объявления.

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

После выполнения тела деструктора и уничтожения любых автоматических объектов, размещенных в теле, деструктор для класса X вызывает деструкторы для Xпрямых неизменяемых нестатических членов данных, деструкторы для Xневиртуальных прямых базовых классов и, если X это тип наиболее производного класса ([class.base.init]), его деструктор вызывает деструкторы для Xвиртуальных базовых классов. Все деструкторы вызываются так, как если бы на них ссылались с квалифицированным именем, то есть игнорируя любые возможные виртуальные переопределяющие деструкторы в более производных классах. Базы и члены уничтожаются в порядке, обратном завершению их конструктора (см.[class.base.init]). A return statement в деструкторе не может напрямую возвращаться к вызывающей стороне; перед передачей управления вызывающей стороне вызываются деструкторы для членов и баз. Деструкторы для элементов массива вызываются в порядке, обратном их построению (см.[class.init]).

Деструктор может быть объявлен virtual илиpure virtual; если в программе создаются какие-либо объекты этого класса или любого производного класса, деструктор должен быть определен. Если у класса есть базовый класс с виртуальным деструктором, его деструктор (объявленный пользователем или неявно объявленный) является виртуальным.

[ Некоторые языковые конструкции имеют особую семантику при использовании во время уничтожения; см . ]Note: [class.cdtor]end note

Деструктор вызывается неявно

В каждом случае контекст вызова - это контекст построения объекта. Деструктор также вызывается неявно через использование a delete-expressionдля сконструированного объекта, выделенного a new-expression; контекст вызова - это delete-expression. [ Note: Массив типа класса содержит несколько подобъектов, для каждого из которых вызывается деструктор. ] Деструктор также может быть вызван явно. Деструктор , если он вызывается или , как указано в , и . Программа плохо сформирована, если деструктор, который потенциально может быть вызван, удален или недоступен из контекста вызова.end notepotentially invoked[expr.new][class.base.init][except.throw]

В точке определения виртуального деструктора (включая implicit definition) функция освобождения массива, не являющаяся массивом, определяется, как если бы для выражения,delete this появляющегося в невиртуальном деструкторе класса деструктора (см[expr.delete]. Раздел "Ресурсы" ). Если поиск завершился неудачно или функция освобождения имеет значокdeleted definition, программа имеет неправильный формат. [ Note: Это гарантирует, что функция освобождения, соответствующая динамическому типу объекта, доступна для delete-expression([class.free]). ]end note

При явном вызове деструктора деструктор указывается с помощью a, ~ за которым следует type-nameили, decltype-specifier что обозначает тип класса деструктора. Вызов деструктора подчиняется обычным правилам дляmember functions; то есть, если объект не относится к типу класса деструктора и не к классу, производному от типа класса деструктора (в том числе, когда деструктор вызывается через значение нулевого указателя), программа имеет неопределенное поведение. [ Note: Вызовdelete нулевого указателя не вызывает деструктор; см[expr.delete]. ] [end noteExample:

struct B {
  virtual ~B() { }
};
struct D : B {
  ~D() { }
};

D D_object;
typedef B B_alias;
B* B_ptr = &D_object;

void f() {
  D_object.B::~B();             // calls B's destructor
  B_ptr->~B();                  // calls D's destructor
  B_ptr->~B_alias();            // calls D's destructor
  B_ptr->B_alias::~B();         // calls B's destructor
  B_ptr->B_alias::~B_alias();   // calls B's destructor
}

end example] [ Note: Явный вызов деструктора всегда должен быть написан с использованиемmember access оператора или qualified-id([expr.prim]); в частности, unary-expression ~X() функция-член не является явным вызовом деструктора ([expr.unary.op]). ]end note

[ Явные вызовы деструкторов нужны редко. Одно из применений таких вызовов - для объектов, размещенных по определенным адресам с помощью размещения . Такое использование явного размещения и уничтожения объектов может быть необходимо для работы с выделенными аппаратными ресурсами и для написания средств управления памятью. Например, Note: new-expression

void* operator new(std::size_t, void* p) { return p; }
struct X {
  X(int);
  ~X();
};
void f(X* p);

void g() {                      // rare, specialized use:
  char* buf = new char[sizeof(X)];
  X* p = new(buf) X(222);       // use buf[] and initialize
  f(p);
  p->X::~X();                   // cleanup
}

end note]

Как только деструктор вызывается для объекта, объект больше не существует; поведение не определено, если деструктор вызывается для объекта, работа которогоlifetime завершилась. [ Example: Если деструктор для автоматического объекта вызывается явным образом, а блок впоследствии оставляется способом, который обычно вызывает неявное уничтожение объекта, поведение не определено. ]end example

[ Обозначение для явного вызова деструктора может использоваться для любого скалярного типа name ( ). Разрешение этого позволяет писать код без необходимости знать, существует ли деструктор для данного типа. Например: Note: [expr.pseudo]

typedef int I;
I* p;
p->I::~I();

end note]