C++ Newsletter/Tutorial Issue 5
Issue #005
January, 1996
Contents
- Web Glossary
- New Fundamental Type in C++ - bool
- Using C++ as a Better C Part 5 - Function Overloading
- Book Review - The Mythical Man Month
- Example C++ Class - Mean and Standard Deviation
WEB C++ GLOSSARY
I have put up a glossary of C++ terms on the Web. The address is:
Please let me know if you have suggestions for additional terms to be added to the list.
NEW FUNDAMENTAL TYPE IN C++ - BOOL
A new fundamental (builtin) type has recently been added to C++. It is a type for representing Boolean values and uses the keyword "bool". For example, you could say:
bool b;
b = true;
if (b)
...
A bool value is either true or false. A bool value can be converted to an integer:
bool b;
int i;
b = false;
i = int(b);
in which case false turns into 0 and true into 1. This process goes under the C/C++ name of "integral promotion".
A pointer, integer, or enumeration can be converted to a bool. A null pointer or zero value becomes false, while any other value becomes true. Such conversion is required for conditional statements:
char* p;
...
if (p)
...
In this example "p" is converted to bool and then the true/false value is checked to determine whether to execute the conditional block of code.
Why is a bool type an advantage? You can get a variety of opinions on whether this is a step forward. In C, common usage to mimic this type would be as follows:
typedef int Bool;
#define FALSE 0
#define TRUE 1
One problem with such an approach is that it's not at all type-safe. For example, a programmer could say:
Bool b;
b = 37;
and the compiler wouldn't care. Another problem is displaying values of Boolean type:
printf("%s", b ? "true" : "false");
which is awkward. In C++ it is possible to set up a stream I/O output operator specifically for a particular type, and thus output of bool values can be distinguished from plain integral types. This is an example of function overloading (see next section). Without bool as a distinct type, usage like:
void f(int i) {}
void f(Bool b) {}
would be invalid.
Finally, why wasn't bool added to the language, but as a class type found in a standard library? This question is hard to answer, but one possible reason is that many C implementations have supplied a Boolean pseudo-type using a typedef and #define scheme as illustrated above, and these implementations rely on representing Booleans as integral types rather than as class types.
USING C++ AS A BETTER C PART 5 - FUNCTION OVERLOADING
Suppose that you are writing some software to manipulate calendar dates, and you wish to allow a user of the software to specify dates in one of two forms:
8, 4, 1964 (as a triple of numbers)
August 4, 1964 (as a string)
In C, if there is a function to convert a raw date into an internal form (for example, the number of days since January 1, 1800), it might look like:
long str_to_date(int m, int d, int y) { ... }
long str_to_date(char* d) { ... }
with one function for each of the two types of dates. Unfortunately, this usage is illegal in C, because two different functions cannot have the same name "str_to_date".
In C++ this usage is legal and goes by the term "function overloading". That is, two or more functions may have the same name, so long as the parameter types are sufficiently different enough to distinguish which function is intended. A function may not be overloaded on the basis of its return type.
The question of what makes two function parameter lists sufficiently different is an interesting one. For example, this usage is not valid:
void f(int) {}
void f(const int) {}
whereas saying:
void f(int) {}
void f(long) {}
is fine.
A common place where function overloading is seen is in constructors for a class. For example, we might have:
class Date {
...
public:
Date(int m, int d, int y);
Date(char*);
};
to represent a calendar date. Two constructors, representing the two ways of creating a date object (from a triple of numbers and from a string) are specified.
What can go wrong with function overloading? Consider an example of a String class:
class String {
...
public:
String();
String(char*);
String(char);
};
Here we have three constructors, the first to create a null String and the second to create a String from a char*. The third constructor creates a String from an individual character, so that for example 'x' turns into a String "x".
What happens if you declare a String object like this:
String s(37);
Clearly, the first String constructor won't be called, because it takes no arguments. And 37 isn't a valid char*, so the second constructor won't be used. That leaves String(char), but 37 is an int and not a char. The third constructor will indeed be called, after 37 is demoted from an int to a char.
In this case, the user "got away" with doing things this way, though it's not clear what was intended. Usage like:
String s(12345);
is even more problematic, because 12345 cannot be converted to a char in any meaningful way.
The process of determining which function should be called is known as "argument matching", and it's one of the most difficult aspects of C++ to understand. Function overloading is powerful, but it's smart to use it in a way that makes clear which function will be called when.
BOOK REVIEW - THE MYTHICAL MAN-MONTH
Some of you may have heard of the book "The Mythical Man-Month" by Fred Brooks. It was first published in 1975 and was updated last year. It is published by Addison-Wesley and costs about $25, and is considered a classic with 250,000 copies in print. Brooks was the manager of the project that developed OS/360 during the early 1960s.
The subject of the book is software development and the complexities associated with it. In the earlier chapters, which have not been updated since the 1975 edition, he talks about a variety of issues. One of my favorite parts is in the first chapter, entitled "The Tar Pit". In the discussion in this chapter he distinguishes four stages in the evolution of a finished software product:
- a program
- a programming system, with interfaces and system integration
- a programming product, with generalization, testing, documentation, and maintenance
- a programming systems product
A program is something you might quickly put together in a few hours or days or weeks. But to take the additional two steps of coming up with a programming system or programming product is a lot of additional work, on the order of 3X as Brooks describes it. Each of these steps is independent, therefore Brooks talks about a 9X ratio of cost between a program and a programming systems product.
Of course, 9X isn't a magic figure, but it captures the huge difference in cost between hacking out a few thousand lines of code over the weekend and putting out a polished product to customers.
The book has been updated with significant new material. He discusses the promise and practicality of object-oriented programming, software reuse, and so on.
Highly recommended.
EXAMPLE C++ CLASS - MEAN AND STANDARD DEVIATION
Here is a complete code example that shows a simple but powerful way of doing numerical calculations. You've probably had many occasions where you need to find the average value of a set of numbers. You set up a couple of variables to accumulate the count of numbers and the total, and then divide to get the average value.
This example shows another way of doing the same thing, without setting up any of these variables or worrying about the details of the calculations. Instead, you set up a Stat class object and then feed numbers into it. The object has an internal state that keeps track of all the details of what numbers have been seen. When you're done feeding in the numbers, you can interrogate the object to give you the average value, the number of values seen, the standard deviation (a statistical measure), and so on.
The same technique can be used in many diverse areas. As an exercise, try extending this class to also compute the smallest and largest numbers from a set, and come up with member functions that will allow a user to retrieve these values.
#include <stdio.h>
#include <assert.h>
#include <math.h>
class Stat {
long cnt; // number of values
double sumx; // sum of values
double sumx2; // sum of squares of values
public:
Stat(); // constructor
void add(double); // add a value
long n(); // get number of values
double sum(); // get sum of values
double mean(); // get average
double stdev(); // get standard deviation
};
// constructor
Stat::Stat()
{
cnt = 0;
sumx = 0.0;
sumx2 = 0.0;
}
// add a value
void Stat::add(double d)
{
cnt++;
sumx += d;
sumx2 += d * d;
}
// get number of values
long Stat::n()
{
return cnt;
}
// get sum of values
double Stat::sum()
{
assert(cnt >= 1);
return sumx;
}
// get average value
double Stat::mean()
{
assert(cnt >= 1);
return sumx / double(cnt);
}
// get standard deviation
double Stat::stdev()
{
assert(cnt >= 2);
return sqrt((double(cnt) * sumx2 - sumx * sumx) /
(double(cnt) * double(cnt - 1)));
}
// driver program
main()
{
Stat st;
int i;
for (i = 1; i <= 10; i++)
st.add(double(i));
printf("n = %ld\n", st.n());
printf("average = %.2f\n", st.mean());
printf("standard deviation = %.2f\n", st.stdev());
return 0;
}
-------------------------
Copyright (c) 1996 Glen McCluskey. All Rights Reserved.
This newsletter may be further distributed provided that it is copied in its entirety, including the newsletter number at the top and the copyright and contact information at the bottom.
Glen McCluskey & Associates
Professional C++ Consulting
Internet: glenm@glenmccl.com
Phone: (800) 722-1613 or (970) 490-2462
Fax: (970) 490-2463
FTP: rmii.com /pub2/glenm/newslett (for back issues)
Web: http://www.rmii.com/~glenm