So it looks like I forgot all about this blog for the last few weeks. Oh dear. But now I’ve remembered that it exists, so here’s some C++ stuff that I discovered recently, based on some interview questions I’ve been doing.
I thought that I had a fairly good idea about how C++ worked, but of course I didn’t, and a question that looked pretty simple on the surface can have lots of subtleties. These usually make themselves known during an interview, when a candidate writes a little bit of code that you hadn’t anticipated, and you’re not sure whether it would compile. Fortunately, the candidate usually has bigger problems than pointing out that the interviewer is a simpleton, but it’s always good to go away and figure out exactly what’s going on with the problem you thought you knew inside out.
To make it easier to see what’s going on, I’ll use the format Code Listing / Question / Solution / Discussion for each problem. (Note that these aren’t the actual interview questions I use, but are small test cases that illustrate what it was that I wasn’t sure about. They are “complete” C++ programs.)
Problem #1: local static variable lifetime
Code
#include <iostream>
using namespace std;
class chatty
{
public:
chatty() { cerr << "chatty()" << endl; }
~chatty() { cerr << "~chatty()" << endl; }
};
void main()
{
cerr << "main()" << endl;
static chatty c;
cerr << "exit main()" << endl;
}
Problem
The program above uses a local static variable in its main() function. When does the constructor get called for this variable? When does the destructor get called?
Solution
First, the line “main()” is printed, then the line “chatty()”, then the line “exit main()”. “~chatty()” never gets printed.
Discussion
This behaviour is what I expected, which is always nice. Basically, local static variables are initialized the first time that the program gets to their declaration. They are never destroyed. They are “better” than standard global variables and static class members in that the construction time is deterministic, but they still have the problem that their destructors are never called. They’re great for little conveniences such as counters and timers, but I’d stay away from using non-integral types in static variables in “real” production systems. Store your state in class members instead, and make the function containing the local static variable a class method.
Problem #2: Auto-generated class methods
Code
#include <iostream>
using namespace std;
class bad_singleton
{
bad_singleton() { cerr << "bad_singleton()" << endl; }
~bad_singleton() { cerr << "~bad_singleton()" << endl; }
public:
static bad_singleton & instance()
{
static bad_singleton inst;
return inst;
}
};
Problem
In this listing, a rather lazily-designed singleton class has been defined. What can go wrong when you write code that uses this class?
Solution
It is possible to create more than one instance of this class. While the constructor is private, the copy constructor is not declared, and will therefore be generated for us in the usual way – i.e. with public visibility.
Another problem is that the inst variable will never have its destructor called, as in Problem #1.
Discussion
An example of some code that you might write, if you’re not careful, is the following:
void main()
{
bad_singleton bs = bad_singleton::instance();
}
In this case, we’re trying to get the instance (which is returned by reference), but we’ve declared bs as a bad_singleton, not a reference to a bad_singleton (we forgot the ampersand). All is not lost: while our auto-generated copy constructor can perform the copy implied with this line of code, the private destructor will lead to a compile error, since bs will go out of scope at the end of main(), and the compiler will attempt to put in a call to the destructor, which fails.
We can get around this with the following (obviously bad) code:
void main()
{
bad_singleton* bs = new bad_singleton(bad_singleton::instance());
}
This code now compiles, and we can create as many of our singleton as we like. Not so single after all.
Problem #3: Template recursion
Code
template<int N>
class factorial
{
public:
enum { value = N*factorial<N-1>::value };
};
template<>
class factorial<1>
{
public:
enum { value = 1 };
};
void main()
{
int a = factorial<4>::value;
int b = factorial<400>::value;
}
Problem
The code listing above gives us a templated (i.e. compile-time) factorial function (!). What happens when we hit “Compile” on this file, and then run it?
Solution
The factorial function given above is perfectly valid, and the computation of 4! (4 factorial) should work as expected. However, when we pass 400 as a template parameter, we have two problems:
- Our int will overflow (400! is a very very big number, much more than 32 bits can represent in integer form)
- Our compiler has to recurse 400 times to generate the result of this computation. This is a pretty deep parse-tree and there’s a lot of generated code lurking in there.
Discussion
The actual results will be compiler-dependent. On Visual C++ 2008, the compiler tries its best, and issues lots of warnings about integer overflow (for all the overflowing recursions, which is most of them), and then either succeeds, or gives a recursion error, or crashes out, depending on how much memory you have, the day of the week, and the colour of your bedroom walls. The template recursion depth of your compiler may have a hard limit set (say max 20 recursions) to avoid this slightly unstable situation, and the moral of the story is: don’t take template metaprogramming too far! Think of it like a normal run-time recursive function, but with a very small stack.
The other thing about this particular bit of templatery is that it’s completely insane. Computing factorials with your compiler is weird.
