Как инициализировать структуру до 0 в C++

Вот связанный ответ C, который не работает (как нулевой инициализатор для структуры) на C++: Инициализация структуры значением 0. Одно из представленных решений:

myStruct _m1 = {0}; 

Это нормально работает в C, но не работает в C++.:(:

ошибка: не удается инициализировать подобъект-член типа myScope::MyStruct с rvalue типа int.

Как в C++ инициализировать структуру нулями?

Связанный:

  1. Инициализация структуры значением 0 в C: инициализация структуры значением 0
  2. Обновление: (смежный, но НЕ повторяющийся вопрос, который также оказался очень полезным) Инициализация с пустыми фигурными скобками

К модам, голосующим за закрытие этого вопроса:

Мой вопрос не является дубликатом этого другого вопроса ( Инициализация с пустыми фигурными скобками), поскольку этот другой вопрос не касается различных способов инициализации структуры в C++ и почему способ C не работает, скорее, они спрашивая, почему ключевое слово C++explicitнарушить один из методов их инициализации. Два разных вопроса. Не дублировать.

4 ответа

Решение

Разобрался: чтобы скомпилировать, просто удалите ноль:

myStruct _m1 = {};

Теперь он компилируется. Однако я провел несколько тестов, чтобы проверить некоторые вещи, и это НЕ инициализирует все элементы структуры нулевым значением! Скорее, он инициализирует структуру значениями по умолчанию.

Предполагая, что у вас есть эта структура:

typedef struct
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
} data_t;

Обратите внимание typedefвыше является переносом из того времени, когда я тестировал этот материал на C вместо C++ (хотя, конечно, значения структуры по умолчанию не разрешены в C). Для C++ это предпочтительнее:

struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};

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

В любом случае, если я заявлю одно из вышеперечисленных data_t структуры, а затем сделайте следующее:

data_t d2 = {};
printf("d2.num1 = %i\nd2.num2 = %i\nd2.num3 = %i\nd2.num4 = %i\n\n",
       d2.num1, d2.num2, d2.num3, d2.num4);

... вывод будет:

d2.num1 = 100
d2.num2 = -100
d2.num3 = 0
d2.num4 = 150

И я даже не уверен, что d2.num3 равен нулю, потому что он был инициализирован нулем или потому, что он был оставлен неинициализированным, и эта ячейка памяти оказалась нулевой.

Как объясняется здесь: https://en.cppreference.com/w/cpp/language/zero_initialization, вы также можете сделать это:

myStruct _m1{};

В приведенном выше примере этот код:

data_t d2{};
printf("d2.num1 = %i\nd2.num2 = %i\nd2.num3 = %i\nd2.num4 = %i\n\n",
       d2.num1, d2.num2, d2.num3, d2.num4);

... выдаст результат, идентичный тому, что я показал выше.

Даже в тех случаях, когда для структуры = {0} ДЕЙСТВИТЕЛЬНО работает, например:

// Does NOT do what I expected! Only sets the FIRST value in the struct to zero! 
// The rest seem to use default values.
data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
       d3.num1, d3.num2, d3.num3, d3.num4);

... результат все еще не такой, как я ожидал, поскольку он устанавливает только первое значение в ноль! (Не понимаю почему):

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150

Однако в массивах в стиле C (НЕ в структурах) эта семантика работает нормально. Обратитесь к этому ответу здесь ( Как инициализировать все элементы массива одинаковым значением?). Таким образом, следующие строки устанавливают все элементы массива в стиле C в ноль при использовании C++:

uint8_t buffer[100] = {0}; // sets all elements to 0 in C OR C++
uint8_t buffer[100] = {};  // sets all elements to 0 in C++ only (won't compile in C)

Итак, после долгих экспериментов, похоже, что следующие несколько способов являются ЕДИНСТВЕННЫМИ способами обнуления структуры PERIOD. Если вы знаете иное, прокомментируйте и / или оставьте здесь свой ответ.

