Как "=default "отличается от" {} " для конструктора и деструктора по умолчанию?
Я изначально разместил это как вопрос только о деструкторах, но теперь я добавляю рассмотрение конструктора по умолчанию. Вот оригинальный вопрос:
если я хочу дать моему классу деструктор, который является виртуальным, но есть в противном случае то же самое, что компилятор будет генерировать, я могу использовать
=default
:class Widget { public: virtual ~Widget() = default; };
но кажется, что я могу получить тот же эффект с меньшим количеством ввода с помощью пустое определение:
class Widget { public: virtual ~Widget() {} };
там каким образом эти два определения ведут себя по-разному?
на основе ответов, опубликованных для этого вопроса, ситуация для конструктора по умолчанию кажется похожей. Учитывая, что почти нет разницы в значении между "=default
" и "{}
" для деструкторов, есть ли аналогично почти нет разницы в значении между этими вариантами для конструкторов по умолчанию? То есть, предполагая, что я хочу создать тип, где объекты этого типа будут созданы и уничтожен, с чего бы мне хотеть сказать
Widget() = default;
вместо
Widget() {}
?
Я прошу прощения, если расширение этого вопроса после его первоначальной публикации нарушает некоторые правила SO. Публикация почти идентичного вопроса для конструкторов по умолчанию поразила меня как менее желательный вариант.
3 ответа:
это совершенно другой вопрос, когда спрашивают о чем конструкторы деструкторы.
если ваш деструктор
virtual
, то разница незначительна, как Говард указал. Однако, если ваш деструктор был невиртуальный, это совершенно другая история. То же самое относится и к конструкторам.используя
= default
синтаксис для специальных функций-членов (конструктор по умолчанию, конструкторы копирования / перемещения / назначения, деструкторы и т. д) означает что-то совсем другое, чем просто делать{}
. С последним, функция становится "Пользователем". И это все меняет.это тривиальный класс по определению C++11:
struct Trivial { int foo; };
если вы попытаетесь сконструировать по умолчанию, компилятор автоматически создаст конструктор по умолчанию. То же самое касается копирования/перемещения и разрушения. Поскольку пользователь не предоставил ни одной из этих функций-членов, спецификация C++11 считает это " тривиальным" класс. Поэтому законно делать это, как memcpy их содержимое вокруг, чтобы инициализировать их и так далее.
это:
struct NotTrivial { int foo; NotTrivial() {} };
как следует из названия, это уже не тривиально. Он имеет конструктор по умолчанию, который является пользователем. Это не имеет значения, если он пуст; что касается правил C++11, это не может быть тривиальным типом.
это:
struct Trivial2 { int foo; Trivial2() = default; };
опять же, как следует из названия, это тривиальный тип. Зачем? Потому что ты сказал ... компилятор автоматически генерирует конструктор по умолчанию. Поэтому конструктор не " предоставляется пользователем."И поэтому тип считается тривиальным, поскольку у него нет пользовательского конструктора по умолчанию.
The
= default
синтаксис в основном используется для выполнения таких действий, как копирование конструкторов/Назначение, Когда вы добавляете функции-члены, которые предотвращают создание таких функций. Но он также вызывает особое поведение компилятора, поэтому он полезен по умолчанию конструкторы/деструкторы тоже.
они оба нетривиальны.
они оба имеют одинаковую спецификацию noexcept в зависимости от спецификации noexcept оснований и членов.
единственная разница, которую я обнаруживаю до сих пор, заключается в том, что если
Widget
содержит базу или элемент с недоступным или удаленным деструктором:struct A { private: ~A(); }; class Widget { A a_; public: #if 1 virtual ~Widget() = default; #else virtual ~Widget() {} #endif };
тут
=default
решение будет компилироваться, ноWidget
не будет разрушаемым типом. Т. е. если вы попытаетесь разрушитьWidget
, вы получите время компиляции ошибка. Но если вы этого не сделаете, у вас есть рабочая программа.Otoh, если вы поставляете user-provided деструктор, тогда вещи не будут компилироваться независимо от того, разрушаете ли вы
Widget
:test.cpp:8:7: error: field of type 'A' has private destructor A a_; ^ test.cpp:4:5: note: declared private here ~A(); ^ 1 error generated.
важная разница между
class B { public: B(){} int i; int j; };
и
class B { public: B() = default; int i; int j; };
это конструктор по умолчанию, определенный с помощью
B() = default;
является не-определенные пользователем. Это означает, что в случае значением-инициализации а вB* pb = new B(); // use of () triggers value-initialization
особый вид инициализации, который вообще не использует конструктор, будет иметь место, и для встроенных типов это приведет к ноль-инициализации. В случае
B(){}
это не проходить. Стандарт C++ n3337 § 8.5/7 говоритзначение-инициализировать объект типа T означает:
- если T - это a (возможно CV-квалифицированный) тип класса (пункт 9) С предоставленным пользователем конструктор (12.1), то конструктор по умолчанию для T называется (и инициализация плохо сформированной, если T имеет никакого доступного по умолчанию конструктор);
- если T является (возможно в CV-квалифицированный) несоюзной тип класса без пользовательского конструктора, то объект нуль-инициализированный и, если t неявно объявленный конструктор по умолчанию нетривиально, что конструктор называется.
- если T является массивом, затем каждый элемент инициализируется значением; - в противном случае объект нуль-инициализирован.
например:
#include <iostream> class A { public: A(){} int i; int j; }; class B { public: B() = default; int i; int j; }; int main() { for( int i = 0; i < 100; ++i) { A* pa = new A(); B* pb = new B(); std::cout << pa->i << "," << pa->j << std::endl; std::cout << pb->i << "," << pb->j << std::endl; delete pa; delete pb; } return 0; }
возможный результат:
0,0 0,0 145084416,0 0,0 145084432,0 0,0 145084416,0 //...