Chapter 4

Advanced Class Usage

IN THIS CHAPTER

check Using polymorphism effectively

check Adjusting member access when deriving new classes

check Multiple-inheriting new classes

check Making virtual inheritance work correctly

check Putting one class or type inside another

Classes are amazingly powerful. You can do so much with them. In this chapter, you discover many of the extra features you can use in your classes. But these aren’t just little extras that you may want to use on occasion. If you follow the instructions in this chapter, you should find that your understanding of classes in C++ greatly improves, and you’ll want to use many of these topics throughout your programming.

This chapter also discusses many of the issues that come up when you’re deriving new classes and inheriting members. This discussion includes virtual inheritance and multiple inheritance, topics that people mess up a lot. As part of this discussion, you see the ways you can put classes and types inside other classes.

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\Chapter04 folder of the downloadable source. See the Introduction for details on how to find these source files.

Inherently Inheriting Correctly

Without inheritance, doing object-oriented programming (OOP) would be nearly impossible. Yes, you could divide your work into objects, but the real power comes from inheritance. However, you have to be careful when using inheritance or you can really cause yourself problems. In the sections that follow, you see different ways to use inheritance — and how to keep it all straight.

Morphing your inheritance

Polymorphism refers to using an object as an instance of a base class. For example, if you have the class Creature and from that you derive the class Platypus, you can treat the instances of class Platypus as if they’re instances of class Creature. This concept is useful if you have a function that takes as a parameter a pointer to Creature. You can pass a pointer to Platypus.

However, you can’t go further than that. You can’t take a pointer to a pointer to Creature. (Remember that when creating a pointer to a pointer, the first pointer is the address of the second pointer variable.) So if you have a function such as this:

void Feed1(Creature *c) {
cout << "Feed me!" << endl;
}

you’re free to pass the address of a Platypus object, as in the following:

Platypus *plato = new Platypus;
Feed1(plato);

However, with a function that takes the address of a pointer variable (note the two asterisks in the parameter), like this:

void Feed2(Creature **c) {
cout << "Feed me!" << endl;
}

you can’t pass the address of a pointer to a Platypus instance, as in this example:

Platypus *plato = new Platypus;
Feed2(&plato);

If you try to compile this code, you get a compiler error.

Avoiding polymorphism

You don’t always use polymorphism when you declare a variable, as shown in the previous section. If you do, you’re declaring variables like this:

Creature *plato = new Platypus;

The type of plato is a pointer to Creature. But the object is a Platypus. You can create a Platypus from a Creature because a pointer to a base class can point to an object of a derived class. But now the compiler thinks that plato is a pointer to a Creature instance, so you can’t use plato to call a Platypus method — you can use plato only to call Creature methods. For example, if your two classes look like this:

class Creature {
public:
void EatFood() {
cout << "I'm eating!" << endl;
}
};

class Platypus : public Creature {
public:
void SingLikeABird() {
cout << "I'm siiiiiinging in the rain!" << endl;
}
};

the following code doesn’t work:

Creature *plato = new Platypus;
plato->SingLikeABird();

Although the first line compiles, the second doesn’t. When the compiler gets to the second line, it thinks that plato is an object of class type Creature, and Creature doesn’t have a method called SingLikeABird(), so the compiler gets upset. You can fix the situation by casting, like this:

Creature *plato = new Platypus;
static_cast <Platypus *>(plato)->SingLikeABird();

If you want to save some work, start by declaring plato as type Platypus, as shown here:

Platypus *plato = new Platypus;
plato->SingLikeABird();

You may need to perform a cast at times. For example, you may have a variable that can hold an instance of an object or its derived object. Then you have to use polymorphism, as in the following code:

Creature *plato;
if (HasABeak == true) {
plato = new Platypus;
} else {
plato = new Creature;
}

This code defines a pointer to Creature. That pointer stores the address of either a Platypus instance or a Creature instance, depending on what’s in the HasABeak variable.

But if you use an if statement like that, you shouldn’t follow it with a call to SingLikeABird(), even if you cast it:

static_cast <Platypus *>(plato)->SingLikeABird();

The reason is that if the else clause took place and plato holds an instance of Creature, not Platypus, the plato object won’t have a SingLikeABird() method. Either you get some type of error message when you run the application or you don’t, but the application will mess up later. And those messing-up-later errors are the worst kind to try to fix.

