У меня есть пара вопросов, касающихся мельчайших деталей структуры/классов С++.
i) В С++ могут ли члены структуры с другим модификатором доступа быть переупорядочены компилятором? Согласно здесь, компилятор может переупорядочивать элементы с разным доступом. Если это так, то как мы можем гарантировать правильную инициализацию членов? Например,
Struct S {
private:
int a;
public:
int b;
S() : a(1), b(a) {}
}
Если компилятор может переставлять a и b, то b может принимать любое произвольное значение, не так ли?
ii) Рассмотрим структуру
Struct S {
private: int a;
public: int b;
}
Если мы сериализуем эту структуру и прочитаем ее в другой программе, может ли произойти сбой, если порядок элементов не гарантируется?
iii) В C мы можем использовать указатель структуры и привести его к указателю первого члена данных. Можем ли мы сделать то же самое на C++? Есть ли какие-то ограничения и как насчет повторного заказа в такой ситуации?
На SO есть и другие сообщения, в которых обсуждается переупорядочивание членов класса, но мне до сих пор не ясно, разрешено ли это и выполняется ли компилятором.
Спасибо
(ii) Дайте определение «сериализации». Если вы имеете в виду просто вывести байтовое представление, а затем скопировать это байтовое представление в экземпляр S
, то это будет демонстрировать неопределенное поведение, поскольку S
не является POD.
Ни один из компиляторов, которые я использую, не переупорядочивает члены класса. Структура POD должна быть совместима с C. Из комментария Игоря структура или объекты класса, не относящиеся к POD, могут иметь измененный макет. Если вы хотите процитировать главу и стих стандарта, вы можете добавить тег language-lawyer
. Сериализацию объектов C++ следует выполнять с осторожностью, а не с дампом памяти, даже для типов POD.
(iii) Да, вы могли бы сделать то же самое в C++ - для структуры POD (которая, по сути, является структурой, которая не использует какие-либо функции C++ и может быть с тем же успехом написана на C; «POD» означает «простой старый данные"). Структура, состоящая из private
членов, не является POD.
Не включайте несколько вопросов одновременно. Подробнее об этом можно прочитать здесь
@IgorTandetnik, просто из любопытства, на языке программирования, означает ли «сериализация» что-нибудь еще, кроме сброса памяти в байты?
Конечно, нет; или, по крайней мере, не так просто, как memcpy(buffer, &some_struct, sizeof(some_struct))
. Скажем, struct Person {std::string first_name, last_name;}
не может быть разумно сериализован таким образом. Хотя бы потому, что sizeof(Person)
— константа времени компиляции, а имя человека может быть сколь угодно длинным. См. также: Сериализация
Хорошо. Таким образом, сериализация требует, чтобы повторное чтение объекта восстанавливало объект, что может быть невозможно при вызовах memcpy.
Что касается вашего первого вопроса: порядок элементов в памяти гарантируется только без вмешательства спецификатора доступа. Однако порядок в памяти не влияет на порядок строительства или уничтожения.
Предполагая, что вы действительно сериализуете данные, организация памяти явно не имеет значения. Конечно, простое использование байтов в памяти для struct
не обеспечивает правильной сериализации. В частности, нет никаких гарантий того, как на самом деле представляются значения, а размер и порядок следования байтов встроенных типов являются общеизвестными вариациями. Кроме того, необходимо учитывать потенциальные отступы. ... и, конечно же, возможно переупорядочивание членов.
Я настоятельно рекомендую не просто писать байты! Я видел более чем крупные компании, которые массово боролись с разработчиками, принявшими такой подход: любое успешное программное обеспечение будет расти и переживет непосредственный контекст, и эти короткие пути станут чрезвычайно вредными! Я видел некоторых людей, которые думали, что разумно эффективно хранить важные данные в базах данных, memcpy()
помещая их в большой двоичный объект (большой двоичный объект): помимо того, что они недоступны для запросов, данные легко теряются, если что-то меняется.
Для C-подобных struct
или class
, то есть для типов стандартной компоновки (в прошлом POD), применяется то же правило: адрес объекта — это адрес первого члена. Если вы используете определенные конструкции C++ (наследование, функции virtual
, спецификаторы доступа или структураторы и, возможно, другие, о которых я забыл), эта гарантия больше не действует.
(i) Члены могут быть переставлены в структуре памяти, но они все равно должны быть инициализированы в том порядке, в котором они перечислены в определении класса. Два заказа не обязательно должны быть одинаковыми.