Как "=default "отличается от" {} " для конструктора и деструктора по умолчанию?


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

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

class Widget {
public:
   virtual ~Widget() = default;
};

но кажется, что я могу получить тот же эффект с меньшим количеством ввода с помощью пустое определение:

class Widget {
public:
   virtual ~Widget() {}
};

там каким образом эти два определения ведут себя по-разному?

на основе ответов, опубликованных для этого вопроса, ситуация для конструктора по умолчанию кажется похожей. Учитывая, что почти нет разницы в значении между "=default" и "{} " для деструкторов, есть ли аналогично почти нет разницы в значении между этими вариантами для конструкторов по умолчанию? То есть, предполагая, что я хочу создать тип, где объекты этого типа будут созданы и уничтожен, с чего бы мне хотеть сказать

Widget() = default;

вместо

Widget() {}

?

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

3 126

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
//...

http://ideone.com/k8mBrd