Правильний спосіб ініціалізації структур C ++


82

Наш код включає структуру POD (Plain Old Datastructure) (це основна структура c ++, яка містить інші структури та змінні POD, які потрібно ініціалізувати на початку).

Виходячи з того, що я прочитав , здається, що:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

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

myStruct = new MyStruct();

Однак, коли структура ініціалізується другим способом, Valgrind пізніше скаржиться на те, що "умовний стрибок або переміщення залежить від неініціалізованих значень", коли використовуються ці змінні. Моє розуміння тут недосконале, або Вальгрінд кидає помилкові спрацьовування?


1
також зверніть увагу, що new MyStruct()в C ++ 03 не потрібно було встановлювати будь-які байти заповнення. У C ++ 0x це так. Будь-які біти заповнення будуть встановлені на 0 в C ++ 0x.
Йоханнес Шауб - літб

Дякую за улов, я відредагував своє запитання.
Shadow503


Як я розумію, ви не повинні отримувати повідомлення про помилку. Можливо, ваша структура даних не є POD? Чи можете ви звести свій код до найменшої програми, яка все ще демонструє таку поведінку, і розмістити цю дистильовану програму? (Див. Sscce.org ).
Робᵩ

Який компілятор? Чи можете ви опублікувати визначення MyStruct?
Алан Стокс,

Відповіді:


121

У C ++ класи / структури однакові (з точки зору ініціалізації).

Неструктурована структура також може мати конструктор, щоб вона могла ініціалізувати члени.
Якщо ваша структура - це POD, тоді ви можете використовувати ініціалізатор.

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

В якості альтернативи ви можете використовувати конструктор за замовчуванням.

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

Я вважаю, що valgrind скаржиться, бо саме так раніше працював C ++. (Я не зовсім впевнений, коли C ++ був оновлений із нульовою конструкцією за замовчуванням для ініціалізації). Найкраще поставити конструктор, який ініціалізує об’єкт (конструкції - це дозволені конструктори).

Як додаткове зауваження:
Багато початківців намагаються оцінити init:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

Швидкий пошук "Найбільш неприємного розбору" дасть краще пояснення, ніж я можу.


1
Чи є у вас посилання на цю нульову ініціалізацію змінних у C ++? Останнє, що я чув, він все ще має таку ж поведінку, як C. IIRC, компілятор MSVC навіть у якийсь момент скинув неініціалізованих членів з нульовою ініціалізацією (з міркувань продуктивності, я припускаю), що, мабуть, створило досить серйозні проблеми для інших підрозділів MS :)
Marc Mutz - mmutz

1
ні, C()але, схоже, ти маєш рацію .
Marc Mutz - mmutz

4
З класу POD, T, T()завжди дорівнює нулю инициализирован скалярні подоб'ектов. Ім'я ініціалізації верхнього рівня в C ++ 98 називали іншим, ніж у C ++ 03 ("ініціалізація за замовчуванням" проти "ініціалізація значень"), але кінцевий ефект для POD однаковий. І в C ++ 98, і в C ++ 03 усі значення в ньому будуть нульовими.
Йоханнес Шауб - літб

1
На C c();жаль, @RockyRaccoon - це не декларація змінної, а швидше декларація функції (функція, що називається 'c', яка не приймає параметрів і повертає об'єкт типу C). Google: Моксичний розбір
Мартін-Йорк,

1
@RockyRaccoon Різниця тут і у питанні, на яке ви посилаєтесь у своєму коментарі; полягає в тому, що в цьому випадку в конструкторі не використовуються параметри. Отже, компілятору важко розібрати це і сказати різницю між оголошеннями змінних та прямими оголошеннями функції. Він вибирає декларацію функції вперед. Якщо вам потрібен параметр у конструкторі, то компілятор може побачити, що це оголошення змінної без будь-яких проблем. C c(4);Є дійсним оголошенням змінної.
Мартін Йорк,

2

З того, що ви сказали нам, у valgrind видається хибнопозитивним. newсинтаксис з() , цінуючи форматувати об'єкт, за умови , що це стручок.

Чи можливо, що якась частина вашої структури насправді не є POD, і це перешкоджає очікуваній ініціалізації? Чи можете ви спростити свій код на приклад, який можна передати, який все ще позначає помилку valgrind?

Можливо, ваш компілятор насправді не ініціалізує значення структур POD.

У будь-якому випадку, мабуть, найпростішим рішенням є написання конструкторів (конструкторів) за необхідністю для struct / subparts.


1

Я пишу тестовий код:

#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

компіляція та запуск g ++:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend

Добре, я не так добре знайомий з new sc;проти new sc();. Я б подумав, що відсутні панелі є помилкою. Але це, мабуть, демонструє, що відповідь є правильною (вона ініціює значення 0, коли ви використовуєте конструктор за замовчуванням.)
nycynik

0

Вам потрібно ініціалізувати будь-яких членів у вашій структурі, наприклад:

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};

У цьому випадку я використовую не клас, а структуру.
Shadow503

Структура - це клас. Редагував мій пост :)
ralphtheninja

2
Вам не потрібно це робити для POD
Алан Стокс

Як структура має приватних членів?
OS2

Що ви маєте на увазі як? Додаючи приватне перед ними, як звичайний клас.
ралфтенджа

0

Оскільки це структура POD, ви завжди можете перестановити її на 0 - це може бути найпростіший спосіб ініціалізації полів (за умови, що це доречно).


1
Незалежно від того, чи це структура, чи клас, це не має нічого спільного з можливістю memset або створення конструктора. Див., Наприклад, stackoverflow.com/questions/2750270/cc-struct-vs-class
Ерік,

Зазвичай ви не створювали б пам'ять класу після його створення, оскільки його конструктор (імовірно) здійснив ініціалізацію.
Скотт С Вілсон,

1
немає різниці між "класом" та "структурою" у C ++, за винятком захисту за замовчуванням (приватного та загальнодоступного, відповідно). Структура може бути не-POD, а клас може бути POD, ці поняття є ортогональними.
Marc Mutz - mmutz

@mmutz та @Erik - погодились - скоротили мою відповідь, щоб уникнути плутанини.
Скотт С Вільсон

memset () не є найкращим варіантом, ви можете нулізувати структуру POD, просто викликавши її за замовчуванням ctor.
Нільс,

0

Це здається мені найпростішим способом. Члени структури можна ініціалізувати за допомогою фігурних дужок "{}". Наприклад, нижче наведено дійсну ініціалізацію.

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

У C ++ є хороша інформація про структури - https://www.geeksforgeeks.org/structures-in-cpp/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.