Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ Timesaving Techniques (2005) [eng].pdf
Скачиваний:
65
Добавлен:
16.08.2013
Размер:
8.35 Mб
Скачать

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