Unie w C++
1. Wprowadzenie
Unia (ang. union) to specjalny typ złożony w języku C++ (i wcześniej w C), który umożliwia przechowywanie różnych typów danych w tym samym obszarze pamięci.
Oznacza to, że wszystkie pola unii współdzielą ten sam fragment pamięci — w danej chwili można przechowywać tylko jedną wartość, spośród wszystkich zdefiniowanych w unii.
Unie są przydatne wtedy, gdy chcemy oszczędzać pamięć lub reprezentować dane, które mogą występować w różnych formatach (np. liczba całkowita lub zmiennoprzecinkowa).
2. Składnia unii
Podstawowa składnia:
Tworzenie obiektu:
3. Jak działa unia?
Wszystkie pola unii dzielą tę samą przestrzeń pamięci. Rozmiar unii jest równy rozmiarowi największego z jej pól.
Przykład:
Wynik (może się różnić):
Jako int: 65
Jako char: A
Jako int po zmianie char: 1094795585
➡️ Po zmianie pola c, wartość pola i staje się niepoprawna — ponieważ oba pola zajmują tę samą pamięć!
4. Rozmiar unii
Rozmiar unii to maksymalny rozmiar spośród wszystkich jej elementów:
5. Inicjalizacja unii
Unie można inicjalizować tylko jednym polem naraz:
Od C++11 można także używać list inicjalizacyjnych:
6. Unie z nazwanymi polami
Unia może być zdefiniowana wewnątrz struktury (lub klasy):
Dzięki temu można przechowywać różne dane w zależności od wartości pola typ.
7. Dostęp do pól unii
Dostęp odbywa się tak samo jak w strukturach:
Po przypisaniu wartości do p.f, zawartość p.i staje się nieokreślona.
8. Unia wewnątrz struktury — przykład praktyczny
To klasyczny wzorzec użycia unii – różne typy danych dla różnych rodzajów zdarzeń.
9. Unie a klasy i struktury
Podobieństwa:
- Mogą zawierać pola i funkcje (od C++).
- Mogą mieć modyfikatory dostępu (
public,private,protected).
Różnice:
| Cecha | struct / class |
union |
|---|---|---|
| Pamięć dla pól | Każde pole ma osobne miejsce | Wszystkie pola współdzielą pamięć |
| Liczba aktywnych pól | Wszystkie naraz | Tylko jedno aktywne w danym momencie |
| Zastosowanie | Grupowanie danych logicznie | Oszczędzanie pamięci, alternatywne reprezentacje danych |
10. Funkcje składowe i konstruktory w uniach (C++11+)
W nowoczesnym C++ unie mogą zawierać funkcje składowe, konstruktory i destruktory, ale:
- nie mogą zawierać pól z niebanalnymi konstruktorami (np.
std::string) w uniach klasycznych, - w C++11 można używać tzw. variant-like unions.
Przykład:
11. Bezpieczne unie – std::variant (C++17)
Ponieważ klasyczne unie są niebezpieczne (łatwo o błędne odczyty danych), C++17 wprowadził nowoczesny zamiennik:
std::variant z biblioteki <variant>.
std::variant jest typo-bezpieczny — kompilator pilnuje, który typ jest aktualnie przechowywany.
12. Typowe zastosowania unii
- Oszczędność pamięci w systemach wbudowanych, sterownikach, mikroprocesorach.
- Reprezentacja danych w różnych formatach (np.
float↔int). - Interpretacja bitów (tzw. type punning).
- Parsowanie protokołów i danych binarnych.
- Struktury z polami alternatywnymi (np. zdarzenia, komunikaty).
- Wewnętrzne implementacje kompilatorów, systemów i bibliotek niskopoziomowych.
13. Ograniczenia unii
- W danej chwili można przechowywać tylko jedno aktywne pole.
- Nie można bezpośrednio przechowywać obiektów z konstruktorami lub destruktorami (np.
std::string) w klasycznych uniach. - Odczyt nieaktywnego pola prowadzi do niezdefiniowanego zachowania (UB).
- Nie ma automatycznej informacji, które pole jest aktualnie aktywne – trzeba to śledzić samodzielnie.