Witam. Na pewno nie raz zdarzyło Ci się korzystać z flag bitowych – podczas używania strumieni, tworzenia okna w WinAPI itp. Są one przydatne, gdy chcemy przekazać jakiejś funkcji informacje na temat preferowanych przez nas ustawień:
1 |
ifstream iFile(fName.c_str() , ios::in | ios::binary) ; |
mainJak widać, ustawiamy dwie flagi: ios::in i ios::binary, co oznacza, że plik ma być otwarty do czytania w trybie binarnym. Wygodne, prawda? W jednym parametrze przesłaliśmy nasze wymagania co do tworzonego strumienia. Możemy samemu tworzyć podobne flagi i wykorzystywać je we własnych programach, gdyż nie jest to nic trudnego.
Cóż to te flagi?
Spójrzmy na flagi, które wykorzystujemy podczas tworzenia strumieni:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
enum open_mode { in = 0x01, out = 0x02, ate = 0x04, app = 0x08, trunc = 0x10, nocreate = 0x20, noreplace = 0x40, binary = 0x80 }; |
Jak widać, flagi są kolejnymi elementami typu wyliczeniowego z przypisanymi specyficznymi wartościami, które zapisane są w systemie heksadecymalnym (szesnastkowym). Flagi są zwykle poszczególnymi bitami w słowie long. Za pomocą operatora sumy bitowej | ustawiamy odpowiednie flagi w naszej zmiennej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include using namespace std ; enum moje_flagi { flaga1 = 0x01 , flaga2 = 0x02 , flaga3 = 0x04} ; long test ; int main() { test = flaga1 | flaga2 ; cout << test << endl ; return 0 ; } |
Na ekran zostanie wypisane 3 – taka jest wartość naszej zmiennej po ustawieniu pierwszej i drugiej flagi. Pewnie zadasz pytanie: ale jak teraz sprawdzić, które flagi zostały ustawione? Dobre pytanie ;]
1 2 3 |
test = flaga1 | flaga2 ; if (test & flaga1) cout << "flaga1 ustawiona!" << endl ; |
Pamiętaj jednak, aby nadawać flagom jedynie wartości kolejnych potęg dwójki. Jest to bardzo ważne! Prześledźmy teraz, jak 'działają’ nasze flagi bitowe.
W systemie binarnym (dwójkowym) wartości flaga1, flaga2 i flaga3 przedstawiają się następująco:
flaga1 | 00000001 |
flaga2 | 00000010 |
flaga3 | 00000100 |
Na początku nasza zmienna test ma wartość 0, ale po wykonaniu intrukcji test = flaga1 | flaga2 ; ustawione zostaną w niej te bity, które są ustawione w flaga1 lub flaga2:
1 |
test = flaga1 | flaga2 ; |
flaga1 | 00000001 |
flaga2 | 00000010 |
test | 00000011 |
Dzięki temu, że nasze flagi są wartościami kolejnych potęg dwójki, każda flaga ma w swoim zapisie binarny ustawiony tylko jeden bit na 1, pozostałe 0. Dzięki temu możemy sprawdzić, czy dana flaga została ustawiona, czy nie, tak jak to wcześniej pokazałem za pomocą operatora iloczynu bitowego &:
1 |
if (test & flaga1) cout << "flaga1 ustawiona!" << endl ; |
flaga1 | 00000001 |
test | 00000001 |
Jeżeli oba odpowiadające sobie bity są ustawione, to znaczy, że dana flaga (w tym przypadku flaga1) jest ustawiona w zmiennej (test). Oba najmniej znaczące bity są ustawione, więc na ekranie pojawi się komunikat.
Jeżeli jednak wartość flagi byłaby różna od jednej z potęgo dwójki, to mielibyśmy niechciany efekt:
1 2 3 4 5 6 7 |
enum moje_flagi { flaga1 = 0x01 , flaga2 = 0x02 , flaga3 = 0x03} ; ... test = flaga3 ; if (test & flaga1) cout << "flaga1 ustawiona!" |
Pytanie: czy zobaczymy komunikat? Oczywiście tak, gdyż zapis binarny trójki prezentuje się następująco: 00000011, więc porównując kolejne bity wyjdzie na to, że flaga1 jest ustawiona, poza tym ustawiona jest też flaga2 i flaga3..w tym momencie praca na flagach jest niemożliwa.
Wracając do korzystania z flag: jak widać, za pomocą operatora iloczynu bitowego sprawdzamy, czy dana flaga została ustawiona. Możemy też pójść dalej:
1 |
if (test & flaga1 && !(test & flaga3)) cout << "flaga1 ustawiona, a flaga3 nie!" << endl ; |
Jeżeli chcielibyśmy skasować wszelkie flagi, do zmiennej należałoby przypisać 0:
1 |
test = 0 ; |
Natomiast, usunięcie jednej lub kilku flag wykonuje się w następujący sposób:
1 2 3 |
test &= ~flaga1 ; // usuwamy flaga1 test &= ~flaga1 & ~flaga2 ; // usuwamy obie flagi |
Przypomnę tylko, że powyższy przykład jest równoważny poniższemu:
1 2 3 |
test = test & ~flaga1 ; test = test & ~flaga1 & ~flaga2 ; |
Czas na mały przykład – bardzo prosta baza danych. Flagi wykorzystujemy do tego, żeby ustalić, które dane mają być wyświetlone. Nie jest to zrobione 'superekstra’, to tylko prosty przykład:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
#include #include #include using namespace std ; enum moje_flagi { fImie = 0x01 , fNazwisko = 0x02 , fEmail = 0x04 , fStronaWWW = 0x08} ; long dane = fImie | fNazwisko | fEmail | fStronaWWW ; bool koniec ; class osoba { private : string imie , nazwisko , email , stronaWWW ; public : Wyswietl(long ktoreDane) ; osoba() ; }; void WyswietlMenu() ; void WyswietlOsoby() ; void ZmienTrybWyswietlania() ; vector osoby ; int main() { while (!koniec) { WyswietlMenu() ; } return 0 ; } osoba::osoba() { cout << "Podaj imie:" << endl ; cin >> imie ; cout << "Podaj nazwisko:" << endl ; cin >> nazwisko ; cout << "Podaj email:" << endl ; cin >> email ; cout << "Podaj adres strony www:" << endl ; cin >> stronaWWW ; } osoba::Wyswietl(long ktoreDane) { if (ktoreDane & fImie) cout << imie << " " ; if (ktoreDane & fNazwisko) cout << nazwisko << " " ; if (ktoreDane & fEmail) cout << email << " " ; if (ktoreDane & fStronaWWW) cout << stronaWWW << " " ; cout << endl ; } void WyswietlMenu() { cout << "<---------MENU--------->" << endl ; cout << "1. Dodaj nowa osobe." << endl ; cout << "2. Wyswietl osoby." << endl ; cout << "3. Zmien tryb wyswietlania." << endl ; cout << "4. Koniec." << endl ; cout << "Co chcesz zrobic?" << endl ; int i ; cin >> i ; switch (i) { case 1 : osoby.push_back(osoba()) ; break ; case 2 : WyswietlOsoby() ; break ; case 3 : ZmienTrybWyswietlania() ; break ; case 4 : koniec = true ; break ; } } void WyswietlOsoby() { if (osoby.size() <= 0) { cout << "Brak osob." << endl ; return ; } for (int i = 0 ; i < osoby.size() ; i++) osoby.Wyswietl(dane) ; } void ZmienTrybWyswietlania() { dane = 0 ; int i ; cout << "\nOdpowiedzi: 0 - nie, 1 - tak" << endl ; cout << "Wyswietlac imiona?" << endl ; cin >> i ; if (i) dane |= fImie ; cout << "Wyswietlac nazwiska?" << endl ; cin >> i ; if (i) dane |= fNazwisko ; cout << "Wyswietlac email?" << endl ; cin >> i ; if (i) dane |= fEmail ; cout << "Wyswietlac adres strony www?" << endl ; cin >> i ; if (i) dane |= fStronaWWW ; } |