Creating Data Structures - Advanced Programming - C++ All-in-One For Dummies (2009)

C++ All-in-One For Dummies (2009)

Book IV

Advanced Programming

Chapter 2: Creating Data Structures

In This Chapter

Discovering all the different data types

Casting and converting

Using structures with your data

Comparing and manipulating structures

C++, being a computer language and all, provides you with a lot of ways to manipulate data — numbers, letters, strings, arrays — anything you can store inside the computer memory. To get the most out of C++, you should know as much as you can about the fundamental data types. This chapter covers them and how to use them.

In this chapter, we refer to the ANSI standard of C++. ANSI is the American National Standards Institute. The information we provide in this chapter deals with the ANSI standard (singular) of C++. Fortunately, the GNU gcc compiler that comes with CodeBlocks is ANSI-standard compliant. (Little nuances show up, but not often.)

Working with Data

In the sections that follow, we tell you how you can manipulate your data, including the types of data available to you and how you can change them.

The great variable roundup

The ANSI C++ standard dictates these fundamental C++ types:

♦ char: This is a single character. On most computers, it takes 1 byte.

♦ int: This is an integer. On most of the computers in the late 1990s and early 2000s, a single integer takes 4 bytes. With 4 bytes, this gives you a range from -2147483648 to 2147483647.

♦ short int: This is a half-sized integer. Just a little 2-byte fellow, which leaves just enough room for -32768 to 32767.

♦ long int: You would expect a long int to be longer than, well, an int. But it’s not with the gcc compiler. The compiler recognizes two types: short int (2 bytes) and long int (4 bytes). If you leave off the first word, the compiler considers it a long int. So a long int is an int.

♦ bool: This can take on a value of either true or false. Inside the computer, it’s a single byte, stored as a number. Normally true is stored as 1, and false is stored as 0. However, you shouldn’t have to convert bool to a number; you should only compare it to the valuestrue or false.

♦ float: This is a number with a decimal point (a floating-point number). The ANSI standard doesn’t define this, but gcc uses 4 bytes.

♦ double: This is another floating-point type, and it means double-precision floating-point. Again, the ANSI standard doesn’t say how long it should be. The gcc compiler uses 8 bytes for a double.

♦ long double: This is a humongous size, a real space hog. In gcc (at the time of this writing), it takes up a whole 12 bytes of space.

♦ void: The ANSI standard considers this an incomplete type. You’re not allowed to declare a variable of type void. However, you can declare a type pointer to void.

♦ wchar_t: Many computers today support a wide character set, primarily for international and non-English characters. The characters in these sets usually are 2 bytes. wchar_t represents these characters. Some operating systems (such as Windows CE) require you to usewchar_t.

You can use some variations of these. You can have arrays of any of these. And you can also modify some of these just a bit:

♦ signed: You can tack the word signed to the beginning of char, short int, int, and long int to get signed char, signed short int, signed int, and signed long int. If you put signed in front, the numbers (and underlying numbers for thechar type) can include negatives or positives.

♦ unsigned: You can put the word unsigned at the beginning of these types to get unsigned char, unsigned short int, unsigned int, and unsigned long int. Unsigned means that the numbers (and underlying numbers for the char type) cannot be negative.

Now when you use signed and unsigned, the size of the variable doesn’t change: It takes the same number of bytes. Instead, the range shifts. For example, a signed short int ranges from -32768 to 32767, so there are 65536 possibilities. An unsigned short int ranges from 0 to 65535; again, there are 65536 possibilities.

The easiest way to see how the signed integers are stored is to use hexadecimal (hex). The hex numbers line themselves up nicely with the bytes. An unsigned short int can hold any hex values from 0x0000 through 0xffff. These two numbers correspond to the decimal numbers 0 through 65535. Now if you put these same numbers into a signed short int, you can see how they’re stored. Here’s how:

short int hoopla;

hoopla = 0x0000;

cout << “0x0000: “ << hoopla << endl;

hoopla = 0x0001;

cout << “0x0001: “ << hoopla << endl;

