Chapter 2

Creating Data Structures

IN THIS CHAPTER

check Discovering all the different data types

check Casting and converting

check Using structures with your data

check 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.

This chapter refers to the ANSI standard of C++. ANSI is the American National Standards Institute. The information provided in this chapter deals with the ANSI standard (singular) of C++. Fortunately, the GNU gcc compiler that comes with Code::Blocks is ANSI-standard-compliant.

Remember You don’t have to type the source code for this chapter manually. In fact, using the downloadable source is a lot easier. You can find the source for this chapter in the \CPP_AIO4\BookV\Chapter02 folder of the downloadable source. See the Introduction for details on how to find these source files.

Working with Data

In the sections that follow, you see how to manipulate data, consider the data types available to you, and discover how you can change one data type to another.

The great variable roundup

The ANSI C++ standard dictates the fundamental C++ types shown in Table 2-1.

Remember C++ includes a signed keyword, but you have little reason to use it because signed is assumed if you don’t specifically use unsigned. Note that when you use unsigned, the size of the variable doesn’t change: It takes the same number of bytes. Instead, the range shifts. For example, a short ranges from –32,768 to 32,767, so there are 65,536 possibilities. An unsigned short ranges from 0 to 65,535; again, there are 65,536 possibilities.

The precise values of some of these types, such as long double, can vary by compiler. The best way to ensure that you understand the limits of your compiler is to run a simple test. The VarTypes example, shown in Listing 2-1, demonstrates the maximum values for each data type found in Table 2-1.

TABLE 2-1: ANSI C++ Character Types

Name

Size in Bytes

Range

char

1

–128 to 127

unsigned char

1

0 to 255

short

2

–32,768 to 32,767

unsigned short

2

0 to 65,535

int and long

4

-2,147,483,648 to 2,147,483,647

unsigned int and unsigned long

4

0 to 4,294,967,295

long long

8

-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

unsigned long long

8

0 to 18,446,744,073,709,551,615

bool

1

true/false

float

4

1.17549e-038 to 3.40282e+038

double

8

2.22507e-308 to 1.79769e+308

long double

12

3.3621e-4932 to 1.18973e+4932

LISTING 2-1: Testing Maximum Type Values

#include <iostream>
#include <climits>
#include <cfloat>

using namespace std;

int main() {
char Char = CHAR_MAX;
unsigned char UChar = UCHAR_MAX;
short Short = SHRT_MAX;
unsigned short UShort = USHRT_MAX;
int Int = INT_MAX;
unsigned int UInt = UINT_MAX;
long Long = LONG_MAX;
unsigned ULong = ULONG_MAX;
long long LongLong = LLONG_MAX;
unsigned long long ULongLong = ULLONG_MAX;
bool Bool = true;
float Float = FLT_MIN;
double Double = DBL_MIN;
long double LDouble = LDBL_MIN;

cout << "Char\t\t\t" << Char << "\t\t\t" <<
sizeof(Char) << endl;
cout << "Unsigned Char\t\t" << UChar << "\t\t\t" <<
sizeof(UChar) << endl;
cout << "Short\t\t\t" << Short << "\t\t\t" <<
sizeof(Short) << endl;
cout << "Unsigned Short\t\t" << UShort << "\t\t\t" <<
sizeof(UShort) << endl;
cout << "Int\t\t\t" << Int << "\t\t" <<
sizeof(Int) << endl;
cout << "Unsigned Int\t\t" << UInt << "\t\t" <<
sizeof(UInt) << endl;
cout << "Long\t\t\t" << Long << "\t\t" <<
sizeof(Long) << endl;
cout << "Unsigned Long\t\t" << ULong << "\t\t" <<
sizeof(ULong) << endl;
cout << "Long Long\t\t" << LongLong << "\t" <<
sizeof(LongLong) << endl;
cout << "Unsigned Long Long\t" << ULongLong << "\t" <<
sizeof(ULongLong) << endl;
cout << "Bool\t\t\t" << (Bool ? "True" : "False") <<
"\t\t\t" << sizeof(Bool) << endl;
cout << "Float\t\t\t" << Float << "\t\t" <<
sizeof(Float) << endl;
cout << "Double\t\t\t" << Double << "\t\t" <<
sizeof(Double) << endl;
cout << "Long Double\t\t" << LDouble << "\t\t" <<
sizeof(LDouble) << endl;
return 0;
}