Единственные возможные способы обнулить структуру в C++.

  1. Будьте откровенны:

    // C-style typedef'ed struct
    typedef struct
    {
        int num1 = 100;
        int num2 = -100;
        int num3;
        int num4 = 150;
    } data_t;
    
    // EXPLICITLY set every value to what you want!
    data_t d1 = {0, 0, 0, 0};
    // OR (using gcc or C++20 only)
    data_t d2 = {.num1 = 0, .num2 = 0, .num3 = 0, .num4 = 0}
    
  2. Использовать memset() чтобы обнулить все байты:

    data_t d3;
    memset(&d3, 0, sizeof(d3));
    
  3. Сначала установите все значения по умолчанию на ноль:

    // C-style typedef'ed struct
    typedef struct
    {
        int num1 = 0;
        int num2 = 0;
        int num3 = 0;
        int num4 = 0;
    } data_t;
    
    // Set all values to their defaults, which are zero in
    // this case
    data_t d4 = {};
    // OR
    data_t d5{}; // same thing as above in C++
    
    // Set the FIRST value only to zero, and all the rest
    // to their defaults, which are also zero in this case
    data_t d6 = {0};
    
  4. Напишите конструктор для структуры C++

    // 1. Using an initializer list
    struct data
    {
        int num1;
        int num2;
        int num3;
        int num4;
    
        data() : 
            num1(0),
            num2(0),
            num3(0),
            num4(0) {}
    };
    
    data d7; // all values are zero
    
    // OR: 2. manually setting the values inside the constructor
    struct data
    {
        int num1;
        int num2;
        int num3;
        int num4;
    
        data()
        {
            num1 = 0;
            num2 = 0;
            num3 = 0;
            num4 = 0;
        }
    };
    
    data d8; // all values are zero
    
  5. Используйте структуру без значений по умолчанию и сделайте свой объект, который вы создаете из нее static

    tpedef struct
    {
        int num1;
        int num2;
        int num3;
        int num4;
    } data_t;
    
    // `static` forces a default initialization of zero for each
    // value when no other default values are set
    static data_t d9;
    
  6. Итак, если у вас есть структура с ненулевыми значениями по умолчанию, и вы хотите обнулить все значения, вы должны сделать это ЯВНО! Вот еще несколько способов:

    // 1. Have a `constexpr` copy of the struct that you use to
    // reset other struct objects. Ex:
    
    struct data
    {
        int num1 = 1;
        int num2 = 7;
        int num3 = -10;
        int num4 = 55;
    };
    
    constexpr data DATA_ALL_ZEROS = {0, 0, 0, 0};
    
    // Now initialize d13 to all zeros using the above `constexpr` struct 
    // object
    data d13 = DATA_ALL_ZEROS; 
    
    
    // OR 2. Use a `zero()` member function to zero the values:
    
    struct data
    {
        int num1 = 1;
        int num2 = 7;
        int num3 = -10;
        int num4 = 55;
    
        zero()
        {
            num1 = 0;
            num2 = 0;
            num3 = 0;
            num4 = 0;
        }
    };
    
    data d14;
    d14.zero();
    

Главный вывод здесь заключается в том, что НИ ОДИН из них: data_t d{}, data_t d = {}, а также data_t d = {0}, фактически установить для всех членов структуры ноль!

  1. data_t d{} устанавливает для всех значений значения по умолчанию, определенные в структуре.
  2. data_t d = {} также устанавливает для всех значений значения по умолчанию.
  3. А также data_t d = {0} устанавливает только ПЕРВОЕ значение на ноль, а все остальные значения на их значения по умолчанию.

ТАК БЫТЬ ЯВНЫМ!

Обратите внимание, что приведенные выше ключевые выводы, которые я написал, похоже, противоречат этой документации, поэтому это побудило меня задать этот дополнительный вопрос, указанный ниже как Ссылка № 1, который оказался ОЧЕНЬ полезным для моего понимания!

Ссылки:

  1. [НАИБОЛЕЕ ПОЛЕЗНО] Почему при инициализации структуры C++ значением `= {0}` для всех ее членов не устанавливается значение 0?
  2. [ОЧЕНЬ ПОЛЕЗНО]
    1. https://en.cppreference.com/w/cpp/language/zero_initialization
    2. https://en.cppreference.com/w/cpp/language/aggregate_initialization
    3. https://en.cppreference.com/w/cpp/language/value_initialization
  3. [ОЧЕНЬ ПОЛЕЗНО] Инициализация всех элементов массива (не структуры) одним значением:
    1. Как инициализировать все элементы массива одинаковым значением?
    2. [только gcc] Как инициализировать все элементы массива одинаковым значением?
  4. https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/cpp/struct_initialization.cpp
    1. Клонируйте это репо и запустите код самостоятельно с помощью cpp/run_struct_initialization.sh

Связанный:

  1. Инициализация значений по умолчанию в структуре

Объекты в C++ имеют возможность управлять значениями любых подобъектов внутри них. Таким образом, C++ не имеет механизма, который принудительно инициирует нулевую инициализацию любого объекта в целом.

Объекты без предоставленных пользователем конструкторов или инициализаторов членов по умолчанию могут подвергаться нулевой инициализации в двух случаях: если переменная объявлена static, или если объект инициализируется значением. Существует несколько синтаксисов, которые вызывают инициализацию значения объекта, включаяT(), T{}, а также T t = {};, где применимо.

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

В C++ для типов без констриктора (идентификатор тривиально конструируется) всегда инициализирует все байты нулем, точно так же, как { 0 }в С.

      PlainOldData pod{};

Для типов с конструктором, который, следовательно, не является тривиальным конструированием, нет особого смысла инициализировать все байты нулем, поскольку эти объекты предназначены для управления своим собственным состоянием. Обычно вам нужна инициализация по умолчанию, которую обычно можно выполнить с помощью {}.:

      Object obj{};
// or
Object obj;

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

      memset(&obj, 0, sizeof obj); // dangerous for non-trivial objects

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

Другие вопросы по тегам