hoopla = 0x7fff;

cout << “0x7fff: “ << hoopla << endl;

hoopla = 0x8000;

cout << “0x8000: “ << hoopla << endl;

hoopla = 0xffff;

cout << “0xffff: “ << hoopla << endl;

When you run this code, here’s what you see:

0x0000: 0

0x0001: 1

0x7fff: 32767

0x8000: -32768

0xffff: -1

These numbers in the output are out of order. Here they are in the correct order:

♦ Negative numbers from -32768 to -1 are stored from 0x8000 to 0xffff.

♦ The number 0 is stored as 0x0000, as you would expect.

♦ Positive numbers from 1 to 32767 are stored from 0x0001 to 0x7ffff.

The larger integers behave similarly. For signed long int, the negatives are stored from 0x800000000 to 0xffffffff. The number 0 is 0x00000000. Positives go from 0x00000001 to 0x7fffffff.

image When you are working with the different floating-point types, remember this rule: It’s not about range, it’s about precision. The double doesn’t just hold a bigger range of numbers than the float type; it holds more decimal places.

image void * is just a generic type pointer. If you want a pointer and don’t want to specify what type it points to, you can make it a void * . If you’re writing a C++ program that uses structures from an older C program, you may see void * crop up. When you use a void * pointer, normally you must cast it to another pointer type (such as MyStruct *).

Expressing variables from either side

Occasionally when you look at error messages (or if you read the ANSI standard!) you see the terms lvalue and rvalue. The l and r refer to left and right, respectively. In an assignment statement, an lvalue is any expression that can be on the left side of the equals sign, and anrvalue is an expression that can be on the right side of an equals sign.

image The terms lvalue and rvalue do not refer to what happens to be on the left side and right side of an assignment statement. They refer to what is allowed or not allowed on the left side of an assignment statement. You can only have lvalues on the left side of an assignment statement and rvalues on the right side of an assignment statement.

Here are some examples, where ploggle is an int type. This is allowed, because ploggle is an lvalue:

ploggle = 3;

On the left side, you cannot have items that are strictly an rvalue. The following is not allowed, because 2 is strictly an rvalue:

2 = ploggle;

Now how do we know ploggle is an lvalue? Because it’s allowed to appear on the left side of an assignment statement. The number 2 can’t appear on the left (setting it equal to something else makes no sense!), therefore it isn’t an rvalue. In fact, anything you can set equal to something else is an lvalue.

The main reason you need to know these terms is their tendency to show up in error messages. If you try to compile the line 2 = ploggle, here are some error messages that appear (one for each of three compilers):

♦ Borland C++ Builder: Lvalue required

♦ gcc (whether CodeBlocks, MinGW, or Cygwin): non-lvalue in assignment

♦ Visual C++: left operand must be l-value

If you don’t know what the term lvalue means, these messages can be confusing. And while seeing the problem with 2 = ploggle is pretty easy, sometimes the problem is not that obvious. Look at this:

ChangeMe() = 10;

DontKnow() = 20;

Do these even make sense? Putting a function call on the left side? In other words: Are the expressions ChangeMe() and DontKnow() lvalues?

It depends. Take a look at this code:

int uggle;

int &ChangeMe()

{

return uggle;

}

int DontKnow()

{

return uggle;

}

The function ChangeMe returns a reference to an integer; this line is valid:

ChangeMe() = 10;

The expression ChangeMe() refers to the variable uggle, and thus this line of code stores 10 in uggle. But the second function, DontKnow, returns just an integer value (a number, not a variable). Therefore, this line is not valid:

DontKnow() = 20;

The left side, DontKnow() is not an lvalue — it’s an rvalue — therefore it cannot appear on the left side of an equation, and that line is an error.

Indeed, when we try to compile these lines, the compiler is happy with the ChangeMe() = 10; line. But for the DontKnow() = 20; line, it gives us the following error message.

error: non-lvalue in assignment

image The words lvalue and rvalue are not C++ words. You do not type these into a program. (Well, yeah, we suppose you could use them as variable names, but we’d really rather not — and we suggest you don’t, either.)