Remember Notice the use of constants, such as CHAR_MAX, to set maximum values. When working with integers, you use the statement #include <climits>, which reads as include the file climits. You may see other headers used, but they may not include the ULLONG_MAX constant. When working with floating-point numbers, you #include <cfloat>. This example provides the following output:

Char 127 1
Unsigned Char 255 1
Short 32767 2
Unsigned Short 65535 2
Int 2147483647 4
Unsigned Int 4294967295 4
Long 2147483647 4
Unsigned Long 4294967295 4
Long Long 9223372036854775807 8
Unsigned Long Long 18446744073709551615 8
Bool True 1
Float 1.17549e-038 4
Double 2.22507e-308 8

Tip The char output will reflect a character, rather than a number, in all cases. Consequently, you may see a different character output on your display. Notice also the use of (Bool ? "True" : "False") to display a textual value, rather than a numeric value, for Bool.

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 an rvalue is an expression that can be on the right side of an equals sign.

Remember The terms lvalue and rvalue don’t 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 or right side of an assignment statement. You can have only lvalues on the left side of an assignment statement and rvalues on the right side of an assignment statement. Here are some examples, in which 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;

The number 2 can’t appear on the left (setting it equal to something else makes no sense), therefore it isn’t an lvalue. 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, you see an error message similar to this one:

non-lvalue in assignment

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

ChangeMe() = 10;

In most cases, putting a function call on the left doesn’t make sense, so you don’t do it. In other words, you must consider whether the expression ChangeMe() is considered an lvalue. Look at this code from the LValueAndRValue example:

#include <iostream>

using namespace std;

int uggle;

int &ChangeMe() {
return uggle;
}

int main() {
ChangeMe() = 10;
cout << ChangeMe() << endl;
return 0;
}

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. You can still use ChangeMe() as a function, as shown in the next line with the cout, so it can still stand alone.

Remember The words lvalue and rvalue aren’t C++ keywords. You don’t type these into an application.

Casting a spell on your data

Although C++ has all these great data types, such as int and char, the fact is that the CPU just stores them as numbers. And sometimes you may have a character and need to use its underlying number. To do this, you can cast the data into a different type.

The way you cast is to take a variable of one type and type the variable’s name, preceded by the other type you would like it to be. You put that other type in parentheses, as shown in the SimpleCast example that follows.

#include <iostream>

using namespace std;

int main() {
char buddy = 'A';
int underneath = (int)buddy;
cout << underneath << endl;
return 0;
}

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.

Comparing casting and converting

The idea behind casting is to take some data and, without changing it, use it in another way. For example, you could have an array containing the characters Apple. But inside the memory, each letter is stored as a number. For example, the A is stored as 65, p is stored as 112, l as 108, and e as 101. Therefore, you use code like that found in the CastOrConvert example that follows when you want to cast each character to an integer:

char str[] = {'A','p','p','l','e','\0'};
cout << str << endl;

for (int x: str)
cout << x << endl;

where str is the string Apple (notice the null value required to end the string). The for each loop casts each character in str, one at a time, to an int, and then prints it out onscreen. This act would print out the numerical equivalents of each letter, as shown here:

Apple
65
112
112
108
101
0

In other words, the code casts the characters to integers — but doesn’t actually change any data.

Converting, however, is different. If you 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 characters. The numbers for the string 123 are 49, 50, and 51, respectively. Casting the number 123 into a char won’t produce the string, "123". Instead, you would need to convert the number to a string using code like this, as shown in CastOrConvert.

int value = 123;
char strValue[4];
strValue[3] = '\0';
for (int counter = 2; counter >= 0; counter--) {
strValue[counter] = (char)(value % 10 + 48);
value = value / 10;
}
cout << strValue << endl;

