
- •Introduction
- •Saving Time with This Book
- •Conventions Used in This Book
- •Part II: Working with the Pre-Processor
- •Part III: Types
- •Part IV: Classes
- •Part V: Arrays and Templates
- •Part VI: Input and Output
- •Part VII: Using the Built-in Functionality
- •Part VIII: Utilities
- •Part IX: Debugging C++ Applications
- •Part X: The Scary (or Fun!) Stuff
- •Icons Used in This Book
- •Creating and Implementing an Encapsulated Class
- •Creating a Mailing-List Application
- •Testing the Mailing-List Application
- •Customizing a Class with Polymorphism
- •Testing the Virtual Function Code
- •Why Do the Destructors Work?
- •Delayed Construction
- •The cDate Class
- •Testing the cDate Class
- •Creating the Header File
- •Testing the Header File
- •The Assert Problem
- •Fixing the Assert Problem
- •Using the const Construct
- •Identifying the Errors
- •Fixing the Errors
- •Fixing What Went Wrong with the Macro
- •Using Macros Appropriately
- •Using the sizeof Function
- •Evaluating the Results
- •Using sizeof with Pointers
- •Implementing the Range Class
- •Testing the Range Class
- •Creating the Matrix Class
- •Matrix Operations
- •Multiplying a Matrix by a Scalar Value
- •Multiplying a Matrix by Scalar Values, Take 2
- •Testing the Matrix Class
- •Implementing the Enumeration Class
- •Testing the Enumeration Class
- •Implementing Structures
- •Interpreting the Output
- •Defining Constants
- •Testing the Constant Application
- •Using the const Keyword
- •Illustrating Scope
- •Interpreting the Output
- •Using Casts
- •Addressing the Compiler Problems
- •Testing the Changes
- •Implementing Member-Function Pointers
- •Updating Your Code with Member-Function Pointers
- •Testing the Member Pointer Code
- •Customizing Functions We Wrote Ourselves
- •Testing the Default Code
- •Fixing the Problem
- •Testing the Complete Class
- •Implementing Virtual Inheritance
- •Correcting the Code
- •Rules for Creating Overloaded Operators
- •Using Conversion Operators
- •Using Overloaded Operators
- •Testing the MyString Class
- •Rules for Implementing new and delete Handlers
- •Overloading new and delete Handlers
- •Testing the Memory Allocation Tracker
- •Implementing Properties
- •Testing the Property Class
- •Implementing Data Validation with Classes
- •Testing Your SSN Validator Class
- •Creating the Date Class
- •Testing the Date Class
- •Some Final Thoughts on the Date Class
- •Creating a Factory Class
- •Testing the Factory
- •Enhancing the Manager Class
- •Implementing Mix-In Classes
- •Testing the Template Classes
- •Implementing Function Templates
- •Creating Method Templates
- •Using the Vector Class
- •Creating the String Array Class
- •Working with Vector Algorithms
- •Creating an Array of Heterogeneous Objects
- •Creating the Column Class
- •Creating the Row Class
- •Creating the Spreadsheet Class
- •Testing Your Spreadsheet
- •Working with Streams
- •Testing the File-Reading Code
- •Creating the Test File
- •Reading Delimited Files
- •Testing the Code
- •Creating the XML Writer
- •Testing the XML Writer
- •Creating the Configuration-File Class
- •Setting Up Your Test File
- •Building the Language Files
- •Creating an Input Text File
- •Reading the International File
- •Testing the String Reader
- •Creating a Translator Class
- •Testing the Translator Class
- •Creating a Virtual File Class
- •Testing the Virtual File Class
- •Using the auto_ptr Class
- •Creating a Memory Safe Buffer Class
- •Throwing and Logging Exceptions
- •Dealing with Unhandled Exceptions
- •Re-throwing Exceptions
- •Creating the Wildcard Matching Class
- •Testing the Wildcard Matching Class
- •Creating the URL Codec Class
- •Testing the URL Codec Class
- •Testing the Rot13 Algorithm
- •Testing the XOR Algorithm
- •Implementing the transform Function to Convert Strings
- •Testing the String Conversions
- •Implementing the Serialization Interface
- •Creating the Buffer Class
- •Testing the Buffer Class
- •Creating the Multiple-Search-Path Class
- •Testing the Multiple-Search-Path Class
- •Testing the Flow Trace System
- •The assert Macro
- •Logging
- •Testing the Logger Class
- •Design by Contract
- •Adding Logging to the Application
- •Making Functions Inline
- •Avoiding Temporary Objects
- •Passing Objects by Reference
- •Choosing Initialization Instead of Assignment
- •Learning How Code Operates
- •Testing the Properties Class
- •Creating the Locking Mechanism
- •Testing the Locking Mechanism
- •Testing the File-Guardian Class
- •Implementing the Complex Class
- •Creating the Conversion Code
- •Testing the Conversion Code
- •A Sample Program
- •Componentizing
- •Restructuring
- •Specialization
- •Index