Casting a spell on your data

Although C++ has all these great data types, such as integer and char, the fact is this: Underneath they are just stored as numbers. And sometimes you may have a character and need to use its underlying number. To do this, you can cast the data.

The way you cast is to take a variable of one type and type the variable’s name preceded with the other type you would like it to be. You put the other type in parentheses.

char buddy = ‘A’;

int underneath = (int)buddy;

cout << underneath << endl;

Comparing casting and converting

The idea behind casting is to take some data and, without changing it (and with nothing up our sleeves), use it in another way. For example, we could have an array containing the characters Applecrisp. But inside the memory, each letter is stored as a number. For example, the A is stored as 65, p is stored as 112, and l as 108. Therefore, if we wanted to, we could cast each character to an integer, using such code as

cout << (int)(str[loop]) << endl;

where str is the string (Applecrisp) and loop is a loop counter that cycles through the string. This would print out the numerical equivalents of each letter. In other words, we cast the characters to integers — but we did not actually change any data. Now we can copy the data like this:

int num = str[0];

This code would copy the data, but again, it wouldn’t change it. We’d just have two copies of the same data. That’s what casting is all about: using data as a different data type from what it was originally.

Converting, however, is different. If we want to take the number 123, casting it to a string will not create a string 123. The string 123 is made up of three underlying byte-sized snacks of numbers. The numbers for the string 123 are 49, 50, and 51, respectively. Casting the number 123just won’t give us that. Instead, you would need to convert the number to a string.

But, like most rules, this one has an exception, and that exception comes into play when converting between floats and integers. Instead of using a conversion function, the C++ compiler automatically converts from float to integer and vice versa if you try to cast one to the other. Ugh. That goes against the rest of the rules, so be careful. Here’s an example of converting a float to an integer:

float f = 6.3;

int i = (int)f;

But the crazy part is that you can also do the same thing without even using the cast, although you will get a compiler warning:

float f = 6.3;

int i = f;

When you run this code, you obtain an output value of 65. If you substituted a lowercase a, the output would be 97 because uppercase and lowercase letters have different numeric values.

image Back in the old days of the C programming language, casting was a common way of converting data — but it’s actually somewhat dangerous. In C, you could take any data type and directly cast it to any other data type. The idea was that if you wanted to burrow into the system and manipulate something (just about anything), you could. But over the years, people started to figure out that maybe, just maybe, this wasn’t such a good idea. (As in, “Hey, bugs, welcome to my computer!”) So although we’re showing you how to cast, you should try to avoid casting. Instead, focus on converting (which sometimes uses castlike syntax) or using safe casts. See Chapter 8 in Minibook 1as well as the next section (“Casting safely with C++”) in this chapter.

Casting safely with C++

With the ANSI standard of C++ came all kinds of new goodies that make life easier than it used to be. Casting is one example. Originally, you could just cast all you wanted and change from one data type to another, possibly causing a mess, especially if you take existing code and compile it under a different operating system or perhaps even under a different compiler on the same operating system. One type may have a different underlying representation, and then, when you convert it on one system, you get one thing; take it to a different system and you get something else. That’s bad. It creates bugs!

So the ANSI standard for C++ gives some newer and better ways of casting between data. These include dynamic_cast and static_cast.

Dynamically casting with dynamic_cast

When the makers of C++ came up with these new ways of casting, their motivation was this: Think in terms of conversions. A cast simply takes one data type and tells the compiler to treat it as another data type. So first ask yourself if one of the conversions will work for you. If not, you can consider one of the new ways of casting.

But remember, a cast tells the compiler to treat some data as another type of data. But the new ways of casting prevent you from doing a cast that doesn’t make sense. For example, you may have a class hierarchy, and you have a pointer to a base class. But because an instance of a derived class can be treated as an instance of a base class, it’s possible that this instance that you’re looking at could actually be an instance of a derived class.

In the old style of C and C++ programming, you could just cast the instance and have at it:

DoSomethingCool( (derivedclass *) someptr);

