Ответ 1
Он будет делать то же самое (ничего, по существу). Но это не то же самое, что если бы вы этого не пишете. Поскольку для записи деструктора потребуется рабочий деструктор рабочего класса. Если деструктор базового класса является закрытым или если есть какая-либо другая причина, он не может быть вызван, значит, ваша программа неисправна. Рассмотрим это
struct A { private: ~A(); };
struct B : A { };
Это нормально, если вам не требуется уничтожать объект типа B (и, следовательно, неявно типа A) - например, если вы никогда не вызываете delete на динамически создаваемом объекте или никогда не создаете объект в первую очередь. Если да, то компилятор отобразит соответствующую диагностику. Теперь, если вы явно укажете
struct A { private: ~A(); };
struct B : A { ~B() { /* ... */ } };
Это попытается неявно вызвать деструктор базового класса и вызовет диагностику уже во время определения ~B
.
Существует еще одно отличие, которое сосредотачивается вокруг определения деструктора и неявных вызовов на деструкторы участников. Рассмотрим этот элемент интеллектуального указателя
struct C;
struct A {
auto_ptr<C> a;
A();
};
Предположим, что объект типа C
создается в определении конструктора A в файле .cpp
, который также содержит определение struct C
. Теперь, если вы используете struct A
и требуете уничтожения объекта A
, компилятор предоставит неявное определение деструктора, как в случае выше. Этот деструктор также неявно вызывает деструктор объекта auto_ptr. И это удалит указатель, который он удерживает, который указывает на объект C
- не зная определения C
! Это появилось в файле .cpp
, где определен конструктор struct A.
Это действительно общая проблема при реализации идиомы pimpl. Решение состоит в том, чтобы добавить деструктор и предоставить пустое определение его в файле .cpp
, где определена структура C
. В то время, когда он вызывает деструктор своего члена, он тогда узнает определение struct C
и может корректно вызвать его деструктор.
struct C;
struct A {
auto_ptr<C> a;
A();
~A(); // defined as ~A() { } in .cpp file, too
};
Обратите внимание, что boost::shared_ptr
не имеет этой проблемы: вместо этого требуется полный тип, когда его конструктор вызывается определенными способами.
Другой момент, когда он имеет значение в текущем С++, - это когда вы хотите использовать memset
и друзей на таком объекте, у которого есть объявленный пользователем деструктор. Такие типы больше не являются POD (простые старые данные), и они не могут быть скопированы. Обратите внимание, что это ограничение на самом деле не требуется - и следующая версия С++ улучшила ситуацию на этом, так что она позволит вам копировать все такие типы до тех пор, пока не будут сделаны другие более важные изменения.
Поскольку вы спросили о конструкторах: ну, для этих вещей одинаковые вещи верны. Обратите внимание, что конструкторы также содержат неявные вызовы деструкторам. В таких вещах, как auto_ptr, эти вызовы (даже если они не выполняются во время выполнения - чистая возможность уже здесь имеет значение) будут иметь тот же самый вред, что и для деструкторов, и происходят, когда что-то в конструкторе бросает - тогда компилятор должен вызвать деструктор членов. Этот ответ использует неявное определение конструкторов по умолчанию.
Кроме того, то же самое верно для видимости и PODness, которые я сказал о деструкторе выше.
Существует одна важная разница в отношении инициализации. Если вы поместите объявленный конструктор пользователя, ваш тип больше не получит инициализацию значений членов, и ваш конструктор должен выполнить любую инициализацию. Пример:
struct A {
int a;
};
struct B {
int b;
B() { }
};
В этом случае всегда верно true
assert(A().a == 0);
В то время как следующее поведение undefined, поскольку b
никогда не инициализировалось (ваш конструктор пропустил это). Значение может быть равно нулю, но может также быть любым другим странным значением. Попытка прочитать из такого неинициализированного объекта вызывает поведение undefined.
assert(B().b == 0);
Это также верно для использования этого синтаксиса в new
, например new A()
(обратите внимание на круглые скобки в конце - если они опущены, инициализация значения не выполняется, и поскольку нет объявленного конструктора пользователя, который мог бы инициализировать его, A
останется неинициализированным).