Often controlling hardware involves turning particular bits on or off or checking their status. The bitwise operators provide the means to perform such actions. We’ll go through the methods quickly.
In the following examples, lottabits
represents a general value, and bit
represents the value corresponding to a particular bit. Bits are numbered from right to left, beginning with bit 0, so the value corresponding to bit position n is 2n. For example, an integer with only bit number 3 set to 1
has the value 23 or 8
. In general, each individual bit corresponds to a power of 2, as described for binary numbers in Appendix A. So we’ll use the term bit to represent a power of 2; this corresponds to a particular bit being set to 1
and all other bits being set to 0
.
The following two operations each turn on the bit in lottabits
that corresponds to the bit represented by bit
:
lottabits = lottabits | bit;
lottabits |= bit;
Each sets the corresponding bit to 1
, regardless of the former value of the bit. That’s because ORing 1
with either 0
or 1
produces 1
. All other bits in lottabits
remain unaltered. That’s because ORing 0
with 0
produces 0
, and ORing 0
with 1
produces 1
.
The following two operations each toggle the bit in lottabits
that corresponds to the bit represented by bit
. That is, they turn the bit on if it is off, and they turn it off if it is on:
lottabits = lottabits ^ bit;
lottabits ^= bit;
XORing 1
with 0
produces 1
, turning an off bit on, and XORing 1
with 1
produces 0
, turning an on bit off. All other bits in lottabits
remain unaltered. That’s because XORing 0
with 0
produces 0
, and XORing 0
with 1
produces 1
.
The following operation turns off the bit in lottabits
that corresponds to the bit represented by bit
:
lottabits = lottabits & ~bit;
These statements turn the bit off, regardless of its prior state. First, the operator ~bit
produces an integer with all its bits set to 1
except the bit that originally was set to 1
; that bit becomes 0
. ANDing a 0
with any bit results in 0
, thus turning that bit off. All other bits in lottabits
are unchanged. That’s because ANDing a 1
with any bit produces the value that bit had before.
Here’s a briefer way of doing the same thing:
lottabits &= ~bit;
Suppose you want to determine whether the bit corresponding to bit
is set to 1
in lottabits
. The following test does not necessarily work:
if (lottabits == bit) // no good
That’s because even if the corresponding bit in lottabits
is set to 1
, other bits might also be set to 1
. The equality above is true only when the corresponding bit is 1
. The fix is to first AND lottabits
with bit
. This produces a value that is 0
in all the other bit positions because 0
AND any value is 0
. Only the bit corresponding to the bit value is left unchanged because 1
AND any value is that value. Thus the proper test is this:
if (lottabits & bit == bit) // testing a bit
Real-world programmers often simplify this test to the following:
if (lottabits & bit) // testing a bit
Because bit
consists of one bit set to 1
and the rest set to 0
, the value of lottabits & bit
is either 0
(which tests as false
) or bit
, which, being nonzero, tests as true
.
C++ lets you define pointers to members of a class. These pointers involve special notations to declare them and to dereference them. To see what’s involved, let’s start with a sample class:
class Example
{
private:
int feet;
int inches;
public:
Example();
Example(int ft);
~Example();
void show_in() const;
void show_ft() const;
void use_ptr() const;
};
Consider the inches
member. Without a specific object, inches
is a label. That is, the class defines inches
as a member identifier, but you need an object before you actually have memory allocated:
Example ob; // now ob.inches exists
Thus, you specify an actual memory location by using the identifier inches
in conjunction with a specific object. (In a member function, you can omit the name of the object, but then the object is understood to be the one pointed to by the pointer.)
C++ lets you define a member pointer to the identifier inches
like this:
int Example::*pt = &Example::inches;
This pointer is a bit different from a regular pointer. A regular pointer points to a specific memory location. But the pt
pointer doesn’t point to a specific memory location because the declaration doesn’t identify a specific object. Instead, the pointer pt
identifies the location of inches
member within any Example
object. Like the identifier inches
, pt
is designed to be used in conjunction with an object identifier. In essence, the expression *pt
assumes the role of the identifier inches
. Therefore, you can use an object identifier to specify which object to access and the pt
pointer to specify the inches
member of that object. For example, a class method could use this code:
int Example::*pt = &Example::inches;
Example ob1;
Example ob2;
Example *pq = new Example;
cout << ob1.*pt << endl; // display inches member of ob1
cout << ob2.*pt << endl; // display inches member of ob2
cout << po->*pt << endl; // display inches member of *po
Here .*
and ->*
are member dereferencing operators. When you have a particular object, such as ob1
, then ob1.*pi
identifies the inches
member of the ob1
object. Similarly, pq->*pt
identifies the inches
member of an object pointed to by pq
.
Changing the object in the preceding example changes which inches
member is used. But you can also change the pt
pointer itself. Because feet
is of the same type as inches
, you can reset pt
to point to the feet
member instead of the inches
member; then ob1.*pt
will refer to the feet member of ob1
:
pt = &Example::feet; // reset pt
cout << ob1.*pt << endl; // display feet member of ob1
In essence, the combination *pt
takes the place of a member name and can be used to identify different member names (of the same type).
You can also use member pointers to identify member functions. The syntax for this is relatively involved. Recall that declaring a pointer to an ordinary type void
function with no arguments looks like this:
void (*pf)(); // pf points to a function
Declaring a pointer to a member function has to indicate that the function belongs to a particular class. Here, for instance, is how to declare a pointer to an Example
class method:
void (Example::*pf)() const; // pf points to an Example member function
This indicates that pf
can be used the same places that Example
method can be used. Note that the term Example:
:
*pf
has to be in parentheses. You can assign the address of a particular member function to this pointer:
pf = &Example::show_inches;
Note that unlike in the case of ordinary function pointer assignment, here you can and must use the address operator. Having made this assignment, you can then use an object to invoke the member function:
Example ob3(20);
(ob3.*pf)(); // invoke show_inches() using the ob3 object
You need to enclose the entire ob3.*pf
construction in parentheses in order to clearly identify the expression as representing a function name.
Because show_feet()
has the same prototype form as show_inches()
, you can use pf
to access the show_feet()
method, too:
pf = &Example::show_feet;
(ob3.*pf)(); // apply show_feet() to the ob3 object
The class definition presented in Listing E.1 has a use_ptr()
method that uses member pointers to access both data members and function members of the Example
class.
// memb_pt.cpp -- dereferencing pointers to class members
#include <iostream>
using namespace std;
class Example
{
private:
int feet;
int inches;
public:
Example();
Example(int ft);
~Example();
void show_in() const;
void show_ft() const;
void use_ptr() const;
};
Example::Example()
{
feet = 0;
inches = 0;
}
Example::Example(int ft)
{
feet = ft;
inches = 12 * feet;
}
Example::~Example()
{
}
void Example::show_in() const
{
cout << inches << " inches
";
}
void Example::show_ft() const
{
cout << feet << " feet
";
}
void Example::use_ptr() const
{
Example yard(3);
int Example::*pt;
pt = &Example::inches;
cout << "Set pt to &Example::inches:
";
cout << "this->pt: " << this->*pt << endl;
cout << "yard.*pt: " << yard.*pt << endl;
pt = &Example::feet;
cout << "Set pt to &Example::feet:
";
cout << "this->pt: " << this->*pt << endl;
cout << "yard.*pt: " << yard.*pt << endl;
void (Example::*pf)() const;
pf = &Example::show_in;
cout << "Set pf to &Example::show_in:
";
cout << "Using (this->*pf)(): ";
(this->*pf)();
cout << "Using (yard.*pf)(): ";
(yard.*pf)();
}
int main()
{
Example car(15);
Example van(20);
Example garage;
cout << "car.use_ptr() output:
";
car.use_ptr();
cout << "
van.use_ptr() output:
";
van.use_ptr();
return 0;
}
Here is a sample run of the program in Listing E.1:
car.use_ptr() output:
Set pt to &Example::inches:
this->pt: 180
yard.*pt: 36
Set pt to &Example::feet:
this->pt: 15
yard.*pt: 3
Set pf to &Example::show_in:
Using (this->*pf)(): 180 inches
Using (yard.*pf)(): 36 inches
van.use_ptr() output:
Set pt to &Example::inches:
this->pt: 240
yard.*pt: 36
Set pt to &Example::feet:
this->pt: 20
yard.*pt: 3
Set pf to &Example::show_in:
Using (this->*pf)(): 240 inches
Using (yard.*pf)(): 36 inches
This example assigned pointer values at compile time. In a more sophisticated class, you can use member pointers to data members and methods for which the exact member associated with the pointer is determined at runtime.
alignof
(C++11)Computer systems can have restrictions on how data is stored in memory. For example, one system might require that a double
value be stored at an even-numbered memory location, whereas another might require the storage to begin at a location that is a multiple of eight. The alignof
operator takes a type as an argument and returns an integer indicating the required alignment type. Alignment requirements can, for example, determine how information is arranged within a structure, as Listing E.2 shows.
// align.cpp -- checking alignment
#include <iostream>
using namespace std;
struct things1
{
char ch;
int a;
double x;
};
struct things2
{
int a;
double x;
char ch;
};
int main()
{
things1 th1;
things2 th2;
cout << "char alignment: " << alignof(char) << endl;
cout << "int alignment: " << alignof(int) << endl;
cout << "double alignment: " << alignof(double) << endl;
cout << "things1 alignment: " << alignof(things1) << endl;
cout << "things2 alignment: " << alignof(things2) << endl;
cout << "things1 size: " << sizeof(things1) << endl;
cout << "things2 size: " << sizeof(things2) << endl;
return 0;
}
Here is the output on one system:
char alignment: 1
int alignment: 4
double alignment: 8
things1 alignment: 8
things2 alignment: 8
things1 size: 16
things2 size: 24
Both structures have an alignment requirement of eight. One implication is that the structure size should be a multiple of eight so that one can create arrays of structures with each element adjacent to the next and also starting on an alignment boundary that’s a multiple of eight. The individual members of each structure in Listing E.2 use only a total of 13 bits, but the requirement of using a multiple of eight bits means each structure needs some padding in it. Next, within each structure, the double
member needs to align on a multiple of eight. The different arrangement of members within things1
and things2
results in things2
needing more internal padding to make the boundaries come out right.
noexcept
(C++11)The noexcept
keyword is used to indicate that a function doesn’t throw an exception. It also can be used as an operator that determines whether its operand (an expression) potentially could throw an exception. It returns false
if the operand could throw an exception and true
otherwise. For example, consider the following declarations:
int hilt(int);
int halt(int) noexcept;
The expression noexcept(hilt)
would evaluate as false
, as the declaration of hilt()
doesn’t guarantee that an exception won’t be thrown. But noexcept(halt)
will evaluate as true
.
18.223.206.69