In this case, we’re assuming that someptr is of type pointer-to-base-class, but we’re hoping that, in fact, it points to a derivedclass instance. Does it? It may, depending on how we wrote the rest of the program. But maybe not. And when the word hope meets the word program, the word disaster tends to show up. Tempers fly and people lose their jobs. It’s not a pretty sight.

But have no fear: ANSI is here! With the new ways of casting, you can be sure. Listing 2-1 is a complete program that demonstrates a proper down-cast, where we take a pointer to a base class and cast it down to a pointer of a derived class.

Listing 2-1: Casting Instances Dynamically for Safety

#include <iostream>

#include <string>

using namespace std;

class King

{

protected:

string CrownName;

public:

virtual string &MyName() { return CrownName; }

virtual ~King(){}

};

class Prince : public King

{

public:

string School;

};

void KingInfo(King *inst)

{

cout << “=========” << endl;

cout << inst->MyName() << endl;

Prince *asPrince = dynamic_cast<Prince *>(inst);

if (asPrince != 0)

{

cout << asPrince->School << endl;

}

}

int main()

{

Prince George;

George.MyName() = “George I”;

George.School = “School of the Kings”;

KingInfo(&George);

King Henry;

Henry.MyName() = “Henry II”;

KingInfo(&Henry);

return 0;

}

Go ahead and run this code. You’ll see output that looks something like this:

=========

George I

School of the Kings

=========

Henry II

Process returned 0 (0x0) execution time : 0.062 s

Press any key to continue.

Some strange things are going on in this code. But first, we want to point out the main thing that this code demonstrates: In the main main, we call the KingInfo function, first passing it the address of George (a Prince instance, derived from King) and then the address of Henry(a King instance).

The KingInfo function first prints the information that is common to both due to inheritance; it calls the MyName function and prints the resulting name. Then comes the important part, the dynamic cast. To do this, we call dynamic_cast and save it in a pointer variable calledasPrince. Notice the syntax of dynamic_cast. It looks like a template in that you include a type in angled brackets. Then you put the thing you want to cast in parentheses (in this case, the instance that was passed into the function).

If the dynamic cast worked, it returns a pointer that you can save as the type inside angled brackets. Otherwise, the dynamic cast will return 0. You can see that after we called dynamic_cast, we tested it against 0. If the result is not 0, the dynamic cast worked, which means that we successfully cast the data to the desired type. And then, in the if block, we retrieve the School member, which is part of Prince, not King.

You may notice the unique design of the King class. Take a look at just the King class:

class King

{

protected:

string CrownName;

public:

virtual string &MyName() { return CrownName; }

virtual ~King(){}

};

For dynamic_cast to work, the base class involved must have at least one virtual function. Thus the base class — and each of its derived classes — has a virtual table (also needed for dynamic_cast to work). In addition, the CodeBlocks compiler raises a warning message when you don’t provide a virtual destructor:

warning: `class King’ has virtual functions but non-virtual destructor

Consequently, the example includes a virtual destructor as well.

Originally, we wanted CrownName to be public. But because we needed to add a virtual function to class King, we decided to make the function useful, rather than just add a function that does nothing at all. So we made it access the CrownName member. And for that, we wanted to give the function a reason for its existence, so we made the CrownName protected. Then we had the MyName function return a reference to it. The end result is that it’s like CrownName is public, which is the way we wanted the class in the beginning.

imageYou don’t need to use references in a class as we did here just to make dynamic_cast work. But you do need at least one virtual function.

imageSome compilers (including Microsoft Visual C++) do not, by default, handle dynamic_cast. To use a dynamic cast in Visual C++, you have to go into the project settings and select Enable Run-time Type Information (RTTI) found in C++ Language Settings. Then you need to recompile your program for the change to take effect. Note that if you don’t select this setting, you get a warning (not an error) that says, dynamic_cast used on polymorphic type ‘class King’ with /GR-; unpredictable behavior may result. The program will still compile and link, but when you run it, a runtime error message pops up.

Remember, here’s the fundamental difference between an old-style direct cast and the new dynamic_cast: The compiler generates code that automatically does an old-style cast, regardless of whether the cast is valid. That is, the cast is hardcoded. But dynamic_cast, on the other hand, tests the types at runtime. The dynamic cast may or may not work depending on the type of the object.

When you use a dynamic cast, you can cast either a pointer or a reference. The KingInfo function back in Listing 2-1 uses a pointer. Here’s a modified form that uses a reference:

void KingInfoAsReference(King &inst)

{

cout << “=========” << endl;

cout << inst.MyName() << endl;

try

{

Prince &asPrince = dynamic_cast<Prince &>(inst);

cout << asPrince.School << endl;

}

catch (...)

{

}

}

To make this work, we had to use an exception handler (which is a way to deal with unusual situations; see Chapter 3 in this Minibook for more information on exception handlers). The reason for using an exception handler is that with a pointer, you can simply test the result against 0. But with references, you have no such thing as a null reference or 0 reference. The reference must work, or you get a runtime error. And in C++, the way you can catch a situation that didn’t work is by typing the word try, followed by your code that attempts to do the job, in braces. Follow that with the word catch and a set of parentheses containing three periods. Following that, you put braces — and possibly any code you want to run — just in case the earlier code didn’t work.

In this code, you can see that we didn’t do anything inside the catch block. We want to do something only if the code works. We would just leave off the catch block because we didn’t have it do anything, but, alas, C++ doesn’t allow that: If you have a try block, you must have acatch block. Them’s the rules.

Statically casting with static_cast

The ANSI C++ standard includes a special type of cast that does no type checking. If you have to cast directly without the help of dynamic_cast, you should opt for static_cast instead of the old C-style cast.

When you want to do a static cast, call static_cast and follow it with angled brackets containing the type you want to cast to. Then put the item being cast inside parentheses, as in the following:

FinalType *f = static_cast<FinalType *>(orig);

The advantage of using static_cast is that it does some type checking at compile time, whereas old C-style casts do not. The compiler allows you to do static_cast only between related objects. For example, you can do a static_cast from an instance of one class to an instance of a derived or base class. But if two classes are not related, you will get a compiler error.

For example, suppose that you have these two lines of code:

class FinalType {};

class AnotherType {};

They are unrelated classes. Then if you have these lines of code

AnotherType *orig = new AnotherType;

FinalType *f = static_cast<FinalType *>(orig);

and you try to compile it, you get an error:

static_cast from `AnotherType *’ to `FinalType *’

But if you instead make the classes related

class FinalType {};

class AnotherType : public FinalType {};

then these two lines will compile (if you’re using the CodeBlocks compiler, you will receive a message, warning: unused variable ‘f’):

AnotherType *orig = new AnotherType;

FinalType *f = static_cast<FinalType *>(orig);

imageA lot of people think that static_cast is useless and is essentially identical to old-style C casts. This is not true. Tell them they’re wrong! (But be nice, now. Friends come before software, after all.) The mistaken notion is that static_cast does no type checking, when in fact it does. The difference between static_cast and dynamic_cast is that static_cast does its type checking at compile time; the compiler makes sure the cast is okay. dynamic_cast, however, also does the same type checking at compile time, but when it runs, it does more checking to make sure that the instance is precisely what you’re converting to. Old C-style casts do none of this type checking.

If you’re just doing a conversion between floating-point numbers and integers, you can do an old-style cast. (That’s because an old-style cast is really a conversion, not a cast.) Alternatively, of course, you’re welcome to use static_cast to get the same job done:

float f = static_cast<float>(x);

Structuring Your Data

Before C++ came to life, C had something that was similar to classes, called structures. The difference was that structures had only member variables — no member functions. Here’s an example of a structure:

struct Dimensions

{

int height;

int width;

int depth;

int weight;

int price;

};

This block of code is similar to a class; as you can see, it has some member variables but no member functions. Nor does it have any access control (such as public, private, or protected).

But not only did the designers of C++ add classes to C++, they also enhanced the structures in C++. So now in C++, you can use structures more powerfully than you could in C. The main change to structures in C++ is that they can have member functions and access control. Thus, we can add to the Dimensions structure like so:

struct Dimensions

{

private:

int price;

public:

int height;

int width;

int depth;

int weight;

int GetPrice() { return price; }

};

Then we can create an instance of Dimensions in our code like this:

Dimensions FirstIem;

Dimensions *SecondItem = new Dimensions;

Well now, isn’t that interesting? This struct business is looking suspiciously like a class, wouldn’t you say? Hmmm. As it happens, the struct code is a class. It’s the same thing.

image When the great founder of the C++ language (Bjarne Stroustrup) created C++, he enhanced structures to the point that classes and structures are identical, with one exception. Members of a structure are public by default. Members of a class, however, are private by default.

That’s nice. But why would you use a structure? Really, it doesn’t matter. Most C++ programmers today never even touch a structure.

image However, some C++ programmers use a special convention concerning structures. If a class has only public member variables and no member functions, then make it a structure.

In other words, programmers use structure for simple data types that are themselves a collection of smaller data types. (That is, they use structures in the same way C originally had it.) That’s actually a pretty good idea. In the sections that follow, we tell you about some of these data structure issues. (And for what it’s worth, keeping structures around in C++ was a good idea because a lot of people originally took C programs and recompiled them with a C++ compiler. It was good that the C++ compiler handled the structures.)

image If you’re familiar with C and just learning C++, you may be interested to know that when you declare a variable that is a structure type, in C++ you need to give only the name of the structure. You no longer need the word struct in the declaration. Thus, the following line will still compile in C++

struct Dimensions another;

but all you really need is

Dimensions another;

Structures as component data types

A common use of structures is as an advanced data type made up of underlying data types. For example, a lot of operating systems that deal with graphics include some libraries that require a Point structure. Typically, a Point structure is simply a grouping of an X-coordinate and a Y-coordinate, all in one package.

You might declare such a structure like this:

struct Point

{

int x;

int y;

};

Then, when you need to call a function that requires such a structure, such as one we made up for this example called DrawDot, you would simply declare a Point and call the function, as in the following.

Point onedot;

onedot.x = 10;

onedot.y = 15;

DrawDot(onedot);

The DrawDot function would have a prototype that looks like this:

void DrawDot(Point pt);

Note that the function doesn’t take a pointer to a Point, nor does it take a reference to a Point. It just gets right to the Point directly.

imageIf you want, you can initialize the members of a structure the same way you would an array:

Point seconddot = { 30, 50 };

DrawDot(seconddot);

Equating structures

Setting simple structures that are equal to another structure is easy. The C++ compiler automatically handles this by copying the members one by one. Listing 2-2 is an example of this process in action.

Listing 2-2: Copying Structures Easily

#include <iostream>

using namespace std;

struct Point3D

{

double x;

double y;

double z;

};

int main()

{

Point3D FirstPoint = { 10.5, 22.25, 30.8 };

Point3D SecondPoint = FirstPoint;

cout << SecondPoint.x << endl;

cout << SecondPoint.y << endl;

cout << SecondPoint.z << endl;

return 0;

}

imageBecause structures are almost identical to classes, you can take Listing 2-2 and change the structure definition to the following class definition, and the program will continue to function the same:

class Point3D

{

public:

double x;

double y;

double z;

};

No matter which form of the application you use, the output is simple. When you run this application, you see output similar to this:

10.5

22.25

30.8

Process returned 0 (0x0) execution time : 0.015 s

Press any key to continue.

Returning compound data types

Because simple structures are just a grouping of smaller data items, you can treat them as one chunk of data. For that reason, you can easily return them from functions without having to use pointers.

The following function is an example of this:

Point3D StartingPoint(float x)

{

Point3D start;

start.x = x;

start.y = x * 2;

start.z = x * 3;

return start;

}

This function relies on the Point3D struct defined in the “Equating structures” section of the chapter. You can easily call this guy, then, by using code like this:

Point3D MyPoint = StartingPoint(5.2);

Point3D OtherPoint = StartingPoint(6.5);

cout << MyPoint.x << endl;

cout << MyPoint.y << endl;

cout << MyPoint.z << endl;

cout << endl;

cout << OtherPoint.x << endl;

cout << OtherPoint.y << endl;

cout << OtherPoint.z << endl;

These cout statements produce the following output:

5.2

10.4

15.6

6.5

13

19.5

Process returned 0 (0x0) execution time : 0.125 s

Press any key to continue.

Note that in the function, we simply created a local variable of type Point3D. This variable is not a pointer, nor is it a reference. And at the end of the function, we just returned it. When we called it, we copied the value of the returned structure into our own variables, first MyPoint and then OtherPoint.

image You may start to see some trouble in paradise when returning structures (or class instances, because they’re the same thing). Does returning a structure work? Why yes, it does, but what happens is sophisticated. When you create an instance of the structure in the function, you’re just creating a local variable. That’s definitely not something you want to return; it would sit on the stack as a local variable. But consider this call:

Point3D MyPoint = StartingPoint(5.2);

At the assembly level, the StartingPoint function receives the address of MyPoint. Then at the end of the function, again at the assembly level, the compiled code copies the contents of the local variable (called start in this case) into the MyPoint structure by using the pointer to MyPoint. So nothing is actually returned, per se; instead, the data is copied. That means, then, that if your structure includes a pointer variable, for example, you will get a copy of the pointer variable as well — that is, your pointer variable will point to the same thing as the one in the function. That may be what you want, or it may not be, depending on your situation. So be careful and make sure you fully understand what you’re doing when you return a structure from a function!

Naming Your Space

It’s often nice to be able to use a common name for a variable or other item without fear of the name clashing with a preexisting identifier. For example, somewhere in a header file you may have a global variable called Count, and somebody else may want to make a variable calledCount in one of his or her functions that uses your global header file. Or you may want to name a function GetData — but how can you be sure the people who use your function won’t include a header file somebody else wrote that already has a GetData function? Is it the great Battle of the GetDatas, where only one will survive? Not good! What can you do to avoid the clash?

You can use namespaces. A namespace is simply a way to group identifiers, such as all your classes. If you called this group Menagerie, for example, Menagerie would be your namespace. You would then put your classes inside it, like this:

namespace Menagerie

{

class Oxen {

public:

int Weight;

int NumberOfTeeth;

};

class Cattle {

public:

int Weight;

int NumberOfChildren;

};

}

The names Oxen and Cattle are unique within the Menagerie namespace. You are free to reuse these names in other namespaces without worrying about a clash. Then, if you want to use either of the two classes inside the Menagerie namespace, you would fully qualify the names of the classes, like so:

Menagerie::Cattle bessie;

bessie.Weight = 643;

image Unlike class and structure declarations, a namespace declaration doesn’t have to end with a semicolon.

If you plan to use the names in the Menagerie namespace without reusing them, just put a line after the namespace declaration (but somewhere preceding the use of the names Cattle and Oxen in your code), like this:

using namespace Menagerie;

Then you can access the names as if they’re not in a namespace:

Cattle bessie;

bessie.Weight = 643;

image When you include a line that has using namespace, the compiler knows the namespace is only for lines that follow the using namespace declaration. Consider the following code:

void cattleranch()

{

Cattle x;

}

using namespace Menagerie;

void dairy()

{

Cattle x;

}

Here the first function won’t compile because the compiler won’t know the name Cattle. To get it to work, you have to replace Cattle with Menagerie::Cattle. But the second function will compile, thanks to some help from our buddy using namespace.

The using namespace line is good only for lines that follow it. If you put using namespace inside a code block — inside curly braces { and }, as you would inside a function — the line applies only to lines that follow it within the same code block. Thus, in this case

void cattleranch()

{

using namespace Menagerie;

Cattle x;

}

void dairy() {

Cattle x;

}

the compiler will be happy with the first function, cattleranch but not with the second function, dairy. The using namespace line is good only for the length of the cattleranch function; it’s inside that function’s code block.

imageWhen you have a using namespace line, any variables or identifiers you create after that line don’t become part of the namespace you’re using. The using namespace line simply tells the compiler that if it finds an identifier it doesn’t recognize, it should check next inside the namespaces you’re using.

image When you have a using namespace line, you can follow it with more using namespace lines for other namespaces — and doing so won’t cause the compiler to forget the previous using namespace line. Thus, if you have

using namespace Menagerie;

using namespace Ocean;

you can successfully refer to identifiers in both the Menagerie and the Ocean namespaces.

Creating one namespace in many places

After you create a namespace, you can add to it later in your code if necessary. All you have to do is start the first block of code with (for example) namespace Menagerie { and then finish it with a closing brace. Then later in your code, do the same line again — starting the block again with namespace Menagerie { and ending it with a closing brace. The identifiers in both blocks become part of the namespace Menagerie.

Using variables and part of a namespace

You can put variables in a namespace and then later refer to them through the namespace, as in the following:

namespace Menagerie

{

int CattleCount;

}

And then again later (for example, in your main) like this:

Menagerie::CattleCount = 10;

But remember: A namespace is not a class! Only one instance of the CattleCount variable exists; it just happens to have a full name of Menagerie::CattleCount. This doesn’t mean you can get away with creating multiple instances of Menagerie: You can’t. It’s a namespace. (Think of it like a surname: There could be multiple people named John, and to distinguish between them in a meeting at work, you might tack on their last names: John Squibbledash and John Poltzerbuckin.) Although the namespace name comes first in Menagerie::CattleCount, it’s analogous to the last name. Two variables can be called CattleCount: one in the Menagerie namespace and one in the Farm namespace. Thus their full names would be Menagerie::CattleCount and Farm::CattleCount.

If you want to use only a portion of a namespace, you are free to do that, too. With the Menagerie namespace that we declared earlier in this section, you could do something like this outside the namespace:

using Menagerie::Oxen;

Oxen ollie;

(Notice that no namespace word appears after using.) The first line tells the compiler about the name Oxen, and the second line creates an instance of Oxen. Of course, if you have using namespace Menagerie, the using Menagerie::Oxen isn’t very useful because theOxen name is already available from the using namespace Menagerie line.

image Think of a using declaration as pulling a name into the current namespace. Therefore a declaration such as using Menagerie::Oxen pulls the name Oxen into the current namespace. The single name then lives in both namespaces.

To understand how one name becomes a part of two namespaces, take a look at Listing 2-3.

The standard namespace

Sooner or later, you’re going to encounter something like this:

std::cout << “Hi” << std::endl;

You see this because normally cout, cin, endl, and everything else that comes from #include<iostream> is in a namespace called std (which is short for standard). But we find that line of code ugly. We don’t want to write a namespace name and two colons every time we want to write a cout or endl (or anything else from iostream, for that matter). So what do you do to avoid it? You simply put

using namespace std;

at the beginning of your program, after the include lines. Fortunately, the gcc compiler automatically recognizes the std namespace, and you don’t need the using namespace std; line. But if you’re using other compilers (notably Borland C++Builder or Microsoft Visual C++), you need to add std:: before each cout and cin and endl words or take the easier way out and use the using namespace std; line. We prefer the using namespace std; line. So if you look at the code on the accompanying CD-ROM, you see that line at the beginning of every program.

Listing 2-3: Pulling Names into Other Namespaces with the using Declaration

#include <iostream>

using namespace std;

namespace A

{

int X;

}

namespace B

{

using A::X;

}

int main()

{

A::X = 2;

cout << B::X << endl;

return 0;

}

This code has two namespaces, A and B. The first namespace, A, has a variable called X. The second namespace, B, has a using statement that pulls the name X into that namespace. The single variable that lives inside A is now part of both namespaces, A and B. main verifies this: It saves a value in the X variable of A and prints the value in the X variable of B. And lo and behold, the result on the screen is this:

2

Yes indeed, A::X and B::X refer to the same variable, thanks to the using declaration!