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

50 Technique 9: Macros and Why Not to Use Them

LISTING 9-1 (continued)

printf(“New string: [%s]\n”, ns ); char *s2 = new char[80];

strcpy( s2, “This is a really long string to test something”); COPY_AND_TRUNC( s2, s2 ); printf(“New string: [%s]\n”, ns );

return 0;

}

Note that you can create a multiple line macro by using the backslash (‘\’) character at the end of the previous line. Doing so expands the macro until it’s almost a complete function.

3. Compile the program with your favorite compiler on your favorite operating system.

4. Run the program on your favorite operating system.

If you’ve done everything properly, you will see the following output:

$ ./a.exe

New string: [This is a really lo] New string: [(null)]

Fixing What Went Wrong with the Macro

What happened here? The output of the last function call should have been the same as the first one! This is a serious problem that can be traced to a side effect of the macro. Because the procedure didn’t check to see whether input and output were the same, you cannot safely delete the character-pointer buffer that you didn’t allocate. However, by following the steps given here, you can rewrite this macro as an equivalent — but safer — function.

1. Reopen the source file in your code editor.

2. Make changes to the source code as shown in

Listing 9-2. Note that the lines to be modified are shown at 1 and 2. The blocks of code shown here should be added.

LISTING 9-2: THE UPDATED MACRO FILE

#include <stdio.h> #include <string.h>

// This will be our macro version #define COPY_AND_TRUNC(ns, s) \

if ( strlen(s) > 20 ) \ { \

ns = new char[ 20 ]; \ memset( ns, 0, 20 ); \ strncpy( ns, s, 20-1 ); \

}\

else \ { \

ns = new char[ strlen(s) ]; \ memset( ns, 0, strlen(s) ); \ strcpy( ns, s ); \

}\

delete s; \ s = NULL;

char *copy_and_truncate( char *& s )

 

1

{

 

char *temp = NULL; if ( strlen(s) > 20 )

{

temp = new char[ 20 ]; memset( temp, 0, 20 ); strncpy( temp, s, 20-1 );

}

else

{

temp = new char[ strlen(s) ]; memset( temp, 0, strlen(s) ); strcpy( temp, s );

}

delete s; s = NULL;

return temp;

}

int main(int argc, char **argv )

{

char *s = new char[80];

strcpy( s, “This is a really long string to test something”);

Using Macros Appropriately

51

char *ns = NULL;

 

 

COPY_AND_TRUNC( ns, s );

 

 

printf(“New string: [%s]\n”, ns );

 

 

char *s2 = new char[80];

 

 

strcpy( s2, “This is a really long

 

 

string to test something”);

 

 

COPY_AND_TRUNC( s2, s2 );

 

 

printf(“New string: [%s]\n”, s2 );

 

 

char *s3 = new char[80];

 

 

strcpy( s3, “This is a really long

 

 

string to test something”);

 

2

s3 = copy_and_truncate( s3 );

 

printf(“New string: [%s]\n”, s3 );

 

}

3. Save the source code in your source-code editor and close the source-code editor application.

4. Compile the program using your favorite compiler on your favorite operating system.

If you have done everything properly, this time you should see the following output in your console window:

$ ./a.exe

New string: [This is a really lo] New string: [(null)]

New string: [This is a really lo]

Note that this time, your function did exactly what you expected it to do. Not only did you not wipe out your pointer, you also did not cause the memory leak that the previous version caused. Okay, imagine having to hassle with macros like that over and over just to get your work done. To avoid all that aggrava-

tion, I recommend choosing functions for anything but the very simplest macros. The function shown in the modified code causes no problems, whereas the macros in the initial listing do. This should illustrate the problems caused unintentionally by macros.

Using Macros Appropriately

What are macros good for, then? Remember, a macro is nothing more (and nothing less) than syntactical sugar; it’s easy to wind up with too much of a good thing. Using a heap of macros may make reading your coding easier, but you should never modify your code itself with a macro. For example, if you have a particularly complex expression — such as (*iter).c_str() — which can occur when you’re using the Standard Template Library (STL) in C++ — you could create a macro that says:

#define PTR(x) (*x)

Then you can write PTR(x).c_str(), and however often you write it, the definition will be consistent. This isn’t a complicated example, but it gives you an idea of when and when not to use macros in your C++ applications. The macro is straightforward, has no side effects, and makes the code easier to read later. These are all good reasons to use a macro.

If you are trying to generalize a block of code, use templates instead of macros. Your finished source code is more compact that way, and debugging considerations are easier.

10 Understanding

sizeof

Technique

Save Time By

Using the sizeof function

Exploring and understanding the byte sizes of various types

Using sizeof with pointers

The sizeof operator is not technically a part of the pre-processor, but it should be thought of as one. The sizeof operator, as its name implies, returns the size, in bytes, of a given piece of informa-

tion in your application. It can be used on basic types — such as int, long, float, double, or char * — and on objects, classes, and allocated blocks as well. In fact, anything that is a legitimate type can be passed to the sizeof function.

The sizeof function is extremely useful. If (for example) you want to allocate a block of memory to hold exactly one specific type of data, you can use the sizeof function to determine how many bytes you need to allocate, like this:

int bytes = sizeof(block);

char *newBlock = new char[bytes]; memcpy( newBlock, block, bytes );

This capability is also useful when you’re saving an object into memory while performing a global undo function. You save the state of the object each time it’s going to change, and then return it to that saved state by simply copying the block of memory over it. There are other ways to do this task, of course, but this one is simple and very extensible.

In the following sections, I show you what sizeof can and cannot do.

Using the sizeof Function

The sizeof function can be valuable in determining system configurations, sizes of classes, and illustrating many of the internals of the C++ system. The following steps show you how the sizeof function is used, and how it can show you something about the internals of your own code:

1. In the code editor of your choice, create a new file to hold the code for the source file of the technique.

In this example, the file is named ch10.cpp, although you can use whatever you choose.

Using the sizeof Function

53

2. Type the code from Listing 10-1 into your file.

Better yet, copy the code from the source file on this book’s companion Web site.

LISTING 10-1: THE SIZEOF PROGRAM

#include <stdio.h> #include <string>

class Foo

{

public: Foo() {};

~Foo() {};

};

class Bar

{

public: Bar() {};

virtual ~Bar() {};

};

class Full

{

int x; double y;

public:

Full()

{

}

virtual ~Full()

{

}

};

class Derived : public Foo

{

public: Derived() {};

~Derived() {};

};

int main()

{

int x = 0; long y = 0; float z = 0; double d = 0.0;

std::string s = “hello”;

// Basic types

printf(“size of char: %d\n”, sizeof(char));

printf(“size of char *: %d\n”, sizeof(char *));

printf(“size of int: %d\n”, sizeof(x)); printf(“size of long: %d\n”, sizeof(y)); printf(“size of float: %d\n”, sizeof(z));

printf(“size of double: %d\n”, sizeof(d));

printf(“size of string: %d\n”, sizeof(s) );

printf(“size of Foo: %d\n”, sizeof(Foo));

printf(“size of Bar: %d\n”, sizeof(Bar));

printf(“size of Full: %d\n”, sizeof(Full));

printf(“size of Derived: %d\n”, sizeof(Derived));

}

3. Save the source code as a file in the code editor and then close the editor application.

4. Compile the program, using your favorite compiler on your favorite operating system.

5. Run the program.

If you have done everything properly, you should see the following output in your console window:

$ ./a.exe

size of char: 1 size of char *: 4 size of int: 4 size of long: 4 size of float: 4 size of double: 8 size of string: 4 size of Foo: 1 size of Bar: 4 size of Full: 16 size of Derived: 1