Oto opcja dla masek bitowych, jeśli tak naprawdę nie masz zastosowania do poszczególnych wartości wyliczenia (np. Nie musisz ich wyłączać) ... i jeśli nie martwisz się o utrzymanie zgodności binarnej, tj .: nie obchodzi cię, gdzie mieszkają twoje bity ... którym prawdopodobnie jesteś. Lepiej też nie przejmuj się zasięgiem i kontrolą dostępu. Hmmm, wyliczenia mają jakieś fajne właściwości dla pól bitowych ... zastanawiam się, czy ktoś kiedykolwiek tego próbował :)
struct AnimalProperties
{
bool HasClaws : 1;
bool CanFly : 1;
bool EatsFish : 1;
bool Endangered : 1;
};
union AnimalDescription
{
AnimalProperties Properties;
int Flags;
};
void TestUnionFlags()
{
AnimalDescription propertiesA;
propertiesA.Properties.CanFly = true;
AnimalDescription propertiesB = propertiesA;
propertiesB.Properties.EatsFish = true;
if( propertiesA.Flags == propertiesB.Flags )
{
cout << "Life is terrible :(";
}
else
{
cout << "Life is great!";
}
AnimalDescription propertiesC = propertiesA;
if( propertiesA.Flags == propertiesC.Flags )
{
cout << "Life is great!";
}
else
{
cout << "Life is terrible :(";
}
}
Widzimy, że życie jest wspaniałe, mamy nasze dyskretne wartości i mamy miłe int & & do naszych serc, które wciąż mają kontekst tego, co oznaczają jego bity. Wszystko jest spójne i przewidywalne ... dla mnie ... dopóki używam kompilatora Microsoft VC ++ w / Update 3 na Win10 x64 i nie dotykam flag mojego kompilatora :)
Mimo że wszystko jest świetne ... mamy teraz pewien kontekst co do znaczenia flag, ponieważ znajduje się on w związku z polem bitowym w strasznym realnym świecie, w którym twój program może być odpowiedzialny za więcej niż jedno dyskretne zadanie, które możesz wciąż przypadkowo (dość łatwo) rozbija dwa pola flag różnych związków (powiedzmy AnimalProperties i ObjectProperties, ponieważ oba są intami), mieszając wszystkie twoje bity, co jest strasznym błędem do wyśledzenia ... i skąd to wiem wiele osób na tym poście nie pracuje zbyt często z maskami bitowymi, ponieważ ich budowa jest łatwa, a utrzymanie ich trudne.
class AnimalDefinition {
public:
static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags ); //A little too obvious for my taste... NEXT!
static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties ); //Oh I see how to use this! BORING, NEXT!
static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something?
AnimalFlags animalFlags; //Well this is *way* too hard to break unintentionally, screw this!
int flags; //PERFECT! Nothing will ever go wrong here...
//wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation?
//Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew!
private:
AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(
}
Więc wtedy ustawiasz swoją deklarację związkową jako prywatną, aby zapobiec bezpośredniemu dostępowi do „Flagi”, i musisz dodać pobierające / ustawiające i przeciążające operatorów, a następnie utworzyć makro dla tego wszystkiego, i jesteś w zasadzie tam, gdzie zacząłeś, kiedy próbowałeś zrób to z Enum.
Niestety, jeśli chcesz, aby Twój kod był przenośny, nie sądzę, że istnieje jakikolwiek sposób na A) zagwarantowanie układu bitów lub B) określenie układu bitów w czasie kompilacji (abyś mógł go śledzić i przynajmniej poprawiać zmiany w różnych wersje / platformy itp.)
Przesunięcie w strukturze z polami bitowymi
W czasie wykonywania możesz grać w sztuczki z ustawieniem pól i XOR-owaniem flag, aby zobaczyć, które bity się zmieniły, brzmi to dla mnie dość głupio, chociaż wersety mają w 100% spójne, niezależne od platformy i całkowicie deterministyczne rozwiązanie, tj. ENUM.
TL; DR: Nie słuchajcie hejterów. C ++ nie jest językiem angielskim. To, że dosłowna definicja skróconego słowa kluczowego odziedziczonego po C może nie pasować do twojego zastosowania, nie oznacza, że nie powinieneś go używać, gdy definicja słowa kluczowego C i C ++ bezwzględnie obejmuje przypadek użycia. Możesz także użyć struktur do modelowania rzeczy innych niż struktury i klas do rzeczy innych niż szkoła i kasta społeczna. Możesz użyć float dla wartości, które są uziemione. Możesz użyć char dla zmiennych, które nie są wypalone ani osoby w powieści, sztuce lub filmie. Każdy programista, który idzie do słownika, aby ustalić znaczenie słowa kluczowego przed specyfikacją języka, jest ... no cóż, trzymam tam język.
Jeśli chcesz modelować swój kod na podstawie języka mówionego, najlepiej pisz w Objective-C, który nawiasem mówiąc, często wykorzystuje wyliczenia dla pól bitowych.
[Flags]
atrybut działa dobrze, tj .:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};