Объединения и связанные с ними риски
Разделяйте память между типами
«Объединения и связанные с ними риски» — бесплатный урок C++ Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.
Что такое объединение
union позволяет нескольким членам совместно использовать одну и ту же область памяти. В каждый момент допустим только один член, а размер объединения равен размеру его самого крупного члена.
#include <iostream>
union Number {
int i;
float f;
};
int main() {
Number n;
n.i = 42;
std::cout << n.i << "\n";
}Размер объединения
Поскольку члены перекрываются, размер sizeof объединения равен размеру его самого крупного члена (с возможным добавлением выравнивающей области), а не сумме размеров.
#include <iostream>
union Mix { char c; int i; double d; };
int main() {
std::cout << sizeof(Mix) << "\n"; // size of double (often 8)
}Запись одного, чтение другого
Запись одного члена и чтение другого — это основной риск. Биты интерпретируются заново, что обычно приводит к неопределённому или неожиданному результату.
#include <iostream>
union Bits { int i; float f; };
int main() {
Bits b;
b.f = 1.0f;
std::cout << b.i << "\n"; // reads float bits as int
}Только один активный член
Безопасно читать можно только тот член, в который Вы записали значение последним. Отслеживайте активный член самостоятельно: объединение этого не запоминает.
#include <iostream>
union Value { int i; double d; };
int main() {
Value v;
v.d = 3.14; // d is active
std::cout << v.d << "\n";
}Шаблон Tagged-объединения
Чтобы безопасно использовать объединения, сопоставляйте их с меткой (перечислением), которая хранит информацию об активном члене. Это предшественник std::variant, реализованный вручную.
#include <iostream>
struct Tagged {
enum { Int, Double } tag;
union { int i; double d; };
};
int main() {
Tagged t; t.tag = Tagged::Int; t.i = 7;
if (t.tag == Tagged::Int) std::cout << t.i << "\n";
}Чтение правильного члена
Перед чтением всегда проверяйте метку. Чтение неправильного члена приводит к мусорным значениям.
#include <iostream>
struct Tagged {
enum { Int, Double } tag;
union { int i; double d; };
};
void print(const Tagged& t) {
if (t.tag == Tagged::Int) std::cout << "int " << t.i << "\n";
else std::cout << "double " << t.d << "\n";
}
int main() { Tagged t{Tagged::Double}; t.d = 2.5; print(t); }Нетипичные члены
Размещение в объединении члена с конструктором или деструктором, например std::string, требует ручного конструирования и уничтожения. Это чревато ошибками и служит ещё одной причиной предпочесть std::variant.
Анонимные объединения
Анонимное объединение не имеет имени: доступ к его членам осуществляется напрямую. Это удобно внутри структуры, но правило активного члена всё равно действует.
#include <iostream>
struct Packet {
int kind;
union { int code; char letter; };
};
int main() {
Packet p; p.kind = 1; p.code = 99;
std::cout << p.code << "\n";
}Почему объединения рискованны
Кратко об опасностях:
- Нет информации о том, какой член активен.
- Чтение неактивного члена приводит к неопределённому поведению.
- Для нетривиальных типов требуется ручное управление временем жизни.
Когда объединения всё ещё полезны
Объединения по-прежнему полезны в низкоуровневом коде: для переинтерпретации байтов, экономии памяти в условиях ограничений встроенных систем или соответствия фиксированному двоичному формату. Для обычных данных «один из нескольких типов» предпочитайте более безопасную альтернативу.
Переход к std::variant
std::variant — это типобезопасное объединение, которое самостоятельно отслеживает активный тип и выбрасывает исключение при обращении к неправильному типу. В остальной части курса мы рассмотрим его подробнее.
Быстрая проверка
Проверьте, насколько хорошо Вы поняли объединения.
Итоги
Вы узнали об объединениях:
- Все члены используют один блок памяти.
- Для чтения допустим только член, в который записали значение последним.
- Используйте метку (перечисление), чтобы отслеживать активный член.
- В современном C++ для безопасности предпочитайте
std::variant.
Изучай C++ с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 51
- Уроки
- 203
Часто задаваемые вопросы
Урок «Объединения и связанные с ними риски» бесплатный?
Да — полный текст урока «Объединения и связанные с ними риски» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.
Чему я научусь в уроке «Объединения и связанные с ними риски»?
Разделяйте память между типами Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C++ Academy?
Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Объединения и связанные с ними риски»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C++ Academy?
Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Перечисление scoped enum class
- Объединения и связанные с ними риски
- std::variant
- std::visit