Adjusting access

You may have a class that has protected members; and in a derived class, you may want to make these members public. You transition the members to public by adjusting the access. You have two ways to do this: One is the older way, and the other is the newer American National Standards Institute (ANSI) way, which is the method supported by the current version of the GNU Compiler Collection (GCC). If your compiler supports the newer way, the creators of the ANSI standard ask that you use the ANSI way.

In the following classes, Secret has a member, X, that is protected. The derived class, Revealed, makes the member X public. Here’s the older way:

class Secret {
protected:
int X;
};

class Revealed : public Secret {
public:
Secret::X;
};

The code declares the member X public by providing the base class name, two colons, and then the member name. It didn’t include any type information; that was implied. So in the class Secret, the member X is protected. But in Revealed, it’s public.

Here’s the ANSI way, which requires the word using. Otherwise, it’s the same:

class Secret {
protected:
int X;
};

class Revealed : public Secret {
public:
using Secret::X;
};

Now, when you use the Revealed class, the inherited member X is public, but X is still protected in the base class, Secret.

Avoiding variable naming conflicts

If you want to make a protected member public in a derived class, don’t just redeclare the member. If you do, you end up with two properties of the same name within the class; and needless to say, that can be confusing! Look at the following two classes:

class Secret {
protected:
int X;
public:
void SetX() {
X = 10;
}
void GetX() {
cout << "Secret X is " << X << endl;
}
};

class Revealed : public Secret {
public:
int X;
};

The Revealed class has two int X members! Suppose you try this code with it:

Revealed me;
me.SetX();
me.X = 30;
me.GetX();

The first line declares the variable. The second line calls SetX(), which stores 10 in the inherited X, because SetX() is part of the base class. The third line stores 30 in the new X declared in the derived class. GetX() is part of the base class, so it prints 10.

Having two properties of the same name is confusing. It would be best if the compiler didn’t allow you to have two variables of the same name. But just because the compiler allows it doesn’t mean you should do it. Having two variables of the same name is a perfect way to increase the chances of bugs creeping into your application.

Using class-based access adjustment

Suppose you have a class that has several public members, and when you derive a new class, you want all the public members to become protected, except for one. You can do this task in a couple of ways. You can adjust the access of all the members except for the one you want left public. Or, if you have lots of members, you can take the opposite approach. Look at this code:

class Secret {
public:
int Code, Number, SkeletonKey, System, Magic;
};

class AddedSecurity : protected Secret {
public:
using Secret::Magic;
};

The derived class inherits the base class as protected, as you can see in the header line for AddedSecurity. That means that all the inherited public members of Secret are protected in the derived class. But then the code promotes Magic back to public by adjusting its member access. Thus, Magic is the only public member of AddedSecurity. All the rest are protected.

Remember If you have a member that is private and you try to adjust its access to protected or public in a derived class, you quickly discover that the compiler won’t let you do it. The reason is that the derived class doesn’t even know about the member because the member is private. And because the derived class doesn’t know about the member, you can’t adjust its access.

Returning something different, virtually speaking

Two words that sound similar and have similar meanings in computer programming are overload and override. To overload means to take a function and write another function of the same name that takes a different set of parameters. To override means to take an existing function in a base class and give the function new code in a derived class. The function in the derived class has the same prototype as the base class: It takes the same parameters and returns the same type.

An overloaded function can optionally return a different type, but the parameters must be different from the original function, whether in number or type or both. The overloaded function can live in the same class or in a derived class. The idea here is to create what appears to be a single function that can take several types of parameters. For example, you may have a function called Append() that works on strings. By using Append(), you’d be able to append a string to the end of the string represented by the instance, or you could append a single character to the end of the string represented by the instance. Now, although it feels like one function called Append(), really you would implement it as two separate functions: one that takes a string parameter and one that takes a character parameter.

This section discusses one particular issue dealing with overriding functions (that is, replacing a function in a derived class). Generally, the overriding function must have the same parameter types and must return the same type as the original function. A situation exists under which you can violate this rule, although only slightly. You can violate the rule of an overriding function returning the same type as the original function if all three of the following are true:

  • The overriding function returns an instance of a class derived from the type returned by the original function.
  • You return either a pointer or a reference, not an object.
  • If you return a pointer, the pointer doesn’t refer to yet another pointer.

Tip Typically, you want to use this approach when you have a container class that holds multiple instances of another class. For example, you may have a class called Peripheral. You may also have a container class called PeripheralList, which holds instances of Peripheral. You may later derive a new class from Peripheral, called Printer, and a new class from PeripheralList, called PrinterList. If PeripheralList has a function that returns an instance of Peripheral, you would override that function in PrinterList. But instead of having it return an instance of Peripheral, you would have it return an instance of Printer. The OverridingDerived example, shown in Listing 4-1, shows how to perform this task.

LISTING 4-1: Overriding and Returning a Derived Class

#include <iostream>
#include <map>

using namespace std;

class Peripheral {
public:
string Name;
int Price;
int SerialNumber;
Peripheral(string aname, int aprice, int aserial) :
Name(aname), Price(aprice),
SerialNumber(aserial) {}
};

class Printer : public Peripheral {
public:
enum PrinterType {laser, inkjet};
PrinterType Type;
Printer(string aname, PrinterType atype, int aprice,
int aserial) :
Peripheral(aname, aprice, aserial), Type(atype) {}
};

typedef map<string, Peripheral *> PeripheralMap;

class PeripheralList {
public:
PeripheralMap list;
virtual Peripheral *GetPeripheralByName(string name);
void AddPeripheral(string name, Peripheral *per);
};

class PrinterList : public PeripheralList {
public:
Printer *GetPeripheralByName(string name);
};

Peripheral *PeripheralList::GetPeripheralByName
(string name){
return list[name];
}

void PeripheralList::AddPeripheral(
string name, Peripheral *per) {
list[name] = per;
}

Printer *PrinterList::GetPeripheralByName(string name) {
return static_cast<Printer *>(
PeripheralList::GetPeripheralByName(name));
}

int main(int argc, char *argv[]) {
PrinterList list;
list.AddPeripheral(string("Koala"),
new Printer("Koala", Printer::laser,
150, 105483932)
);
list.AddPeripheral(string("Bear"),
new Printer("Bear", Printer::inkjet,
80, 5427892)
);

Printer *myprinter = list.GetPeripheralByName("Bear");
if (myprinter != 0) {
cout << myprinter->Price << endl;
}
return 0;
}

This example uses a special type called map, which is simply a container or list that holds items in pairs. The first item in the pair is called a key, and the second item is called a value. You can retrieve values from the map based on the key. This example stores a Peripheral (the value) based on a name, which is a string (the key). The example uses a typedef to create the map by specifying the two types involved: first the key and then the value. The typedef, then, looks like this:

typedef map<string, Peripheral *> PeripheralMap;

This line creates a type of a map that stores a set of Peripheral instances and you can look them up based on a name. The code uses a notation similar to that of an array to put an item in the map, where list is the map, name is a string, and per is a pointer to Peripheral. The key goes inside square brackets, like this:

list[name] = per;

To retrieve the item, you refer to the map entry using brackets again, as in this line from the listing:

return list[name];

Listing 4-1 shows a Printer class derived from a Peripheral class. It also has a container class called PrinterList derived from PeripheralList. The idea is that the PrinterList holds only instances of the class called Printer. So the code overrides the GetPeripheralByName() function. The version inside PrinterList casts the item to a Printer because the items in the list are instances of Peripheral. If you were to leave this function as is, every time you want to retrieve a Printer, you’d get back a pointer to a Peripheral instead, and you’d have to cast it to a (Printer *) type. Overriding the GetPeripheralByName() function and performing the cast there is easier and more efficient.

Technical stuff The code in Listing 4-1 has a small bug: Nothing is stopping you from putting an instance of Peripheral in the PrinterList container. Or, for that matter, you could add an instance of any other class derived from Peripheral if there were more. But when you retrieve the instance in the GetPeripheralByName(), it’s automatically cast to a Printer. That would be a problem if somebody had stuffed something else in there other than a Printer instance. To prevent a wrongful addition, create a special AddPeripheral() function for the PrinterList class that takes, specifically, a Printer. To do that, you would make the AddPeripheral() function in PeripheralList virtual and then override it, modifying the parameter to take a Printer rather than a Peripheral. When you do so, you hide the function in the base class. But that’s okay: You don’t want people calling the base class version because it can accept any Peripheral, not just a Printer instance. When you run this application, you should get an output value of 80 (the price of the printer named Bear).

Multiple inheritance

In C++, having a single base class from which your class inherits is generally best. However, it is possible to inherit from multiple base classes, a process called multiple inheritance.

Employing multiple inheritance

One class may have some features that you want in a derived class, and another class may have other features that you want in the same derived class. If that’s the case, you can inherit from both through multiple inheritance.

Remember Multiple inheritance is messy and difficult to pull off properly. But when you use it with care, you can make it work. The DerivingTwoDiff example, shown in Listing 4-2, shows how to perform this task.

LISTING 4-2: Deriving from Two Different Classes

#include <iostream>

using namespace std;

class Mom {
public:
void Brains() {
cout << "I'm smart!" << endl;
}
};

class Dad {
public:
void Beauty() {
cout << "I'm beautiful!" << endl;
}
};

class Derived : public Mom, public Dad {
};

int main(int argc, char *argv[]) {
Derived child;
child.Brains();
child.Beauty();
return 0;
}

When you run this code, you see the following output:

I'm smart!
I'm beautiful!

In the preceding code, the class Derived inherited the functions of both classes Mom and Dad. Because it did, the compiler allows a Derived instance, child, to call both functions. You use this approach to derive from multiple classes:

class Derived : public Mom, public Dad

You start with the base classes to the right of the single colon, as with a single inheritance, and separate the classes with a comma. You also precede each class with the type of inheritance, public.

Setting access in multiple inheritance

As with single inheritance, you can use inheritance other than public. But you don’t have to use the same access for all the classes. For example, the following, although a bit confusing, is acceptable:

class Derived : public Mom, protected Dad

This means that public members derived from Dad are now protected in the Derived class, which also means that users can’t call the methods inherited from Dad, nor can they access any properties inherited from Dad. If you used this type of inheritance in Listing 4-2, this line would no longer work:

child.Beauty();

If you try to compile it, you see the following error, because the Beauty() member is protected now:

'void Dad::Beauty()' is inaccessible

Remember When you work with multiple inheritance, be careful that you understand what your code is doing. Although it may compile correctly, it still may not function correctly, leading to the famous creepy-crawly thing called a bug.

Seeing multiple inheritance go wrong

Strange, bizarre, freaky things can happen with multiple inheritance. If both base classes have a property called Bagel, the compiler gets confused. Suppose you enhance the two base classes with a Bagel effect (as seen in the DerivingTwoDiff2 example):

class Mom {
public:
int Bagel;
void Brains() {
cout << "I'm smart!" << endl;
}
};

class Dad {
public:
int Bagel;
void Beauty() {
cout << "I'm beautiful!" << endl;
}
};

class Derived : public Mom, public Dad {
};

In the preceding code, each of the two base classes, Mom and Dad, has a Bagel member. The compiler will let you do this. But if you try to access the member, as in the following code, you get an error:

Derived child;
child.Bagel = 42;

Here’s the error message we see in Code::Blocks:

error: request for member 'Bagel' is ambiguous

The message means that the compiler isn’t sure which Bagel the code refers to: The one inherited from Mom or the one inherited from Dad. If you write code like this, make sure you know which inherited member you’re referring to so you can fix the problem.

Remember Now this is going to look bizarre, but it’s correct. Suppose you’re referring to the Bagel inherited from Mom. You can put the name Mom before the word Bagel, separated by two colons:

child.Mom::Bagel = 42;

Yes, that really is correct, even though it seems a little strange. And if you want to refer to the one by Dad, you do this:

child.Dad::Bagel = 17;

Both lines compile properly because you removed any ambiguities. In addition, you can access them individually by using the same technique:

cout << child.Mom::Bagel << endl;
cout << child.Dad::Bagel << endl;

Virtual inheritance

At times, you may see the word virtual thrown in when deriving a new class, as in the following:

class Diamond : virtual public Rock

This inclusion of virtual is to fix a strange problem that can arise. When you use multiple inheritance, you can run into a crazy situation in which you have a diamond-shaped inheritance, as in Figure 4-1.

In Figure 4-1, you can see that the base class is Rock. The Diamond and Jade classes derive from Rock. At this point, the code uses multiple inheritance to derive the class MeltedMess from Diamond and Jade. Yes, you can do this. But you have to be careful.

Schematic illustration of using diamond inheritance can be hard.

FIGURE 4-1: Using diamond inheritance can be hard.

Understanding the diamond-shaped inheritance problem

Think about this: Suppose Rock has a public member called Weight. Then both Diamond and Jade inherit that member. Now when you derive MeltedMess and try to access its Weight member, the compiler claims that it doesn’t know which Weight you’re referring to — the one inherited from Diamond or the one inherited from Jade. You know that there should only be one instance of Weight, because it came from a single base class, Rock. But the compiler sees only one level up, not two.

To understand how to fix the problem, recognize what happens when you create an instance of a class derived from another class: Deep down inside the computer, the instance has a portion that is itself an instance of the base class. When you derive a class from multiple base classes, instances of the derived class have one portion for each base class. Thus an instance of MeltedMess has a portion that is a Diamond and a portion that is a Jade, as well as a portion that wasn’t directly inherited from Rock.

Digging deeper, MeltedMess has both a Diamond in it and a Jade in it, and each of those in turn has a Rock in them, which means that the compiler sees two Rocks in MeltedMess. With each Rock comes a separate Weight instance. The CrackingDiamonds example, shown in Listing 4-3, demonstrates the problem. This listing declares the classes Rock, Diamond, Jade, and MeltedMess.

LISTING 4-3: Cracking Diamonds

#include <iostream>

using namespace std;

class Rock {
public:
int Weight;
};

class Diamond : public Rock {
public:
void SetDiamondWeight(int newweight) {
Weight = newweight;
}

int GetDiamondWeight() {
return Weight;
}
};

class Jade : public Rock {
public:
void SetJadeWeight(int newweight) {
Weight = newweight;
}

int GetJadeWeight() {
return Weight;
}
};

class MeltedMess : public Diamond, public Jade {
};

int main(int argc, char *argv[])
{
MeltedMess mymess;
mymess.SetDiamondWeight(10);
mymess.SetJadeWeight(20);

cout << mymess.GetDiamondWeight() << endl;
cout << mymess.GetJadeWeight() << endl;
return 0;
}

One member is called Weight, and it’s part of Rock. The Jade and Diamond classes include two accessor methods, one to set the value of Weight and one to get it.

The MeltedMess class derives from both Diamond and Jade. The code creates an instance of MeltedMess and calls the four methods that access the supposedly single Weight member in Rock. The code calls the accessor for Diamond, setting Weight to 10. Then it calls the one for Jade, setting Weight to 20.

In a perfect world, in which each object only has one Weight, this would have first set the Weight to 10 and then to 20. When you print it, you should see 20 both times. But you don’t:

10
20

Repairing the diamond-shaped inheritance problem

When you print the Diamond portion of the MeltedMess instance Weight in Listing 4-3, shown previously, you see 10. The Jade portion displays 20 instead. Therefore, mymess has two different Weight members. That’s not a good thing.

To fix it, add the word virtual when you inherit from Rock. According to the ANSI standard, you put virtual in the two middle classes (as shown in the CrackingDiamonds2 example provided with the downloadable source and explained in this section). This means Diamond and Jade in this case. Thus, you need to modify the class headers in Listing 4-3 to look like this:

class Diamond : virtual public Rock {

and this:

class Jade : virtual public Rock {

When you make these modifications and then run the application, you find that you have only one instance of Weight in the final MeltedMess class instance, mymess. It’s not such a mess after all! Here’s the output after making the change:

20
20

Now this makes sense: Only one instance of Weight is in the mymess object, so the following line changes the Weight to 10:

mymess.SetDiamondWeight(10);

Then the following line changes the same Weight to 20:

mymess.SetJadeWeight(20);

You can also access Weight directly now without error, so the accessor methods aren’t strictly needed:

mymess.Weight = 30;

Then the following lines print the value of the one Weight instance, 30:

cout << mymess.GetDiamondWeight() << endl;
cout << mymess.GetJadeWeight() << endl;
cout << mymess.Weight << endl;

Remember With a diamond inheritance, use virtual inheritance in the middle classes to ensure that they point to the correct type. Although you can also add the word virtual to the final class (in the example’s case, that’s MeltedClass), you don’t need to.

Friend classes and functions

You may encounter a situation in which you want one class to access the private and protected members of another class. Normally, doing so isn’t allowed. But it is if you make the two classes friends. C++ provides the friend keyword to override the normal class protections.

Use friend only when you really need to. If you have a class, say Square, that needs access to the private and protected members of a class called DrawingBoard, you can add a line inside the class DrawingBoard that looks like this:

friend class Square;

This code allows the code in Square to access the private and protected members of any instance of type DrawingBoard.

Remember In many cases, allowing complete access of one class by another class opens too many possibilities for bugs and security issues (among other things). Friend functions are a more limited form of the friend keyword because they limit access to a global function or a single method within a class. If you need to provide friend access for some reason, using a friend function is better. Listing 4-4 shows the BestFriends example that demonstrates both friend classes and friend functions.

LISTING 4-4: Working with Friends

#include <iostream>

using namespace std;

class PAndP;

class Limited {
public:
void ShowProtected(PAndP &);
};

class PAndP {
public:
friend class Peeks;
friend void Limited::ShowProtected(PAndP &X);
friend void FriendFunction(PAndP &X);
protected:
void IsProtected() {cout << "Protected" << endl;}
private:
string var = "Var";
void IsPrivate() {cout << "Private " << var << endl;}
};

class Peeks {
public:
void ShowProtected(PAndP &X) {X.IsProtected();}
void ShowPrivate(PAndP &X) {
X.var = "From Peeks";
X.IsPrivate();
}
};

void Limited::ShowProtected(PAndP &X){
X.IsProtected();
}

void FriendFunction(PAndP &X) {
X.IsProtected();
X.var = "From FriendFunction";
X.IsPrivate();
}

int main() {
PAndP Hidden;
Peeks ShowMe;
Limited ShowMeAgain;

ShowMe.ShowProtected(Hidden);
ShowMe.ShowPrivate(Hidden);

ShowMeAgain.ShowProtected(Hidden);

FriendFunction(Hidden);
return 0;
}

The private and protected (PAndP) class contains protected and private members that Peeks, Limited::ShowProtected(), and FriendFunction() access. Each of these entities takes a different route, and that route isn’t always as obvious as it might be.

When working with the Limited class, coding order is important. You begin by creating a forward reference to class PAndP and then define the Limited class. However, you can’t define Limited::ShowProtected() yet because the members of PAndP aren’t known to the compiler and there isn’t a way to create a forward declaration of them. Consequently, the actual code for Limited::ShowProtected() comes later, which is the only method in Limited that has access to PAndP. If you were to try accessing PAndP from any other member, the compiler would complain.

Tip Creating the friend entries for Peeks and FriendFunction() is easier because you aren’t dealing with just a part of the element. All you need are the friend entries in the PAndP class. Note that every method or function that interacts with PAndP receives a pointer to a PAndP instance to do so. The code in main() creates the required objects and accesses the PAndP protected and private members. You see the following output when you run this application:

Protected
Private From Peeks
Protected
Protected
Private From FriendFunction

Using Classes and Types within Classes

Sometimes an application needs a fairly complex internal structure to get its work done. Three ways to accomplish this goal with relatively few headaches are nesting classes, embedding classes, and declaring types within classes. The following sections discuss the two most common goals: nesting classes and declaring types within classes. The “Nesting a class” section also discusses protection for embedded classes.

Nesting a class

You may have times when you create a set of classes with one class acting as the primary class while all the other classes function as supporting classes. For example, you may be a member of a team of programmers, and your job is to write a set of classes that log on to a competitor’s computer at night and lower all the prices on the products. Other members of your team will use your classes in their applications. You’re just writing a set of classes; the teammates are writing the rest of the application. The following sections consider the issues involved in performing this task, as well as a potential solution in the form of nested classes.

Considering the nesting scenario issues

In the classes you’re creating, you want to make the task easy on your coworkers. In doing so, you may make a primary class, such as EthicalCompetition, that they will instantiate to use your set of classes. This primary class will include the methods for using the system. In other words, it serves as an interface to the set of classes.

In addition to the main EthicalCompetition class, you might create additional auxiliary classes that the EthicalCompetition class will use, but your coworkers won’t interact with directly. One might be a class called Connection that handles the tasks of connecting to the competitor’s computer. However, the Connection class may present these problems:

  • Potential conflicts: The class Connection may be something you write, but your organization may support another Connection class, and your coworkers might need to use that class.
  • Privacy: You may not want your coworkers using this special Connection class. Perhaps it has special functionality that would reveal too many organizational secrets, or you just want them using the main interface, the EthicalCompetition class.

To solve the unique name problem, you have several choices. For one, you can just rename the class something different, such as EthicalCompetitionConnection. But that’s a bit long for a class used exclusively for internal needs. However, you could shorten the class name and call it something that’s likely to be unique, such as ECConnection.

Yet at the same time, if the users of your classes look at the header file and see a whole set of classes, which classes they should be using may not be clear. (Of course, you would write some documentation to clear this up, but you do want the code to be at least somewhat self-explanatory.)

Understanding the nested class solution

One solution for dealing with both naming conflict and privacy issues for support classes is to use nested classes. With a nested class, you write the declaration for the main class, EthicalCompetition, and then, inside the class, you write the supporting classes, as in the following:

class EthicalCompetition {
private:
class Connection {
public:
void Connect();
};
public:
void HardWork();
};

Note that this shows a class inside a class. Here’s the code for the functions:

void EthicalCompetition::HardWork() {
Connection c;
c.Connect();
cout << "Connected" << endl;
}

void EthicalCompetition::Connection::Connect() {
cout << "Connecting…" << endl;
}

The header for the Connect function in the Connection class requires first the outer class name, then two colons, then the inner class name, then two colons again, and finally the function name. This follows the pattern you normally use where you put the class name first, then two colons, and then the function name. But in this case, you have two class names separated with two colons.

When you want to declare an instance of the Connection class, you do it differently, depending on where you are in the code when you declare it:

  • Inside a method of the outer EthicalCompetition class: You simply refer to the class by its name, Connection. Look at the method HardWork, with this line:

    Connection c;

  • Outside the methods: You can declare an instance of the inner class, Connection, without an instance of the outer class, EthicalCompetition. To do this, you fully qualify the class name, like this:

    EthicalCompetition::Connection myconnect;

    This line would go, for instance, in the main() function of your application if you want to create an instance of the inner class, Connection.

However, you may recall that one of the reasons for putting the class inside the other was to shield it from the outside world, to keep your nosy coworkers from creating an instance of it. But so far, what you’ve done doesn’t really stop them from using the class. They can just use it by referring to its fully qualified name, EthicalCompetition::Connection.

Creating an inner class definition

So far, you’ve created a handy grouping of the class, and you also set up your grouping so that you can use a simpler name that won’t conflict with other classes. If you just want to group your classes, you can use a nested class. If you want to add higher security to a class so that others can’t use your inner class, however, you have to create an inner class definition.

Here’s a series of three tricks devoted to showing you how you create that inner class definition. For the first trick, you declare the class with a forward definition but put the class definition outside the outer class. Never put the inner class definition inside a private or protected section of the outer class definition; it doesn’t work. The following code takes care of that declaration for you:

class EthicalCompetition {
private:
class Connection;
public:
void HardWork();
};

class EthicalCompetition::Connection {
public:
void Connect();
};

Here, inside the outer class, is a header for the inner class and a semicolon that you use instead of writing the whole inner class; that’s a forward declaration. The rest of the inner class appears after the outer class. To make this code work, you must fully qualify the class name, like this:

class EthicalCompetition::Connection

Tip If you skip the word EthicalCompetition and two colons, the compiler compiles this class as though it’s a different class. Later, the compiler will complain it can’t find the rest of the Connection class declaration. The error is

error: aggregate 'EthicalCompetition::Connection c' has incomplete type and cannot be defined

Remember that message so that you know how to correct it when you forget the outer class name.

By declaring the inner class after the outer class, you can now employ the second trick. The idea is to write the inner class so that only the outer class can access the members. To accomplish this task, you make all the members of the inner class either private or protected and then make the outer class, EthicalCompetition, a friend of the inner class, Connection. Here’s the modified version of the Connection class:

class EthicalCompetition::Connection {
protected:
friend class EthicalCompetition;
void Connect();
};

Only the outer class can access most of the Connection members now. However, even though the members are protected, nothing stops users outside EthicalConnection from creating an instance of the Connection class. To add this security, you employ the third trick, which is to create a constructor for the class that is either private or protected. When you change the constructor’s access, following suit with a destructor is a good idea. Make the destructor private or protected, too. Even if the constructor and destructor don’t do anything, making them private or protected prevents others from creating an instance of the class — others, that is, except any friends to the class. So here’s yet one more version of the class:

class EthicalCompetition::Connection {
protected:
friend class EthicalCompetition;
void Connect();
Connection() {}
~Connection() {}
};

This third trick completes the process. When someone tries to make an instance of the class outside EthicalCompetition (such as in main()), as in this:

EthicalCompetition::Connection myconnect;

you see the following message:

EthicalCompetition::Connection::~Connection()' is protected

You can still create an instance from within the methods of EthicalCompetition. The ProtectingEmbedded example, shown in Listing 4-5, contains the final application.

LISTING 4-5: Protecting Embedded Classes

#include <iostream>

using namespace std;

class EthicalCompetition {
private:
class Connection;
public:
void HardWork();
};

class EthicalCompetition::Connection {
protected:
friend class EthicalCompetition;
void Connect();
Connection() {}
~Connection() {}
};

void EthicalCompetition::HardWork() {
Connection c;
c.Connect();
cout << "Connected" << endl;
}

void EthicalCompetition::Connection::Connect() {
cout << "Connecting…" << endl;
}

int main(int argc, char *argv[]) {
// Uncomment this line to see the access error.
// EthicalCompetition::Connection myconnect;
EthicalCompetition comp;
comp.HardWork();
return 0;
}

Here’s the output from this example:

Connecting…
Connected

Types within classes

When you declare a type, such as an enum, associating it with a class can be convenient. For example, you may have a class called Cheesecake. In this class, you may have the SelectedFlavor property, which can be an enumerated type, such as Flavor:

enum Flavor {
ChocolateSuicide,
SquishyStrawberry,
BrokenBanana,
PrettyPlainVanilla,
CoolLuah,
BizarrePurple
};

Use this code to associate Flavor with a class:

class Cheesecake {
public:
enum Flavor
{
ChocolateSuicide, SquishyStrawberry, BrokenBanana,
PrettyPlainVanilla, CoolLuah, BizarrePurple
};
Flavor SelectedFlavor;

int AmountLeft;
void Eat() {
AmountLeft = 0;
}
};

You can use the Flavor type anywhere in your application, but to use it outside the Cheesecake class, you must fully qualify its name by lining up the class name, two colons, and then the type name, like this:

Cheesecake::Flavor myflavor = Cheesecake::CoolLuah;

An enum requires that you also fully qualify the enumeration. Using just CoolLuah on the right side of the equals sign will cause the compiler to complain and say that CoolLuah is undeclared. The Cheesecake example, shown in Listing 4-6, demonstrates how we can use the Cheesecake class.

LISTING 4-6: Using Types within a Class

#include <iostream>

using namespace std;

class Cheesecake {
public:
enum Flavor {
ChocolateSuicide, SquishyStrawberry, BrokenBanana,
PrettyPlainVanilla, CoolLuah, BizarrePurple
};
Flavor SelectedFlavor;

int AmountLeft;
void Eat() {
AmountLeft = 0;
}
};

int main() {
Cheesecake yum;
yum.SelectedFlavor = Cheesecake::SquishyStrawberry;
yum.AmountLeft = 100;
yum.Eat();
cout << yum.AmountLeft << endl;
return 0;
}

Remember When you declare a type (using a typedef or an enum) inside a class, you don’t need an instance of the class present to use the type. But you must fully qualify the name when you are using it from outside of the class. Thus, you can set up a variable of type Cheesecake::Flavor and use it in your application without creating an instance of Cheesecake.

In contrast to nested classes, you can make a type within a class private or protected. If you do so, you can use the type only within the class members. If you try to use the type outside the class (including setting a property, as in yum.SelectedFlavor = Cheesecake::SquishyStrawberry;), you get a compiler error.

Tip You can also put a typedef inside your class in the same way you’d put an enum inside the class, as in the following example:

class Spongecake {
public:
typedef int SpongeNumber;
SpongeNumber weight;
SpongeNumber diameter;
};

int main() {
Spongecake::SpongeNumber myweight = 30;
Spongecake fluff;
fluff.weight = myweight;
return 0;
}