Инициализация структуры C ++

223

Возможно ли инициализировать структуры на С++, как указано ниже.

struct address {
    int street_no;
    char *street_name;
    char *city;
    char *prov;
    char *postal_code;
};
address temp_address =
    { .city = "Hamilton", .prov = "Ontario" };

В ссылках здесь и здесь упоминается, что этот стиль можно использовать только в C. Если да, то почему это невозможно в С++? Есть ли какая-либо техническая причина, почему она не реализована на С++, или это плохая практика использования этого стиля. Мне нравится использовать этот способ инициализации, потому что моя структура большая, и этот стиль дает мне четкое представление о том, какое значение присваивается члену.

Пожалуйста, поделитесь со мной, если есть другие способы, с помощью которых мы можем достичь такой же удобочитаемости.

Я отправил следующие ссылки перед публикацией этого вопроса

  • 18
    Персональный взгляд на мир: вам не нужен этот стиль инициализации объектов в C ++, потому что вы должны использовать конструктор.
  • 6
    Да, я думал об этом, но у меня есть массив большой структуры. Это было бы легко и понятно для меня. У вас есть какой-нибудь стиль / хорошая практика инициализации с помощью конструктора, которая также дает лучшую читаемость.
Показать ещё 9 комментариев
Теги:

17 ответов

134
Лучший ответ

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

address temp_addres = {
  0,  // street_no
  nullptr,  // street_name
  "Hamilton",  // city
  "Ontario",  // prov
  nullptr,  // postal_code
};
  • 5
    Мне лично нравится и рекомендую этот стиль
  • 350
    Я не знаю, потому что это склонно к ошибкам.
Показать ещё 7 комментариев
81

После моего вопроса результат не привел к удовлетворительному результату (потому что С++ не реализует тег-основанный init для структур), я взял трюк, который я нашел здесь: Являются ли члены структуры С++ инициализированы по умолчанию 0?

Для вас это будет означать:

address temp_address = {}; // will zero all fields in C++
temp_address.city = "Hamilton";
temp_address.prov = "Ontario";

Это, безусловно, самое близкое к тому, что вы хотели первоначально (нуль всех полей, кроме тех, которые вы хотите инициализировать).

  • 8
    Это не работает для статически не инициализированных объектов
  • 4
    static address temp_address = {}; буду работать. Заполнение его потом зависит от времени выполнения, да. Вы можете обойти это, предоставив статическую функцию, которая выполняет static address temp_address = init_my_temp_address(); за вас: static address temp_address = init_my_temp_address(); ,
Показать ещё 3 комментария
13

Идентификаторы полей действительно являются синтаксисом инициализатора C. В С++ просто укажите значения в правильном порядке без имен полей. К сожалению, это означает, что вам нужно дать им все (на самом деле вы можете опустить конечные поля с нулевым значением, и результат будет таким же):

address temp_address = { 0, 0, "Hamilton", "Ontario", 0 }; 
  • 1
    Да, вы всегда можете использовать выравнивание структуры.
  • 3
    Да, в настоящее время я использую только этот метод (Aligned Struct Initialization). Но я чувствую, что читаемость не очень хорошая. Поскольку моя Структура велика, инициализатор имеет так много данных, и мне трудно отследить, какое значение назначено какому элементу.
Показать ещё 6 комментариев
10

Эта функция называется назначенными инициализаторами. Это дополнение к стандарту C99. Однако эта функция была исключена из С++ 11. Согласно языку программирования С++, четвертое издание, раздел 44.3.3.2 (функции C, не принятые на С++):

Несколько дополнений к C99 (по сравнению с C89) были намеренно не приняты в С++:

[1] Массивы переменной длины (VLA); использовать вектор или какую-либо форму динамического массива

[2] Назначенные инициализаторы; использовать конструкторы

В грамматике C99 есть назначенные инициализаторы [См. ИСО/МЭК 9899: 2011, проект Комитета N1570 - 12 апреля 2011 г.]

6.7.9 Инициализация

initializer:
    assignment-expression
    { initializer-list }
    { initializer-list , }
initializer-list:
    designation_opt initializer
    initializer-list , designationopt initializer
designation:
    designator-list =
designator-list:
    designator
    designator-list designator
designator:
    [ constant-expression ]
    . identifier

С другой стороны, С++ 11 не имеет назначенных инициализаторов [См. ИСО/МЭК 14882: 2011, проект Комитета N3690 - 15 мая 2013 г.]

8.5 Инициализаторы

initializer:
    brace-or-equal-initializer
    ( expression-list )
brace-or-equal-initializer:
    = initializer-clause
    braced-init-list
