Lesson 25. Working with Bit Flags Using STL

Bits can be an efficient way of storing settings and flags. The Standard Template Library (STL) supplies classes that help organize and manipulate bitwise information. This lesson introduces you to

Image The bitset class

Image The vector<bool>

The bitset Class

std::bitset is the STL class designed for handling information in bits and bit flags. std::bitset is not an STL container class because it cannot resize itself. This is a utility class that is optimized for working with a sequence of bits whose length is known at compile time.


Tip

To use class std::bitset, include header:

#include <bitset>


Instantiating the std::bitset

This template class requires you to supply one template parameter that contains the number of bits the instance of the class has to manage:

bitset <4> fourBits;  // 4 bits initialized to 0000

You can also initialize the bitset to a bit sequence represented in a char* string literal:

bitset <5> fiveBits("10101"); // 5 bits 10101

Copying from one bitset while instantiating another is quite simple:

bitset <8> fiveBitsCopy(fiveBits);

Some instantiation techniques of the bitset class are demonstrated by Listing 25.1.

LISTING 25.1 Instantiating a std::bitset


  0: #include <bitset>
  1: #include <iostream>
  2: #include <string>
  3:
  4: int main ()
  5: {
  6:    using namespace std;
  7:
  8:    bitset <4> fourBits;  // 4 bits initialized to 0000
  9:    cout << "Initial contents of fourBits: " << fourBits << endl;
 10:
 11:    bitset <5> fiveBits ("10101"); // 5 bits 10101
 12:    cout << "Initial contents of fiveBits: " << fiveBits << endl;
 13:
 14:    bitset <6> sixBits(0b100001); // C++14 binary literal
 15:    cout << "Initial contents of sixBits: " << sixBits << endl;
 16:
 17:    bitset <8> eightBits (255); // 8 bits initialized to long int 255
 18:    cout << "Initial contents of eightBits: " << eightBits << endl;
 19:
 20:    // instantiate one bitset as a copy of another
 21:    bitset <8> eightBitsCopy(eightBits);
 21:
 23:    return 0;
 24: }


Output Image

Initial contents of fourBits: 0000
Initial contents of fiveBits: 10101
Initial contents of sixBits: 100001
Initial contents of eightBits: 11111111

Analysis Image

The sample demonstrates four different ways of constructing a bitset object. The default constructor initializes the bit sequence to 0, as shown in Line 9. A C-style string that contains the string representation of the desired bit sequence is used in Line 11. An unsigned long that holds the decimal value of the binary sequence is used in Lines 14 and 17, and the copy constructor is used in Line 21. Note that in each of these instances, you had to supply the number of bits that the bitset is supposed to contain as a template parameter. This number is fixed at compile time; it isn’t dynamic. You can’t insert more bits into a bitset than what you specified in your code the way you can insert more elements in a vector than the size() planned at compile time.


Tip

Note the usage of binary literal 0b100001 in Line 14. The prefix 0b or 0B tells the compiler that the following digits are a binary representation of an integer. This literal is new to C++ and introduced in C++14.


Using std::bitset and Its Members

The bitset class supplies member functions that help perform insertions into the bitset, set or reset contents, read the bits, or write them into a stream. It also supplies operators that help display the contents of a bitset and perform bitwise logical operations among others.

Useful Operators Featured in std::bitset

You learned operators in Lesson 12, “Operator Types and Operator Overloading,” and you also learned that the most important role played by operators is in increasing the usability of a class. std::bitset provides some very useful operators, as shown in Table 25.1, that make using it really easy. The operators are explained using the sample bitset you learned in Listing 25.1, fourBits.

Image

TABLE 25.1 Operators Supported by std::bitset

In addition to these, std::bitset also features operators such as |=, &=, ^=, and ~= that help perform bitwise operations on a bitset object.

std::bitset Member Methods

Bits can hold two states—they are either set (1) or reset (0). To help manipulate the contents of a bitset, you can use the member functions as listed in Table 25.2 that can help you work with a bit, or with all the bits in a bitset.

Image

TABLE 25.2 Member Methods of a std::bitset

The usage of these member methods and operators is demonstrated in Listing 25.2.

LISTING 25.2 Performing Logical Operations Using a Bitset


  0: #include <bitset>
  1: #include <string>
  2: #include <iostream>
  3:
  4: int main ()
  5: {
  6:    using namespace std;
  7:    bitset <8> inputBits;
  8:    cout << "Enter a 8-bit sequence: ";
  9:
 10:    cin >> inputBits;  // store user input in bitset
 11:
 12:    cout << "Num 1s you supplied: " << inputBits.count () << endl;
 13:    cout << "Num 0s you supplied: ";
 14:    cout << inputBits.size () - inputBits.count () << endl;
 15:
 16:    bitset <8> inputFlipped (inputBits);  // copy
 17:    inputFlipped.flip ();  // toggle the bits
 18:
 19:    cout << "Flipped version is: " << inputFlipped << endl;
 20:
 21:    cout << "Result of AND, OR and XOR between the two:" << endl;
 22:    cout << inputBits << " & " << inputFlipped << " = ";
 23:    cout << (inputBits & inputFlipped) << endl;  // bitwise AND
 24:
 25:    cout << inputBits << " | " << inputFlipped << " = ";
 26:    cout << (inputBits | inputFlipped) << endl;  // bitwise OR
 27:
 28:    cout << inputBits << " ^ " << inputFlipped << " = ";
 29:    cout << (inputBits ^ inputFlipped) << endl; // bitwise XOR
 30:
 31:    return 0;
 32: }


Output Image

Enter a 8-bit sequence: 10110101
Num 1s you supplied: 5
Num 0s you supplied: 3
Flipped version is: 01001010
Result of AND, OR and XOR between the two:
10110101 & 01001010 = 00000000
10110101 | 01001010 = 11111111
10110101 ^ 01001010 = 11111111

Analysis Image

This interactive program demonstrates not only how easy performing bitwise operations between two-bit sequences using std::bitset is, but also the utility of its stream operators. Shift operators (>> and <<) implemented by std::bitset made writing a bit sequence to the screen and reading a bit sequence from the user in string format a simple task. inputBits contains a user-supplied sequence that is fed into it in Line 10. count() used in Line 12 tells the number of ones in the sequence, and the number of zeroes is evaluated as the difference between size() that returns the number of bits in the bitset and count(), as shown in Line 14. inputFlipped is at the beginning a copy of inputBits, and then flipped using flip(), as shown in Line 17. It now contains the sequence with individual bits flipped—that is, toggled (0s become 1s and vice versa). The rest of the program demonstrates the result of bitwise AND, OR, and XOR operations between the two bitsets.


Note

One disadvantage of STL bitset<> is its inability to resize itself dynamically. You can use the bitset only where the number of bits to be stored in the sequence is known at compile time.

STL supplies the programmer with a class vector<bool> (also called bit_vector in some implementations of STL) that overcomes this shortcoming.


The vector<bool>

The vector<bool> is a partial specialization of the std::vector and is intended for storing boolean data. This class is able to dynamically size itself. Therefore, the programmer does not need to know the number of boolean flags to be stored at compile time.


Tip

To use class std::vector<bool>, include header:

#include <vector>


Instantiating vector<bool>

Instantiating a vector<bool> is similar to a vector, with some convenient overloads:

vector <bool> boolFlags1;

For instance, you can create a vector with 10 boolean values to start with, each initialized to 1 (that is, true):

vector <bool> boolFlags2 (10, true);

You can also create an object as a copy of another:

vector <bool> boolFlags2Copy (boolFlags2);

Some of the instantiation techniques of a vector<bool> are demonstrated by Listing 25.3.

LISTING 25.3 The Instantiation of vector<bool>


  0: #include <vector>
  1:
  2: int main ()
  3: {
  4:     using namespace std;
  5:
  6:     // Instantiate an object using the default constructor
  7:     vector <bool> boolFlags1;
  8:
  9:     // Initialize a vector with 10 elements with value true
 10:     vector <bool> boolFlags2 (10, true);
 11:
 12:     // Instantiate one object as a copy of another
 13:     vector <bool> boolFlags2Copy (boolFlags2);
 14:
 15:     return 0;
 16: }