In this case, the code works backward to create the string from the number by using a combination of integer division and modulus. The content in value is destroyed in the process, but strValue contains the correct string in the end. To get an idea of how this works, 123 % 10 = 3, while 123 / 10 = 12. The value 3 + 48 = 51 comes out to the char value ’3’ when cast. Of course, there is a much easier way to perform this task (you must #include <string>):

string EasyValue = to_string(123);
cout << EasyValue << endl;

As is true of most techniques, there are times when casting won’t work as expected. One of those times come 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:

float f = 6.3;
int i = f;

Warning Casting and converting can both cause problems. For example, when casting between a float and an int, you have the potential for data loss. A float value of 0.123 will appear as an int value of 0. Whenever possible, use built-in conversions (those where you can simply make one data type equal to another data type, such as making an int variable equal to a char variable) to ensure that the output you receive truly represents the correct transition between one type and another. Later in this chapter, you also see how to use safe casting techniques with both dynamic_cast and static_cast. Unlike most languages, C++ won’t protect you from yourself. For example, you can cast a pointer to some other type, even though such a cast doesn’t make sense. You could even convert the address into a string if you want. C++ assumes that you want the low-level access that it can provide, so it also gives you the extra flexibility to perform tasks incorrectly.

Casting safely with C++

The ANSI standard of C++ comes with all kinds of 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 items of data. These include dynamic_cast, static_cast, and const_cast. (There is also a reinterpret_cast, but it’s incredibly unsafe to use and therefore not demonstrated.)

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 whether 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, 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);

This code assumes that someptr is of type pointer-to-base-class that, in fact, points to a derivedclass instance. It may point to derivedclass, but that depends on how you wrote the application. But, relying on assumptions rather than actual knowledge is a great way to create a buggy application.

However, with the new ANSI ways of casting, you can be sure that someptr points to a derivedclass instance. The DynamicCast example, shown in Listing 2-2, is a complete application that demonstrates a proper down-cast that uses a pointer to a base class and casts it down to a pointer of a derived class.

LISTING 2-2: Casting Instances Dynamically for Safety

#include <iostream>

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;
}

When you run this code, you see output that looks like this:

=========
George I
School of the Kings
=========
Henry II

Some strange things are going on in this code. Starting with main(), the code calls KingInfo(), 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 using the MyName() function and prints the resulting name. Then comes the important part: the dynamic cast. To do the dynamic cast, the code calls dynamic_cast and saves inst (which can be of type King or Prince) in a pointer variable called asPrince. Notice the syntax of dynamic_cast. It looks like a template in that you include a type in angle brackets. Then you put the variable you want to cast in parentheses (in this case inst).

If the dynamic cast works, it returns a pointer that you can save as the type inside angle brackets. Otherwise, the dynamic cast returns 0. After calling dynamic_cast, the code tests the result against 0. If the result is not 0, the dynamic cast worked, which means that inst is of type Prince. Then, in the if block, the code retrieves and prints the School member, which is part of Prince, not King.

Notice the unique design of the King class in Listing 2-2. 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 Code::Blocks 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. Notice also that this class uses good design by keeping CrownName private and providing an accessor function, MyName(), to it.

Tip You don’t need to use references in a class as shown here to make dynamic_cast work. But you do need at least one virtual function.

The fundamental difference between an old-style direct cast and a dynamic_cast is that the compiler generates code that automatically does an old-style cast, regardless of whether the cast is valid, during compile time. That is, the cast is hardcoded. But dynamic_cast 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 shown previously in Listing 2-2 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 version work, you have 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. 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.

This code doesn’t do anything inside the catch block because the application will continue to work even if the call fails — the output simply lacks the school name. C++ requires that all try blocks are matched with a catch block, so you must include the catch block even when it doesn’t do anything.

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 angle 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. 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’re 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 the code, you get an error:

static_cast from 'AnotherType *' to 'FinalType *'

The following code, found in the StaticCast example, shows how to make the casting work:

#include <iostream>

using namespace std;

class FinalType {};
class AnotherType : public FinalType {};

int main() {
AnotherType *orig = new AnotherType;
FinalType *f = static_cast<FinalType *>(orig);
}

Tip The difference between static_cast and dynamic_cast is that static_cast does all its type checking at compile time; the compiler makes sure that the cast is okay. A dynamic_cast performs both runtime and compile time checks, so it’s more comprehensive. 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);

Changing the constness of variables with const_cast

Sometimes you need to add or remove const from a variable in order to perform a cast. The variable itself doesn’t change, but the cast output does. For example, if you want to send a const value to a function that doesn’t accept a const value, you need to perform a const_cast. Likewise, you may have a volatile variable, one that is changed by code outside the current application. (This is a common process in embedded applications; see the article at https://www.tutorialspoint.com/What-does-the-volatile-keyword-mean-in-Cplusplus for more information.) You may need to cast the volatile variable as a common variable. The ConstCast example that follows shows both techniques:

#include <iostream>

using namespace std;

void PrintIt(int *out) {
cout << "The value is: " << *out << endl;
}

int main() {
volatile int X = 20;
const int Y = 30;

PrintIt(const_cast<int*>(&X));
PrintIt(const_cast<int*>(&Y));
return 0;
}

In the first case, if you were to try PrintIt(&X), you’d see error: invalid conversion from ’volatile int*’ to ’int*’ during compilation. Likewise, in the second case, PrintIt(&Y) would produce an error: invalid conversion from ’const int*’ to ’int*’ error message. Of course, neither X nor Y has its attributes removed; you simply strip the volatile or const attribute off for the purpose of sending the value to PrintIt().

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 properties — no methods. 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 properties but no methods. 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 you can use structures more powerfully in C++ than you could in C. The main change to structures in C++ is that they can have methods and access control. Thus, you can add to the Dimensions structure like so (making struct and class equivalent):

struct Dimensions {
private:
int price;
public:
int height;
int width;
int depth;
int weight;
int GetPrice() { return price; }
};

Then create an instance of Dimensions in your code like this:

Dimensions FirstIem;
Dimensions *SecondItem = new Dimensions;

Remember 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. Because the differences are so small, most C++ programmers today never even touch a structure, except to create an object that has only public properties.

In other words, programmers use struct for simple data types that are a collection of smaller data types. (That is, they use structs the same way C originally used them.) The sections that follow tell you about some of these data-structure issues.

Technical stuff 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 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 like this:

struct Point {
int x;
int y;
};

Then, when you need to call a function that requires such a structure — such as the function created 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.

Tip If 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. The EquateStruct example, shown in Listing 2-3, is an example of this process in action.

LISTING 2-3: 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;
}

Tip Because structures are almost identical to classes, you can take Listing 2-2 and change the structure definition to the following class definition, and the application 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

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 (found in the CompoundData example) shows how to return a structure:

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 preceding section, “Equating structures.” The following code shows how to use this function:

int main() {
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

Note that StartingPoint() creates a local variable, start, of type Point3D. This variable isn’t a pointer or reference. The return is an unmodified start. Calling StartingPoint() copies the value of the returned structure into variables in main(), first MyPoint and then OtherPoint.

Technical stuff You may start to see some trouble in paradise when returning structures (or class instances, because they’re the same thing). Returning a structure works, 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, StartingPoint() receives the address of MyPoint. Then at the end of the function, again at the assembly level, the compiled code copies the contents of start into the MyPoint structure by using the pointer to MyPoint. So StartingPoint() doesn’t actually return anything; instead, the data is copied. Thus, if your structure includes a pointer variable (for example), you get a copy of the pointer variable as well — that is, your pointer variable will point to the same thing as the pointer in the function. That may or may not be what you want, 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 that the name will clash 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 called Count in an application that uses your header file. Or you may want to name a function GetData() — but you need to ensure that it doesn’t conflict with another header that already has a GetData() function. These are examples of potential naming clashes (or sometimes called a name collision). The following sections describe how to create and use namespaces to your benefit.

Creating a namespace

You can use namespaces to group identifiers, such as all your classes, under a single name. If you called this group Menagerie, for example, Menagerie is your namespace. You would then put your classes inside it, as shown in the SimpleNamespace example:

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 outside the Menagerie namespace, you fully qualify the names of the classes, like so (notice the use of the double colons between Menagerie and Cattle):

Menagerie::Cattle bessie;
bessie.Weight = 643;

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

Employing using namespace

If you plan to use the names in the Menagerie namespace without having to retype the namespace name each time, just put a line after the namespace declaration in the other namespace (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;

Remember When you include a line that has using namespace, the compiler knows that 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 because you included using namespace Menagerie;.

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.

Tip When 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.

Remember 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.

Warning However, now if there are multiple occurrences of the same name, you receive an error message saying that the reference to the name is ambiguous. The compiler then presents a list of namespaces that contain the name so that you can decide which one to use. You resolve the name clash by fully qualifying the name.

Using variables

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 do it 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. You can’t get away with creating multiple instances of Menagerie because 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. Their full names are Menagerie::CattleCount and Farm::CattleCount.

Using part of a namespace

You can use only a portion of a namespace if desired. Using the Menagerie namespace 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 the Oxen name is already available from the using namespace Menagerie line.

Remember 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, consider the Namespace example, shown in Listing 2-4.

LISTING 2-4 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 with an output of:

2

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