initializer-clause:
    assignment-expression
    braced-init-list
initializer-list:
    initializer-clause ...opt
    initializer-list , initializer-clause ...opt
braced-init-list:
    { initializer-list ,opt }
    { }

Чтобы добиться такого же эффекта, используйте конструкторы или списки инициализаторов:

7

Как отмечали другие, это назначенный инициализатор.

Эта функция является частью С++ 20

7

Вы можете просто инициализировать через ctor:

struct address {
  address() : city("Hamilton"), prov("Ontario") {}
  int street_no;
  char *street_name;
  char *city;
  char *prov;
  char *postal_code;
};
  • 8
    Это имеет место, только если вы контролируете определение struct address . Кроме того, типы POD часто намеренно не имеют конструктора и деструктора.
7

Вы даже можете упаковать решение Gui13 в один оператор инициализации:

struct address {
                 int street_no;
                 char *street_name;
                 char *city;
                 char *prov;
                 char *postal_code;
               };


address ta = (ta = address(), ta.city = "Hamilton", ta.prov = "Ontario", ta);

Отказ от ответственности: я не рекомендую этот стиль

6

Я знаю, что этот вопрос довольно старый, но я нашел другой способ инициализации, используя constexpr и currying:

struct mp_struct_t {
    public:
        constexpr mp_struct_t(int member1) : mp_struct_t(member1, 0, 0) {}
        constexpr mp_struct_t(int member1, int member2, int member3) : member1(member1), member2(member2), member3(member3) {}
        constexpr mp_struct_t another_member(int member) { return {member1, member,     member2}; }
        constexpr mp_struct_t yet_another_one(int member) { return {member1, member2, member}; }

    int member1, member2, member3;
};

static mp_struct_t a_struct = mp_struct_t{1}
                           .another_member(2)
                           .yet_another_one(3);

Этот метод также работает для глобальных статических переменных и даже для constexpr. Единственным недостатком является плохой ремонтопригодность: каждый раз, когда этот метод должен быть инициализирован, необходимо изменить все методы инициализации членов.

  • 1
    Это модель строителя . Методы-члены могут возвращать ссылку на свойство, которое нужно изменить, вместо того, чтобы каждый раз создавать новую структуру
  • 0
    Я помню, как Мейерс однажды представил это в своей книге Effective Mordern C ++.
Показать ещё 1 комментарий
5

Он не реализован в С++. (также, char* строки? Надеюсь, что нет).

Обычно, если у вас так много параметров, это довольно серьезный запах кода. Но вместо этого, почему бы не просто инициализировать структуру и затем назначить каждый член?

  • 6
    «(также char* strings? Надеюсь, что нет).» - Ну, это пример C.
  • 5
    Он конвертирует в C ++.
Показать ещё 4 комментария
3

Я мог бы здесь что-то упустить, почему бы и нет:

#include <cstdio>    
struct Group {
    int x;
    int y;
    const char* s;
};

int main() 
{  
  Group group {
    .x = 1, 
    .y = 2, 
    .s = "Hello it works"
  };
  printf("%d, %d, %s", group.x, group.y, group.s);
}
  • 0
    Это C ответ. Вопрос по поводу C ++.
  • 0
    Я скомпилировал вышеупомянутую программу с помощью компилятора MinGW C ++ и компилятора Arduino AVR C ++, и оба работали, как и ожидалось. Обратите внимание на #include <cstdio>
Показать ещё 1 комментарий
2

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

struct address {
             int street_no;
             char *street_name;
             char *city;
             char *prov;
             char *postal_code;
           };

затем объявить переменную нового типа, унаследованную от исходного типа структуры, и использовать конструктор для инициализации полей:

struct temp_address : address { temp_address() { 
    city = "Hamilton"; 
    prov = "Ontario"; 
} } temp_address;

Не такой элегантный, как стиль C, хотя...

Для локальной переменной также должно быть возможно, но нужно проверить, нужна ли дополнительная memset (это, 0, sizeof (* this)),...

(Обратите внимание, что "temp_address" - это переменная типа temp_address, но этот новый тип наследуется от "адреса" и может использоваться во всех местах, где ожидается "адрес", так что это нормально.)

  • 1
    Слишком многословно. трудно читать и поддерживать.
1

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

Это хорошая практика, но иногда предварительная инициализация удобна, как в вашем примере. ООП решает это абстрактными классами или шаблонами создания дизайна.

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

В качестве альтернативного решения я предлагаю определить макросы, используя lambdas, чтобы упростить инициализацию, чтобы выглядеть почти как C-стиль:

struct address {
  int street_no;
  const char *street_name;
  const char *city;
  const char *prov;
  const char *postal_code;
};
#define ADDRESS_OPEN [] { address _={};
#define ADDRESS_CLOSE ; return _; }()
#define ADDRESS(x) ADDRESS_OPEN x ADDRESS_CLOSE

Макрос ADDRESS расширяется до

[] { address _={}; /* definition... */ ; return _; }()

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

address temp_address = ADDRESS(( _.city = "Hamilton", _.prov = "Ontario" ));

Вы также можете написать генерализованный инициализатор макросов

#define INIT_OPEN(type) [] { type _={};
#define INIT_CLOSE ; return _; }()
#define INIT(type,x) INIT_OPEN(type) x INIT_CLOSE

но затем вызов немного менее красивый

address temp_address = INIT(address,( _.city = "Hamilton", _.prov = "Ontario" ));

однако вы можете определить макрос ADDRESS с помощью общего макроса INIT.

#define ADDRESS(x) INIT(address,x)
1

Возможно, но только если инициализация структуры - это структура POD (простые старые данные). Он не может содержать никаких методов, конструкторов или даже значений по умолчанию.

0

В GNUC++ (кажется, устарел с 2.5, давным-давно :) Смотрите ответы здесь: инициализация C struct с использованием меток. Это работает, но как?), можно инициализировать структуру следующим образом:

struct inventory_item {
    int bananas;
    int apples;
    int pineapples;
};

inventory_item first_item = {
    bananas: 2,
    apples: 49,
    pineapples: 4
};
0

Вдохновлен этим действительно аккуратным ответом: (https://stackoverflow.com/questions/18731707/why-does-c11-not-support-designated-initializer-lists-as-c99)

Вы можете сделать закрытие ламбы:

// Nobody wants to remember the order of these things
struct SomeBigStruct {
  int min = 1;
  int mean = 3 ;
  int mode = 5;
  int max = 10;
  string name;
  string nickname;
  ... // the list goes on
}

,

class SomeClass {
  static const inline SomeBigStruct voiceAmps = []{
    ModulationTarget $ {};
    $.min = 0;  
    $.nickname = "Bobby";
    $.bloodtype = "O-";
    return $;
  }();
}

Или, если вы хотите быть очень модным

#define DesignatedInit(T, ...)\
  []{ T ${}; __VA_ARGS__; return $; }()

class SomeClass {
  static const inline SomeBigStruct voiceAmps = DesignatedInit(
    ModulationTarget,
    $.min = 0,
    $.nickname = "Bobby",
    $.bloodtype = "O-",
  );
}

С этим связаны некоторые недостатки, в основном связанные с неинициализированными членами. Судя по комментариям в связанных ответах, он компилируется эффективно, хотя я не проверял его.

В целом, я просто считаю это аккуратным подходом.

0

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

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

Вероятно, неверная практика состоит в том, чтобы аргументы конструктора имели те же имена, что и переменные public-члена, требующие использования указателя this. Кто-то может предложить отредактировать, если есть лучший способ.

typedef struct testdatum_s {
    public:
    std::string argument1;
    std::string argument2;
    std::string argument3;
    std::string argument4;
    int count;

    testdatum_s (
        std::string argument1,
        std::string argument2,
        std::string argument3,
        std::string argument4,
        int count)
    {
        this->rotation = argument1;
        this->tstamp = argument2;
        this->auth = argument3;
        this->answer = argument4;
        this->count = count;
    }

} testdatum;

Что я использовал в своей тестовой функции для вызова тестируемой функции с помощью следующих аргументов:

std::vector<testdatum> testdata;

testdata.push_back(testdatum("val11", "val12", "val13", "val14", 5));
testdata.push_back(testdatum("val21", "val22", "val23", "val24", 1));
testdata.push_back(testdatum("val31", "val32", "val33", "val34", 7));

for (std::vector<testdatum>::iterator i = testdata.begin(); i != testdata.end(); ++i) {
    function_in_test(i->argument1, i->argument2, i->argument3, i->argument4m i->count);
}
-14

вы можете использовать extern "C" для компиляции кода c на С++. В вашем случае вы можете использовать ниже код

extern "C" {
//write your C code which you want to compile in C++
struct address {
int street_no;
char *street_name;`enter code here`
char *city;
char *prov;
char *postal_code;
};
address temp_address ={ .city = "Hamilton", .prov = "Ontario" };
}
  • 29
    extern "C" не "компилирует код C", содержимое extern "C" блока extern "C" по-прежнему является кодом C ++, все это влияет на изменение имен, а не на язык, используемый в блоке.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню