|
By Reg. Charney
The Problem
Recently, I was using a powerful non-C++ class library. Within it
guts, it had an important class that kept a container of items that
I needed to extend. However, the design of the class precluded my
doing this from outside the class itself. This meant that I had two
basic choices: take ownership of the class forever; or foregoing
changing the original class and designing a complete replacement.
Neither choice was a very appetizing. In fact, if the original class
had been properly designed, I would not have faced this decision at
all and could have extended the class easily.
Collection Principles
I would like to talk about avoiding this problem in future. But
first, we need to discuss a few general principles. When we ask a
container to hold information for us, we expect it to do that,
regardless of what we do with the original copy of the data. For
example, if a container is meant to hold a set of book titles, then
it is safest for all concerned if it keeps copies of the titles. In
that way, we can do anything we need to do with the original data
and know our collection is safe as would happen if the book titles
are read off a database, one title per record/row at a time. A
second general principle states it is often better to keep pointers
to data in a collection than to keep the data itself. The main
reason is to allow for polymorphism. We will discuss that a little
later. However, it is tempting to pass the pointers that you
generate to your data to the collection and let it keep those
copies. Great care must be exercised when doing this. Since you
generate the pointers, you have nominal ownership of the data being
pointed at. Thus, you have the ability to delete the data whenever
you wish, even though the data is still being referenced by elements
of the collection. This often causes very ugly problems at program
termination.
Examples
| I use some coding
conventions when I am writing code in limited space like
this column:
I use the using namespace
std; statement. However, it is almost always better
to use explicitly qualified instances of std members. For
example:
list<X> lx; and
cout << endl;
would be better written as:
std::list<X> lx; and
std::cout << std::endl;
I have also in-lined all the member functions. Their
declarations and definitions should be in different source
files. |
In this simple example, class A has a container of class X
objects. We can add items to the container in A and to print the
contents of the container that A holds. Since A will be extended,
some of its member functions are virtual. Class Y is derived from X.
Also, I wanted to be able to add both stack and heap variables to
the collection.
(For simplicity in this column, I have used an STL std::list
container. While I am working from the assumption that
templates are not available, as is the case in other languages like
Java and Delphi, I have used the one instance of a list of X
objects. Other languages have containers that can hold references to
generic objects).
#include
<iostream>
#include <string>
#include <list>
using namespace std;
class X {
string s;
public:
X(const char* ss):s(ss) { }
virtual string prt() { return s; }
};
class Y : public X {
string t;
public:
Y(const char* ss, const char* tt) : X(ss), t(tt) { }
virtual string prt() { return X::prt()+t; }
};
class A {
typedef list<X*> LXP;
typedef LXP::iterator LXPI;
protected:
LXP ls; // container of X*s
public:
virtual void prt(const char *t)
{
cout << t << endl;
for(LXPI i=ls.begin(); i!=ls.end();++i)
cout << (*i)->prt()
<<endl;
}
virtual void add(const X& rx)
{
X* xp = new X(rx); // 1
ls.push_back(xp);
}
virtual ~A()
{
for(LXPI i=ls.begin(); i!=ls.end(); ++i)
delete *i;
}
};
struct B : public A {
virtual void add(const Y& ry) // 2
{
Y* yp = new Y(ry);
ls.push_back(yp);
}
};
int main()
{
A a1;
{
X x1("a"), x2("b"), *px;
a1.add(x1);
a1.add(x2);
px = new X("c");
if (px)
a1.add(*px);
delete px;
} // deletes stack items
a1.prt("A1 list is:");
B b1;
{
Y y1("d","1");
Y y2("e","2");
Y *py;
b1.add(y1);
b1.add(y2);
py = new Y("f","3");
if (py)
b1.add(*py);
delete py;
} // deletes stack items
b1.prt("B1 list is:");
return 0;
} |
The add() function must be overridden (as in //2) , else derived
classes will be truncated at //1 when the copy constructor is called
prior to the pointer being inserted into the collection.
by Francis Glassborow
The exact route by which things have become part of C or C++ is
often shrouded in private interactions. Throughout much of the
development of ISO Standard C and pre-standard versions of C++ Brian
Kernighan and Bjarne Stroustrup were separated by a few feet of
corridor. That means that ideas were certainly being swapped back
and forth as both language designers have a high regard for each
other.
The public position has always been that const
was an invention by Bjarne Stroustrup and that volatile was the
invention of some member of J16. There were several motivations for
introducing const into C++, probably the biggest was to couple it
with references to allow passing of arguments that would behave much
like value arguments whilst having the efficiency of a pass by
pointer.
Once the decision had been made to provide const reference
parameters, the grammar of the language almost inevitably led to
other uses of const. But the point
to note is that in order to support the semantics of const
references C++ also needed to require const correctness. By that I
mean that use of an object via a const
guaranteeing name must honour that promise.
const correctness has a hidden
cost in that a return value based on a const
qualified object must retain that qualification. The consequence is
that objects can acquire const
protection in their passage through a function. One of the classic
examples is found in the Standard C Library; strstr().
This function takes a pointer to a const
array of char and returns a pointer
to an array of char in C. That will
not do in C++ because the return value is based on the pointer
provided as an argument. The function promises to prevent the data
pointed to from being changed, but the return does not support that
promise.
C++ has a simple fix for that problem; it uses a pair of
overloaded functions. One function takes and returns a char
const * and the other takes and returns a char
*. There is also a basic C++ programming idiom that
effectively allows the same source code to define both functions:
char const*
strstr(char const*,char const*);
inline char* strstr(char* s1,char const* s2)
{
return const_cast<char *>(strstr(const_cast<char
const *> s1, s2));
} |
which effectively instructs the compiler to use the
implementation of the const version
for the non-const one if the first
argument is not const. but return a
plain char*.
The point I am making here is that C++ const
correctness relies on function overloading in order to work. It is
not immediately obvious to the casual observer that const
correctness has anything to do with function overloading.
Remove function overloading and you are faced with a very
different set of circumstances which C programmers call const
poisoning.
Even implementing such potentially helpful concepts such as const
comes at a cost. Next time you find yourself asking for some new
feature to be added in the next revision of your favourite language,
stop and ask yourself what hidden costs there may be. I happen to
think that C would benefit for a controlled form of function
overloading, but that is another story that I will leave for next
month.
By Reg. Charney
Tax season is rapidly approaching. Paying taxes often involves a
complex series of forms and rules that are beyond any single person’s
ability to comprehend and fill out correctly. That is why many
Americans need to pay an additional tax in the form of purchasing
tax software or tax advice. (Think of the savings if the tax system
was simpler and more equitable.)
We now have an additional quirk in dealing with our taxes. As
reported by Walter Mossberg in the Wall Street Journal on January
30, 2003, we also have spyware to contend with when we try to use
the latest version of TurboTax from Intuit.
It seems that Intuit now treats all its customers like criminals.
Intuit believes that you are intent on stealing their software by
handing out copies of the software to other members of your family.
To prevent this criminal behavior, Intuit secretly installs a
spyware program from Macrovision called SafeCast. It supposedly
ensures that TurboTax is only running on the one PC on which the
mandatory “activation” occurred. You activate your copy of
TurboTax when you register it online. This process then secretly
installs the spyware program and you are exposed. By the way, this
spyware program is not uninstalled when you remove TurboTax, nor
does it cease operating when tax season is over. To defend the
indefensible, Intuit has since claimed that no personal information,
like income and expenses, are sent anywhere—only program usage is
monitored. Yea, we should believe a company that treats us like
criminals and spies on us in secret. All this while handling our
most confidential information.
I agree with Walter Mossberg—get the excellent competing
product TaxCut by H&R Block. It is less expensive and it does
not spy on you. Also your whole household is licensed to use it.
I was contact early this month by Luby Aczel, a Volunteer
Coordinator for the non-profit Northern California studio of
Recording for the Blind and Dyslexic. They create and lend
educational material on CD or four track cassette to individuals
with learning or visual disabilities. They need volunteers to read
the many technical and computer manuals for which they get requests.
Please visit their web site at www.rfbd.org
to find out more about them. You can also contact Luby Aczel, at
650-493-3717, ext. 13
Extreme Programming
Perspectives by Michele Marchesi, Giancarlo Succi, Don
Wells, and Laurie Williams, Addison-Wesley, 2002, ISBN
0-201-770055-9
Rating: Highly Recommended
Reading Level: Easy.
This book is a comprehensive collection of articles by many
authors on various aspects of Extreme Programming.
No specific experience in any one programming language or
environment. It is assumed that the reader has significant
programming and architecture experience.
The style is very readable from all authors. This speaks well of
the authors, editors, and XP in general. Bear in mind that XP
considers clear communication and expression an essential component
of a successful project.
I do not have years of XP experience. I think, in general, that
it is difficult to define precisely the XP "domain". The
book presents a wealth of perspectives by people who understand and
convey the topic well. I am pleased by the coverage.
I found no errors worth mentioning. The content of the book was
not so technical that errors in a sample algorithm would be an
issue.
Extreme Programming is an approach that I think has much merit.
Its adoption, however, encounters resistance due to misconceptions
about what it is and the zealous behavior of its practitioners. This
book serves both the potential adopter and the practitioner by
viewing XP from many angles and answering tough questions.
— Al Bauer
By Reg Charney and Ali
Çehreli
I have included two figures in this month’s column. I was stuck
by the similarities of seemingly independent IT areas: job openings
by type of computer language; and job openings by type of platform.
In both cases and across most series, there seems to be a flattening
out and even a slight increase in job openings. Also, this has gone
on for more than three months. I also have antidotal evidence that
things may be turning around. I and some of my acquaintances have
been approached about job openings without explicit effort on our
part.


|