C++ Nuts and Bolts: Casts, Call-by-Reference, and Inheritance
Just like spoken languages, programming languages have nonintuitive areas that are difficult to master, and C++ is no exception. Not learning these difficult areas properly has knock-on effects in that programmers might make mistakes or simply make life harder for themselves. The end result is lower-quality solutions.
This article takes a look at three areas of C++ that present some difficulty:
- Numerical casts
- Call-by-reference
- Inheritance
You’ll see how a clear understanding of these areas allows for elegant, powerful, and concise code.
Let’s get started with an example of numerical casting. All the code is available for download (see [1] in the "Additional Reading" section at the end of this article).
Numerical Casting
Listing 1 shows two integers. One represents an imaginary prize fund for a syndicate of three winners. The prize fund of $20,000 must be divided evenly between the 3 syndicate members.
Listing 1 illustrates a simple example of integer division in which a cast is required.
Listing 1 Implementation
int winnings = 20000; int numOfPeople = 3; Console::Write("Per person winnings: "); Console::WriteLine(winnings/numOfPeople); Console::Write("Per person winnings with cast: "); Console::WriteLine(static_cast<double>(winnings)/numOfPeople); Console::Write("Per person winnings with incorrect cast: "); Console::WriteLine(static_cast<double>(winnings/numOfPeople)); Console::Write("Per person winnings with old form of cast: "); Console::WriteLine(double(winnings)/numOfPeople); Console::Write("Per person winnings with incorrect old form of cast: "); Console::WriteLine (double(winnings/numOfPeople));
In Listing 1, Line 4, I execute an integer division. The two parameters in this division are integers, so the result is also an integer; that is, no fractional part. In Line 6, I use a static_cast<double> statement to cast the first parameter as a double. This has the required effect of producing a result with a fractional part (see the second line of Figure 1).
It’s important to not cast the overall result as a double—that is, static_cast<double>(winnings/numOfPeople)—because this produces an integer result (losing the fractional part) that is then cast as a double (see Line 8 of Listing 1).
The remainder of Listing 1 uses the older form of cast, in which the required type is prepended as double(winnings)/numOfPeople. Personally, I prefer this format to the static_cast approach because it’s more economical and less cumbersome. Also, the static_cast is not backward compatible; that is, it might not work on an older C++ compiler. But I guess the static_cast approach really spells out that you want to cast an item of data and this is now the preferred approach for performing casts.
Regardless of which method you use, it’s important that just one parameter, instead of the entire expression, be cast to double.
Figure 1 illustrates the program output from the code in Listing 1.
Figure 1 Casting the right (and wrong) ways
Instead of casting integers as doubles, you can just convert every number to a double, as illustrated in Listing 2.
Listing 2 All numbers as doubles
double winnings = 20000; double numOfPeople = 3; Console::Write("Per person winnings as doubles: "); Console::WriteLine(winnings/numOfPeople);
The code in Listing 2 produces the output shown in Figure 2.
Figure 2 Using all doubles
Using all doubles certainly solves the problem of needing explicit casts. However, doubles are expensive in terms of resources.
Take a look at Listing 3 and Figure 3 to see the sizes of these two types:
Listing 3 Size of double and int
Console::Write("Sizes of doubles and ints: "); Console::Write(sizeof(double)); Console::Write(" "); Console::WriteLine(sizeof(int));
You can see in Figure 3 that doubles require eight bytes of storage, and ints require four bytes—twice the space.
Figure 3 Size of double and int
This isn’t a big deal for a simple C++ program on a powerful desktop machine. However, if you’re writing code for a small resource-constrained device (such as a mobile phone or for some other embedded environment), you might have to save memory as much as possible.
It’s even possible that the C++ code you write today might at some future date have to be squeezed into a much smaller device than the one for which you wrote it. So it’s important to always think upfront about economy in your code.
Notice the long fractional parts in Figures 1, 2, and 3. They are a bit ugly, so let’s look briefly at how they can be improved.