Using a namespace to Group Utility Functions

That takes care of the main program. Now we're going to take a look at a number of functions that have something in common but really don't constitute a class, as they don't share any data. It would be possible to use a class for grouping these functions, but there's a better way: creating a namespace to hold these functions. Of course, we've been using the std namespace throughout this book, but up until now we haven't made one of our own.[5] It's not terribly difficult, as you'll see. Let's start by taking a look at the HomeUtility namespace, starting with its interface, shown in Figure 13.5.

[5] Actually, this isn't quite correct. As I mentioned earlier, since every scope (and therefore every class) is a namespace, we really have been creating namespaces all along without even trying. However, this is the first namespace we've created that has a name and isn't a class.

Figure 13.5. The HomeUtility interface (codehmutil1.h)
//hmutil1.h

#ifndef HMUTIL1_H
#define HMUTIL1_H

namespace HomeUtility
{
bool ReadDoubleFromLine(std::istream& is, double& Result);
bool ReadLongFromLine(std::istream& is, long& Result);
bool ReadDateFromLine(std::istream& is, long& Date);

void IgnoreTillCR(std::istream& is=std::cin);
void HandleError(const xstring& Message);
short GetNumberOrEnter(bool AllowArrows=false);
void ClearRestOfScreen(short StartingRow);
short SelectItem(const Vec<short>& Number,
 const Vec<xstring>& Name);
enum KeyValue {e_Return = -1, e_Up = -2, e_Down = -3};
bool CheckNumericInput(std::istream& is);
}

#endif

This file declares a number of functions that can be used anywhere, so long as you precede their names with their namespace qualifier, namely HomeUtility. But you may be wondering why we should go to the bother of putting them all in a namespace. Why not just make them global?

The Advantages of Using namespaces

The reason is to avoid polluting the global namespace. That is, it's entirely possible that another programmer might write a function called HandleError, and we want to make sure that the code we write can coexist with code that uses such common names. By creating a namespace to hold these functions that would otherwise be global, we are preventing clashes with other functions that might have the same names.

How did I decide which functions should go into this namespace rather than anywhere else? My criterion was that the function could be used in more than one other class, so that it would most reasonably belong in a commonly accessible place such as a utility namespace.

You might not be surprised that Susan had some questions about this issue. Here is our discussion.

Susan: Exactly how is a namespace different from a class? I don't get it.

Steve: They are very similar. In fact, every class actually defines a namespace for its member variables and functions. You can think of a namespace as a class with all public static member variables and functions.

Susan: Okay, but why bother with this anyway? Why not just leave everything global?

Steve: It's an ecological issue. If we create global functions, especially ones with names that other programmers might want to use, it's like dumping garbage in the ocean where it can affect others. In fact, the practice of creating global functions without a good reason is widely referred to as “polluting the global namespace”, because such functions interfere with other programmers' use of the same names. Remember, there can be only one global function with a given name and parameter list, so we should create such functions only when absolutely necessary.[6]

[6] By an interesting coincidence, after first writing this paragraph I had exactly this problem when trying to reuse some old code I had written. This old code defined global names that conflicted with another library that the user of this code also needed. I fixed this by changing the names, but having kept them out of the global namespace would have prevented the problem.

Susan: OK, but why should these functions be in this namespace? How did you decide which ones should be here?

Steve: A function belongs in the utility namespace if it is used in several other classes. We certainly don't want to copy it into each of those classes: that wouldn't be very object-oriented!

Now that we know why we need the functions in the HomeUtility namespace, I should explain what the new construct bool AllowArrows=false means in a function declaration and why we need to specify it here.

Default Arguments

This is another C++ feature that we haven't seen before. It's called a default argument, and its purpose is to specify a value for an argument to a function when the user of the function doesn't supply a value for that argument. In this case, the construct “bool AllowArrows=false” specifies that if the application programmer calls GetNumberOrEnter without specifying an argument, the AllowArrows argument will be assumed to be false. On the other hand, if the application programmer calls GetNumberOrEnter with a bool argument, the AllowArrows argument will be set to the bool value supplied in the function call.

We don't have to use default arguments if we don't want to; we can achieve the same effect by writing a separate function for each possible number of arguments supplied by the calling program. To see how this alternative works, let's start with Figure 13.6, which shows an interface file that contains two overloaded functions that can be replaced by one function with a default argument.

Figure 13.6. Not declaring a default argument (code odef.h)
void Answer(int x);
void Answer();

The implementation of the first set of functions is shown in Figure 13.7.

Figure 13.7. Not using a default argument (code odef.cpp)
#include <iostream>
#include "nodef.h"
using namespace std;

void Answer(int x)
{
cout << "Here is the answer: " << x << endl;
}

void Answer()
{
cout << "Here is the answer: " << 42 << endl;
}

main()
{
 Answer(10);
 Answer();
}

Now how would we do this using a default argument? Figure 13.8 shows the header file for this approach.

Figure 13.8. Declaring a default argument (codedefault.h)
void Answer(int x=42);

Figure 13.9 shows the implementation for this function:

Figure 13.9. Using a default argument (code odef.cpp)
#include <iostream>
#include "default.h"
using namespace std;

void Answer(int x)
{
cout << "Here is the answer: " << x << endl;
}

main()
{
 Answer(10);
 Answer();
}

To recapitulate, specifying a default argument as int x=42 for the argument list of a function is exactly equivalent to writing two overloaded functions:

  1. One with no argument, having a constant value 42 for the value to be used in the function.

  2. One with an int argument having no default value.

Of course, the output of the program is the same whether we use one function with a default argument or two functions with no default arguments; the difference is only in how we implement the same functionality.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset