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. floatint).
  • 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.