Here is a C++ gotcha that derailed me for a while the other day. C++ added some fetchers I love, one being pass-by-reference. Not to dive into it too deep (if you're reading this, you likely understand enough C++ to know this), here's an example: What I find nice about it is when passing around classes. It avoids having to take the address of objects (the & operator) and makes things a little more clear.
And now, a gotcha. This will compile without error, but I missed something rather important. When buried in thousands of lines of code spanning tens of files, the missing character is really hard to stop.
The result of this code will print "50" if it doesn't crash. Why? What happened? The mistake is in the constructor for the class B. Notice how class B has a reference to some instance of class A. To the compiler, this is just a pointer that is automatically dereferenced when used. It it required that a reference in a class be initialized in the constructor, which we did. But we didn't pass a reference to the constructor--we passed a temporary copy. B::a is initialized pointing to the copy--likely some location on the stack. The declaration of the constructor should have been: All that was missing was the ampersand to denote that "a_" was a reference to some instance of class A. In my case, class A was fairly complicated and had a standard list member (std::list). When I called functions that manipulated the list, random results happened. Sometimes it crashed the program--other times it did not. Tracing down problems like this is difficult. Even with the most pedantic compiler warnings (which I always have turned on), there was nothing said about this problem. It's hard to say when such this mistake could be valid syntax--taking a reference to temporary value would only be useful for the duration of the constructor. So I could see a compiler warning being globally applicable for such things. But the bottom line is that the mistake was mine--and a hard gotcha it was.
void someFunction( int & value )
{
++value;
}
//...
value = 100;
someFunction( value );
// value is now 101
{
++value;
}
//...
value = 100;
someFunction( value );
// value is now 101
And now, a gotcha.
class A
{
public:
void put( int newValue )
{
value = newValue;
}
int get() const
{
return value;
}
private:
int value;
};
class B
{
public:
B( A a_ )
:
a( a_ )
{
}
void put( int value )
{
a.put( value );
}
private:
A & a;
};
// ...
A a;
B b( a );
a.put( 50 );
b.put( 100 );
cout << a.get() << endl;
{
public:
void put( int newValue )
{
value = newValue;
}
int get() const
{
return value;
}
private:
int value;
};
class B
{
public:
B( A a_ )
:
a( a_ )
{
}
void put( int value )
{
a.put( value );
}
private:
A & a;
};
// ...
A a;
B b( a );
a.put( 50 );
b.put( 100 );
cout << a.get() << endl;
The result of this code will print "50" if it doesn't crash. Why? What happened? The mistake is in the constructor for the class B. Notice how class B has a reference to some instance of class A. To the compiler, this is just a pointer that is automatically dereferenced when used. It it required that a reference in a class be initialized in the constructor, which we did. But we didn't pass a reference to the constructor--we passed a temporary copy. B::a is initialized pointing to the copy--likely some location on the stack. The declaration of the constructor should have been:
B( A & a_ )
:
a( a_ )
{
}
:
a( a_ )
{
}
1 comment has been made.
From hef
August 14, 2013 at 5:30 PM
Check out valgrind, I think it can catch that kind of error. Valgrind can get a little noisy though.