2015-12-20 2 views
16

У меня есть абстрактный класс, который объявлен следующим образом:Должен ли я по умолчанию виртуальные деструкторы?

class my_type { 
public: 
    virtual ~my_type() = default; 
    virtual void do_something() = 0; 
}; 

она считается хорошей практикой объявлять деструктор, как это, с default ключевое слово? Есть ли способ лучше?

Кроме того, есть = 0 современный (C++ 11) способ указания реализации по умолчанию или есть лучший способ?

+0

Что не так с 'virtual ~ foo() {}'? –

+0

ничего не подумал, что путь C++ 11 - использовать ключевое слово по умолчанию .. не так ли? –

+3

@Humam Обратите внимание на то, что вы ошибочно установили 'virtual' и тип данных' data'. – LogicStuff

ответ

17

Да, вы можете использовать = default для таких деструкторов. Особенно, если вы просто заменили его {}. Я думаю, что = default лучше, потому что он более явный, поэтому он сразу бросается в глаза и не оставляет места для сомнений.

Однако при этом необходимо учитывать несколько примечаний.

Когда вы = default деструктор в файле заголовка(см редактирования) (или любой другой специальной функции по этому вопросу), это в основном определяя его в заголовке. При разработке общей библиотеки вы можете явно указать деструктор, предоставляемый только библиотекой, а не в заголовке, чтобы вы могли легче изменить ее в будущем, не требуя пересоздания зависимого двоичного файла. Но опять же, это вопрос, когда вопрос не является просто ли до = default или до {}.


EDIT: Как Шон остро отметил в комментариях, вы можете также использовать = default вне объявления класса, который получает лучшее из обоих миров здесь.


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

struct A { ~A() = delete; }; 
struct B : A { ~B() {}; } 

Это будет не компиляции, так как вы заставляете компилятор генерировать указанный код (и его неявные предпосылки, такие как вызова деструктора элементов а) для деструктора Б - и не может, поскольку деструктор A удален. Рассмотрим это, однако:

struct A { ~A() = delete; }; 
struct B : A { ~B() = default; } 

Это, на самом деле, бы компиляции, потому что компилятор видит, что ~B() не может быть сгенерирован, поэтому он просто не производит - и объявляет его как удаленный. Это означает, что вы получите только ошибку, когда пытаетесь на самом деле использовать/вызывать B::~B().

Это имеет по крайней мере два значения, которые вы должны знать:

  1. Если вы хотите получить сообщение об ошибке при компиляции просто все, что включает в себя объявление класса, вы не получите его, так как это технически обоснованно.
  2. Если вы изначально всегда используете = default для таких деструкторов, вам не придется беспокоиться о том, что деструктор вашего супер класса будет удален, если окажется, что все в порядке, и вы никогда его не используете. Это своего рода экзотическое использование, но в этом смысле оно более корректно и надежно. Вы получите ошибку, только если вы действительно используете деструктор, иначе вы останетесь в покое.

Итак, если вы собираетесь использовать защитный подход к программированию, вам может потребоваться просто использовать {}. В противном случае вам, вероятно, будет лучше = default ing, поскольку он лучше придерживается минимальных программных инструкций, необходимых для получения правильной рабочей базы и предотвращения непреднамеренных последствий. .


Что касается = 0: Да, это все еще правильный способ сделать это. Но учтите, что на самом деле он не указывает, что «нет реализации по умолчанию», а скорее, что (1) класс не является исполняемым; и (2) Любые производные классы должны переопределить эту функцию (хотя они могут использовать необязательную реализацию, предоставляемую суперклассом). Другими словами, вы можете определить функцию и объявить ее чистым виртуальным. Вот пример:

struct A { virtual ~A() = 0; } 
A::~A() = default; 

Это обеспечит эти ограничения для A (и его деструктора).


1) Хороший пример того, почему это может быть полезно в самых неожиданных способов, как некоторые люди всегда return со скобками, а затем C++ 14 добавлена ​​decltype(auto), который по существу создал техническую разницу между использованием он с круглыми скобками и без них.

+1

Я обычно 'default' целый" _big 5_ ", за _Rule of 5/0_. И только для информации: если вы по какой-то причине хотите сделать деструктор чистым виртуальным, вам все равно нужно обеспечить реализацию для него. –

+3

«Один случай, когда я бы не стал = по умолчанию деструктор (или любая другая специальная функция, если на то пошло), это когда я хочу убедиться, что его определение не входит в заголовочный файл» - вы можете положить '= default 'на определениях функций вне определения начального класса. Я делаю это все время специально в тех случаях, когда я не забочусь о тривиальной разрушаемости и просто хочу уменьшить давление линкера. –

+0

Спасибо @SeanMiddleditch. Ред. –