Składowymi klas mogą być też pola bitowe. Są to składowe klasy o długości wyrażonej w bitach. Ich wartościami są liczby całkowite zapisane (w układzie dwójkowym) w zadeklarowanej ilości bitów. Oczywiście wynika z tego, że zakres tych wartości zależy od zadeklarowanej ilości bitów. Na przykład, deklarując pole bitowe bez znaku o długości 3, możemy tam zapisywać dane całkowite z zakresu [0, 7], bo istnieje osiem zero-jedynkowych kombinacji trzech bitów:
7 6 5 4 3 2 1 0 111 110 101 100 011 010 001 000Można deklarować pola bitowe tylko typu int: ze znakiem (signed) lub bez znaku(unsigned) — podobnie jak dla normalnych typów całkowitych. Liczbę bitów przeznaczonych dla danego pola deklarujemy po dwukropku; na przykład po
struct A { unsigned kolor : 4; // ... };w klasie A istnieje składowa kolor będąca polem bitowym bez znaku o długości czterech bitów. Można zatem tej składowej przypisywać wartości z zakresu [0, 15]. Dlaczego nie użyliśmy zwykłego typu int bez określania ilości bitów? Otóż chodzi o to, że jeśli w klasie występuje kilka pól bitowych, kompilator zadba o to, aby je wszystkie upakować jak najgęściej. Jeśli zatem w klasie mamy pola bitowe o długościach 4, 7 i 3, to, ponieważ jedna liczba typu int zajmuje 32 bity, wszystkie zmieszczą sie w jednym int'cie! Uzyskujemy zatem oszczędność pamięci i mamy możliwość kodowania kilku różnych informacji w pojedynczej fizycznie danej.
W poniższym przykładzie definiujemy klasę opisującą
rodzaj czcionki: w polach bitowych przechowujemy informację o kroju,
wadze i kolorze czcionki:
1. #include <iostream>
2. using namespace std;
3.
4. class Czcionka {
5. unsigned kroj : 3;
6. unsigned waga : 1;
7. unsigned kolor : 2;
8. public:
9. enum Kroj { HELVETICA, TIMES, ARIAL, ➊
10. COURIER, BOOKMAN, SYMBOL};
11. enum Waga { NORMAL, BOLD };
12. enum Kolor { BLACK, RED, GREEN, BLUE };
13.
14. Czcionka(Kroj kroj, Waga waga, Kolor kolor) {
15. this->kroj = kroj;
16. this->waga = waga;
17. this->kolor = kolor;
18. }
19.
20. void opis() {
21. cout << "Kroj nr " << kroj << "; Waga nr "
22. << waga << "; Kolor nr " << kolor << endl;
23. }
24. };
25.
26. int main() {
27. Czcionka tytul(Czcionka::ARIAL, Czcionka::BOLD,
28. Czcionka::RED);
29. Czcionka tekst(Czcionka::TIMES, Czcionka::NORMAL,
30. Czcionka::BLACK);
31. Czcionka greka(Czcionka::SYMBOL, Czcionka::BOLD,
32. Czcionka::BLUE);
33. tytul.opis();
34. tekst.opis();
35. greka.opis();
36. cout << "Rozmiar obiektu: " << sizeof(Czcionka) << endl;
37. }
Pole przewidziane dla wyboru kroju ma długość trzech bitów, a więc może przyjmować wartości od 0 do 7. Tym wartościom nadaliśmy dla wygody nazwy za pomocą wyliczenia Kroj (➊). W ten sposób użytkownik klasy nie musi pamiętać, jakie liczby zostały przypisane poszczególnym krojom — może się do nich odnosić poprzez ich czytelne nazwy. Podobnie postąpiliśmy dla wagi (jeden bit, a więc dwie możliwości odpowiadające liczbom 0 i 1) i koloru (dwa bity, a więc cztery możliwości: 0, 1, 2 i 3). Wydruk z tego programu
Kroj nr 2; Waga nr 1; Kolor nr 1 Kroj nr 1; Waga nr 0; Kolor nr 0 Kroj nr 5; Waga nr 1; Kolor nr 3 Rozmiar obiektu: 4przekonuje nas, że rozmiar obiektu wynosi cztery bajty, a więc kompilator upakował wszystkie trzy pola w jednym int'cie. Ponieważ kilka pól bitowych może być upakowanych przez kompilator w obszarze jednej liczby całkowitej typu int w sposób trudny do przewidzenia, nie ma sensu stosowanie do składowych bitowych operatora wyłuskania adresu (' &'), a zatem i wskaźników.
Pola takie nie mogą być składowymi statycznymi.
Biblioteka standardowa C++ dostarcza klasę (a właściwie szablon klasy) bitset, która zapewnia funkcjonalność pól bitowych, a zawiera wygodny interfejs dla użytkownika i efektywną implementację. Tam, gdzie to możliwe, należy zatem korzystać z tej klasy, a nie z pól bitowych.
T.R. Werner, 21 lutego 2016; 20:17