Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Tuesday, April 24, 2007

It's easy to make mistakes while coding C++

I was reading CLC++ today and found this little gem. It's got 3 glaring problems in 23 lines of C++ code.


Guys, I have the following piece of code. Could you please help me understand why
b.ToString( ) cannot be called while b.foo( ) can? When I compile I get (gcc, but visual studio gives the same pretty much). Thanks

$ g++ -Wall foo.cpp
foo.cpp: In function `int main(int, char**)':
foo.cpp:21: error: no matching function for call to `Bar::ToString()'
foo.cpp:14: note: candidates are: virtual std::string Bar::ToString(std::string&) const

#include < stdlib.h>
#include < string>
using namespace std;

template < typename> class Foo {
public:
virtual std::string ToString (std::string& pfx) const = 0;
std::string ToString ( ) const { return(ToString(std::string( ))); }
virtual void* _foo ( ) const = 0;
void* foo( ) const { return (_foo( )); }
};

class Bar: public Foo< int> {
public:
std::string ToString (std::string& pfx) const { return (std::string("test")); }
void* _foo ( ) const { return(NULL); }
};

int main (int argc, char **argv) {
Bar b;
b.foo( );
b.ToString( );
return (0);
}

Gratz, you have manged to demonstrate two C++ gotchas in your short code: binding temporaries to non-const reference and name hiding. When derived class uses same name declared in base class, the name in base class is hidden. The example below is how you fix them:

#include < stdlib.h>
#include < string>
using namespace std;

template < typename> class Foo {
public:
virtual std::string ToString (const std::string& pfx) const = 0;
std::string ToString ( ) const { return(ToString(std::string( ))); }
virtual void* _foo ( ) const = 0;
void* foo( ) const { return (_foo( )); }
virtual ~Foo(){}
};

class Bar: public Foo< int> {
public:
using Foo< int>::ToString;
std::string ToString (const std::string& pfx) const { return (std::string("test")); }
void* _foo ( ) const { return(NULL); }
};

int main (int argc, char **argv) {
Bar b;
b.foo( );
b.ToString( );
return (0);
}

Although arguably it's not an issue in this short example, but make it a habit to declare a virtual destructor in the base virtual class. It'll save a lot of headache in the future.