Analysis Image

This sample presents some of the ways in which a vector<bool> object can be constructed. Line 7 is one that uses the default constructor. Line 10 demonstrates the creation of an object that is initialized to contain 10 boolean flags, each holding the value true. Line 13 demonstrates how one vector<bool> can be constructed as a copy of another.

vector<bool> Functions and Operators

The vector<bool> features the function flip() that toggles the state of the Boolean values in the sequence, similar to the function of bitset<>::flip().

Otherwise, this class is quite similar to the std::vector in the sense that you can, for example, even push_back flags into the sequence. The example in Listing 25.4 demonstrates the usage of this class in further detail.

LISTING 25.4 Using the vector<bool>


  0: #include <vector>
  1: #include <iostream>
  2: using namespace std;
  3:
  4: int main ()
  5: {
  6:    vector <bool> boolFlags(3);  // instantiated to hold 3 bool flags
  7:    boolFlags [0] = true;
  8:    boolFlags [1] = true;
  9:    boolFlags [2] = false;
 10:
 11:    boolFlags.push_back (true); // insert a fourth bool at the end
 12:
 13:    cout << "The contents of the vector are: " << endl;
 14:    for (size_t index = 0; index < boolFlags.size (); ++ index)
 15:       cout << boolFlags [index] << ' ';
 16:
 17:    cout << endl;
 18:    boolFlags.flip ();
 19:
 20:    cout << "The contents of the vector are: " << endl;
 21:    for (size_t index = 0; index < boolFlags.size (); ++ index)
 22:       cout << boolFlags [index] << ' ';
 23:
 24:    cout << endl;
 25:
 26:    return 0;
 27: }


Output Image

The contents of the vector are:
1 1 0 1
The contents of the vector are:
0 0 1 0

Analysis Image

In this sample, the Boolean flags in the vector have been accessed using the operator[], as shown in Lines 7–9, just like you would access a regular vector. The function flip() used in Line 18 toggles individual bit flags, essentially converting all 0s to 1s and vice versa. Note the usage of push_back() in Line 11. Even though you initialized boolFlags to contain three flags in Line 6, you were able to add more to it dynamically at Line 11. Adding more flags than the number specified at compile time is what you cannot do with a std::bitset.


Tip

Since C++11, you may instantiate boolFlags in Listing 25.4 with initial values using List Initialization:

vector <bool> boolFlags{ true, true, false };


Summary

In this lesson, you learned about the most effective tool in handling bit sequences and bit flags: the std::bitset class. You also gained knowledge on the vector<bool> class that allows you to store Boolean flags—the number of which does not need to be known at compile time.

Q&A

Q Given a situation where std::bitset and vector<bool> can both be used, which of the two classes would you prefer to hold your binary flags?

A The bitset, as it is most suited to this requirement.

Q I have a std::bitset object called myBitSet that contains a certain number of stored bits. How would I determine the number of bits that are at value 0 (or false)?

A bitset::count() supplies the number of bits at value 1. This number, when subtracted from bitset::size() (which indicates the total number of bits stored), would give you the number of 0s in the sequence.

Q Can I use iterators to access the individual elements in a vector<bool>?

A Yes. Because the vector<bool> is a partial specialization of the std::vector, iterators are supported.

Q Can I specify the number of elements to be held in a vector<bool> at compile time?

A Yes, by either specifying the number in the overloaded constructor or using vector<bool>::resize() function at a later instance.

Workshop

The Workshop contains quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you’ve learned. Try to answer the quiz and exercise questions before checking the answers in Appendix E, and be certain you understand the answers before going to the next lesson.

Quiz

1. Can the bitset expand its internal buffer to hold a variable number of elements?

2. Why is the bitset not classified as an STL container class?

3. Would you use the std::vector to hold a number of bits that is fixed and known at compile time?

Exercises

1. Write a bitset class that contains four bits. Initialize it to a number, display the result, and add it to another bitset object. (The catch: Bitsets don’t allow bitsetA = bitsetX + bitsetY.)

2. Demonstrate how you would toggle (that is, switch) the bits in a bitset.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.111.9