Инициализация Структуры C++
можно ли инициализировать структуры в C++, как показано ниже
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address temp_address =
{ .city = "Hamilton", .prov = "Ontario" };
ссылки здесь и здесь упоминается, что этот стиль можно использовать только в C. Если да, то почему это невозможно в C++? Есть ли какая-либо основная техническая причина, по которой он не реализован в C++, или это плохая практика использования этого стиля. Мне нравится использовать этот способ инициализации, потому что моя структура большая, и этот стиль дает мне четкую читаемость того, какое значение присваивается которому член.
пожалуйста, поделитесь со мной, если есть другие способы, посредством которых мы можем достичь той же читабельности.
Я сослался на следующие ссылки перед публикацией этого вопроса
- C / C++ для AIX
- инициализация структуры C переменной
- инициализация статической структуры с тегами в C++
- C++11 Инициализация Правильной Структуры
15 ответов:
Если вы хотите, чтобы было ясно, что такое каждое значение инициализатора, просто разделите его на несколько строк, с комментарием к каждому:
address temp_addres = { 0, // street_no nullptr, // street_name "Hamilton", // city "Ontario", // prov nullptr, // postal_code };
после у меня вопрос не привело к удовлетворительному результату (потому что C++ не реализует инициализацию на основе тегов для структур), я взял трюк, который я нашел здесь: являются ли члены структуры C++ инициализированы в 0 по умолчанию?
для вас это было бы равносильно:
address temp_address = {}; // will zero all fields in C++ temp_address.city = "Hamilton"; temp_address.prov = "Ontario";это, безусловно, ближе всего к тому, что вы хотели изначально (ноль всех полей, кроме тех, которые вы хотите инициализировать).
идентификаторы полей действительно являются синтаксисом инициализатора C. В C++ просто дайте значения в правильном порядке без имен полей. К сожалению, это означает, что вам нужно дать им все (на самом деле вы можете опустить конечные нулевые поля, и результат будет одинаковым):
address temp_address = { 0, 0, "Hamilton", "Ontario", 0 };
вы можете просто инициализировать через конструктор:
struct address { address() : city("Hamilton"), prov("Ontario") {} int street_no; char *street_name; char *city; char *prov; char *postal_code; };
эта функция называется назначенные инициализаторы. Это дополнение к стандарту C99. Однако эта функция была исключена из C++11. Согласно языку программирования C++, 4-е издание, раздел 44.3.3.2 (функции C, не принятые C++):
несколько дополнений к C99 (по сравнению с C89) были намеренно не приняты в C++:
[1] массивы переменной длины (VLAs); используйте вектор или некоторую форму динамического массива
[2] назначенные инициализаторы; использовать конструкторы
грамматика C99 имеет назначенные инициализаторы [см. ISO / IEC 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С другой стороны, C++11 не имеет назначенные инициализаторы [см. ISO / IEC 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 } { }для достижения того же эффекта используйте конструкторы или списки инициализаторов:
вы даже можете упаковать решение 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);отказ от ответственности: я не рекомендую этот стиль
Как уже упоминалось, это места инициализатор.
эта функция является частью C++20
Это не реализовано в C++. (кроме того,
char*строк? Надеюсь, что нет).как правило, если у вас так много параметров это довольно серьезный код. Но вместо этого, почему бы просто не инициализировать значение структуры, а затем назначить каждому члену?
Я знаю, что этот вопрос довольно старый, но я нашел другой способ инициализации, используя 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. Единственным недостатком является плохая ремонтопригодность: каждый раз, когда другой элемент должен быть инициализирован с помощью этого метода, все методы инициализации элемента должны быть изменены.
Я нашел этот способ сделать это для глобальных переменных, что не требует изменения исходного определения структуры:
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 (this, 0, sizeof(*this значит, это необходимо ...
(обратите внимание, что' temp_address 'является переменной типа' temp_address', однако этот новый тип наследуется от' address 'и может использоваться в каждом месте, где ожидается' address', поэтому все в порядке.)
это возможно, но только если структура, которую вы инициализируете, является структурой POD (простые старые данные). Он не может содержать никаких методов, конструкторов или даже значений по умолчанию.
в C++ инициализаторы c-стиля были заменены конструкторами, которые во время компиляции могут гарантировать, что выполняются только допустимые инициализации (т. е. после инициализации члены объекта согласованы).
это хорошая практика, но иногда предварительная инициализация удобно, как в вашем примере. ООП решает это абстрактными классами или creational design patterns.
на мой взгляд, использование этого безопасного способа убивает простоту, а иногда и компромисс безопасности может быть слишком дорогим, так как простой код не нуждается в сложной конструкции, чтобы оставаться ремонтопригодным.
в качестве альтернативного решения я предлагаю определить макросы с использованием лямбд, чтобы упростить инициализацию, чтобы выглядеть почти как C-style:
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 _={}; /* 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" ));однако вы можете легко определить макрос адреса с помощью общего макроса INIT
#define ADDRESS(x) INIT(address,x)
сегодня я столкнулся с аналогичной проблемой, когда у меня есть структура, которую я хочу заполнить тестовыми данными, которые будут переданы в качестве аргументов функции, которую я тестирую. Я хотел иметь вектор этих структур и искал однострочный метод для инициализации каждой структуры.
Я закончил с функцией конструктора в структуре, которая, как я считаю, также была предложена в нескольких ответах на ваш вопрос.
Это, вероятно, плохая практика, чтобы иметь аргументы в пользу конструктор имеет те же имена, что и открытые переменные-члены, требующие использования
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); }
Я мог бы что-то упустить здесь, почему бы и нет:
#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); }
вы можете использовать extern "C" для компиляции кода 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" }; }
Comments