I still don't, but only because it seems to me to be a real obvious error. But, having said that, I've made all kinds of really obvious errors before... |
It's an obvious error, but I've seen excellent programmers with more than 10 years of experience make this mistake. We are all only human. A really good program is maintainable, and that means that the code is structured in a way that makes it hard to make mistakes when modifying it. In other words, you want to avoid conditions where a change "here" will break something "there."
I'll give an example (and apologize for hijacking this thread). This is from the library for the AP C.S. course many years ago when it was taught in C++. They had a vector (okay, it was really a template) declared like this:
1 2 3 4 5 6
|
class vector {
unsigned size;
int *data;
public:
vector(int sz);
};
|
and in another file:
1 2 3 4
|
vector::vector(int sz) :
size(sz),
data(new int[size];
{;}
|
This works, but if I saw it in a code review at work, I would insist that it be changed. The reason is that the
size
and
data
members get initialized in the order that they are declared, not the order that their initializers appear in the constructor. So suppose someone decides to swap the position of
size
and
data
in the declaration:
1 2 3 4 5 6
|
class vector {
int *data;
unsigned size;
public:
vector(int sz);
};
|
simple right? What could go wrong? Only now the constructor, which isn't even in the same file, breaks because instead of calling
size(sz)
and then
data(new int[size])
, it calls
data(new int[size])
first, and since
size
is uninitialized at that point, who knows how much space will be allocated.
Even worse, this is the sort of bug that might get past regression testing if it happens to allocate enough space in the test program.
The change is to have the initializers independent of the order in which they are called:
1 2 3 4
|
vector::vector(int sz) :
size(sz),
data(new int[sz];
{;}
|