54 Technique 10: Understanding sizeof
You can see from the output the number of bytes that each of the elements we print up occupy in memory for this program. There are no real surprises here, except for the size of the classes. Let’s take a look at what these results mean.
Evaluating the Results
required to return at least 1 byte for every class. This is to ensure the address of one object will never be the same as the address of another object. If C++ permitted objects to have zero size, the compiler wouldn’t be forced to assign those objects a new address in memory. To illustrate, if I wrote the following:
Foo f1;
Foo f2;
There are some interesting conclusions to be made from this output. For example, although some of the results are not surprising at all (for instance, that the size of a character field is 1 byte), some surprises crop up — for example, the size of a character pointer is the same as any other pointer, which turns out to be the size of a long. That means the maximum allowable number of bytes you can allocate using standard pointers is 4 bytes worth — 32 bits. (That’s why Microsoft Windows is a 32-bit operating system. But you knew that.)
You can save a lot of debugging time and design effort by remembering one handy rule: Always check the size of the values you are working with. Rather than hard-code into your application numbers specifically for reading bytes, words, and floating-point values, use the sizeof function to get the correct sizes for the compiler, platform, and operating system you are using.
The next surprise is lurking among the objects in the list: The size of a string is shown as 4 bytes, which can’t possibly be right — the string it’s storing is longer than that. How can that be? The answer is that the sizeof function returns the number of bytes directly allocated by the object — that is, the number of bytes occupied by the private and public variables in the object, plus a few bytes for virtual functions (such as those in the Foo and Bar classes). Notice that even though the Bar class has no member variables, it still takes up 4 bytes because it needs the virtual function table (or v-table) discussed earlier in Technique 2. Now, why does the Foo class take up 1 byte, when it has no virtual methods and no member variables? The answer is that the sizeof function is
the compiler would be free to make both of these objects point at the same location in memory. This is not desirable, even if neither object had any memory allocated to it. Having two objects with the same location would break too many standard library functions. Any function that compared source and destination (for example) would be broken, even if that breakage caused no real harm. The reason for this is that comparison is done by looking at the addresses the two pointers occupy in memory. If the two addresses are the same, the assumption is that what they point at is the same. If two objects have no data in them, but occupy the same position in memory, they are not the same, even if they seem to be.
The Bar class also contains no member variables, but contains a virtual function, and thus pushes the number of allocated bytes to 4. That way of working suggests that there is something very physical about virtual functions, and that you have to incur a memory cost to use that feature.
Even in programming, there is no such thing as a free lunch.
The Full class contains several member variables — a double that takes up 8 bytes, and an integer that takes up 4 — and yet it has 16 allocated bytes. Where do the other 4 bytes come from? You guessed it: from the infamous virtual table, which is created by that virtual destructor. What does this tell us? Even if you don’t have a “normal” virtual method, having a virtual destructor still creates a v-table entry — and that means 4 more bytes in the allocation.

Using sizeof with Pointers |
55 |
The Derived class is puzzling — it looks like it ought to eat up more size than it does. When you look carefully at this class, however, you realize that it contains no virtual function, and neither does the base class from which it is derived. So once again, here is an example of an empty class that takes up a single byte.
Using sizeof with Pointers
No discussion of the sizeof function would be quite complete without a look at a common mistake that C++ programmers make when they use the function. Consider the following little program:
#include <stdio.h> #include <stdlib.h>
const char arr[] = “hello”; const char *cp = arr;
main(){
printf(“Size of array %d\n”, sizeof(arr));
printf(“Size of pointer %dn”, sizeof(cp));
return(0);
}
Because one statement outputs the size of an array and the other the size of a pointer to that array, you would think that the two printf statements in this little program would display the same values. But they do not. In fact, if you take a look at the output, it looks like this:
Size of array 6
Size of pointer 4
The C++ language offers no way to get the size of an array from a single pointer. If you try to use the sizeof operator for that purpose, it will return a valid result but won’t give you what you want.
The size of an array is known at compile time and can be displayed by the sizeof function. On the other hand, a pointer is always the size of a pointer, no matter what it’s pointing at. Furthermore, if you try to return the size of an array by including the statement sizeof(*cp) where cp is the array, you’ll find that the answer is (again) not 6 but 1. Oops. Why is this? Because the expression *cp evaluates to a character, and the size of a single character is always one byte. Be very careful if you’re trying to use the sizeof function on pointers — especially if you want to use the result to represent the size of what’s being pointed at.


Part III
Types


11 Creating Your Own
Basic Types
Technique
Save Time By
Removing duplicated code with self-created basic types
Checking ranges in integer values
Testing self-created basic types
In C++, types are separated into two regions, basic and user-defined. Basic types are those defined by the language, which generally are modeled on types supported directly by the computer hardware. These types include integers, floating point numbers, and characters. Advanced types, such as strings, structures, and classes, fall into the user-defined region. In this technique, we examine the first region, the
basic type. I save the advanced types for the next technique.
How many times have you written a program that required a basic integer variable to be constrained within a given range? You end up duplicating the same code over and over throughout your application, in blocks that look like this:
int value = get_a_value();
if ( value < 0 || value > 10 )
{
printf(“invalid input, try again\n”); return false;
}
Of course, after you have shoehorned all these blocks into the code, your boss comes along and tells you that the folks in the accounting department have decided that ten is no longer the magic number — now it’s 12. So, you modify all of the code, learning something in the process — namely that you’re better off using constants in this situation than variables. Your modifications look like this:
const int maxValue = 12;
int value = get_a_value();
if ( value < 0 || value > maxValue )
{
printf(“invalid input, try again\n”); return false;
}
You check the code into your source-code repository and sure enough, the boss comes into your office again. The accountants have requested another change. While the maximum allowable value is still 12, zeroes