The Forkless Philosopher

On C++

A Complex Language

C++ is an extremely complex programming language - to write bug-free code takes a lot of knowledge beyond the mere syntax as it is taught in the numerous books on the subject. Actually, C++ is so complex, that, when somebody denies this being the case, chances are high that that someone might actually be suffering from the Dunning-Kruger effect.
I have been working professionally in C++ development on and off for some six or so years by now and yes, it took me a while as well to realize that C++ is more than just the sum of the language's keywords. My moment of epiphany came when I read the seminal "Effective C++" by Scott Meyers for the first time. It has changed my attitude towards the language profoundly and has subsequently been only the first in a range of similar books I have since read about C++. If you haven't read Meyers so far and want to be more than a code monkey in the C++ world, that's the first thing you should catch up with (for other books, see "Recommended Reading" further down).

The Need For In-Depth Knowledge

Over the years the number of projects where the complexity of C++ could largely (or at least partly) be ignored has been shrinking constantly.
20 years ago, C++ was the standard language for pretty much everything from standalone gui applications to CGI driven web sites. These days, one of the biggest markets for C++ developers is embedded software development. An area where standards occasionally go as far as mandating the use of C/C++ but where at the same time even the smallest memory leak is completely unacceptable - in a device that's running 24/7 with a very limited amount of memory, even a leak of only one byte per hour will eventually crash the system. And memory leaks are rather easy to implement in C++.

Testing Prospective Job Candidates

It is therefore not surprising that companies looking for C++ developers now often employ small tests to check if applicants are up to scratch.
These tests might be anything from an hour long Q&A session by telephone, an online multiple choice test or some "implement <insert_challenge> in x minutes" scenario (an expertly explained example of this can be found at "The Anatomy of the Assignment Operator". It is definitely worth reading, even if it is only to assure yourself that you would have thought of all these eventualities). And of course, the company you apply at might even make it something like a college exam - have a look "C++ Language-specific Questions" for an example.

Over the years I have come across the odd test myself. I found them extremely interesting as they sometimes contained questions I couldn't yet answer at the time, providing valuable tips about areas where I could (and should) improve my knowledge.
At some point I decided that it might be useful to collect possible questions; then I thought it might also be a good thing to share them. Some might find the questions boring, but for some they might serve as a hint as to where they could expand their expertise. And for those that shake their head and claim that such intricacies are irrelevant in real life - well you might not be working in an environment demanding such deep knowledge right now. But with the current direction in C++ development, that might translate into "you might not be working" in ten years time.

A Collection of Questions

If you google for C++ related job interview questions, you will of course find lots of pages. The questions I have collected so far are a mix of questions I have been asked myself or that I have seen on the web; I have also provided a few of my own, although so far, I have not ever been in a position where I had to screen prospective new collegues. Still, it's what I would ask should the need ever arise.
I do not provide the answers here as well. If you don't know them, google them or better still read up on the language (a lot of knowledge can be gained pretty fast from the C++ FAQ Lite).

  • What is the difference between an object and a class?
  • What is an abstract class and when would you use the concept?
  • Which methods, if any, are automatically created by the compiler if you do not declare them?
  • Explain the differences between public, protected and private members/methods; when would you use which?
  • In respect to access modifiers, does the intention of being able to store objects in a std::map have any influence on the design of the class?
  • Is the constructor of a class allowed to throw exceptions? Explain your answer.
  • Is the destructor of a class allowed to throw exceptions? Explain your answer.
  • Can you call a virtual method from the constructor?
  • When must a destructor be declared virtual?
  • If you do not initialize them explicitly, how and to what are a) members of an integral datatype like integers initialized and b) members of a class type like Point m_point?
  • What is the correct way to initialize member fields?
  • Do the keywords this and self in C++ mean the same?
  • Is a friend declaration unidirectional or bidirectional?
  • If class A declares class B as its friend, can a class C that inherits from B access A's private members?
  • In C++, how do you dump a floating-point number to console output?
  • What is the difference between a shallow copy and a deep copy?
  • What is the correct way to delete an array a1 that you have created on the heap?
  • What is the difference between malloc/free and new/delete?
  • From an operating system point-of-view: why would one prefer not to create temporary instances of objects on the heap (with new/delete)?
  • In C++, classes are sometimes referred to as having "owning semantics" i.e. that they "own" a pointer to an instance. What is meant by "owning" in this context?
  • Explain the concepts "passing by reference" and "passing by value".
  • In C++, the concept of "passing by reference" can be achieved in two ways. What are they and what is the difference between the two?
  • What is the advantage of "passing by reference" over "passing by value"?
  • What is the difference between prefix and postfix operators, i.e. ++i vs. i++?
  • What is the difference between an inline declaration of a function and a macro definition of the same function?
  • What is the difference between an inline function and an ordinary function?
  • What is a namespace and why is it useful?
  • What is an iterator?
  • How would you check if a linked list contains a loop?
  • What are design patterns?
  • Can you name at least three design patterns you have used so far?
  • Two arrays with keys and values vs. a hashmap. What are the differences and when would you use which?
  • What differentiates a hashmap from a linked list?
  • In many header files you will find something like:
    #ifndef header_filename
    #define header_filename
    <header file code goes here>
    #endif

    What does this macro definition do and why would you use it?
  • In a resoource-restricted environment it is considered good practice to create objects on the heap only during the start-up phase and on the stack afterwards. Why?
  • Can you explain the concept of "string internment"?
  • Can you explain the idea behind the Pimpl (or "pointer to implementation" idiom?

Can you name the problems?

Another good way to gauge the level of knowledge is to present a printout of some code and then ask the candidate to point out all the problematic bits. I have once seen this employed right in a job advert, where the job applicant was asked to improve the code presented and to sent in the list of improvements together with his resume.
Such code examples do not need to come out of production code - you can easily make them up. Just look at the following implementation of class A:

class A {
  private:
    int id;
    B* m_pB;
    C* m_pC;
    D m_d;
  public:
    A() {
      this->m_pC = new C();
    }
    ~A() {
      delete(m_pB);
      delete(m_pC);
    }
  public:
    B GetB() {
      return *m_pB;
    }
    C* GetC() {
      return m_pC;
    }
    void SetB(B* a_pB) {
      this->m_pB = a_pB;
    }
    void SetC(C* a_pC) {
      this->m_pC = a_pC;
    }
    virtual std::string ToString() {
      std::stringstream s;
      s << this->id << ",";
      s << this->m_pB->ToString() << ",";
      s << this->m_pC->ToString();
      return s.str();
    }
};

What, if any, problems can you detect in the code? Please elaborate on each why this should be considered as a problem.

Just as an example what possible answers could look like:

  • Member field m_d is not used. This is, apart from the unnecessary memory consumption, harmless.
  • The default constructor does not initialize the member fields. That means that the id is undefined, as is the pointer m_pB.
  • The destructor calls delete on m_pB which is not guaranteed to point to anything. If that delete fails, the memory that is occupied by the instance of C that m_pC points to will never be freed, resulting in a memory leak.
  • GetB() returns the instance m_pB points to; if m_pB is null or does not point to an instance of B (remember, that m_pB is not initialized in the constructor) than GetB() will crash.
  • While GetB() returns an instance, GetC() returns a pointer. Is that intentional? As class A exhibits owning semantics regarding its pointers (the destructor is an indicator for that), exposing the pointer might cause problems at other places in the code once the instance of A from which the pointer to C has been obtained is destructed.
  • SetB() without further ado sets the member field m_pB to the value provided as in-parameter. However, if m_pB had pointed to a valid instance of B before being overwritten, this results in a memory leak because the memory occupied by that instance can now never be freed again, since there exists no pointer that points to it.
  • SetC() might exhibit the same problem would it not be for the getter that returns a pointer. This is highly problematic code as the ownership of the memory pointed to by m_pC is unclear. Whose responsibility is it to free the memory allocated for an instance of C by A's constructor?
  • ToString() is declared virtual which indicates that class A is intended to be inherited from. In this case the destructor should be virtual as well.
  • ToString() dereferences pointers without prior checking if these do indeed point to something. If either m_pB or m_pC is null (and both can be, their setters provide the interface) then the method call with crash.
  • No copy constructor and assignment operator have been declared. That means the compiler-generated ones will be used if necessary, which provide only shallow copies. That means that two instances of A can exist where m_pB and m_pC point to the same instances of B and C in memory. If one of the instances of A gets destructed, later calling ToString() on the other will lead to a crash.

In 30 minutes...

Another good way to measure the abilities of candidates is to give them a machine and a specification and then ask them to implement as much as possible (or all) of the specification.
This has two advantages - you can see how the candidate works whith a given deadline in mind, and you can see what quality the codes has the candidate produces under such a constraint.
One example of this is asking for the implementation of the assignment operator as in "The Anatomy of the Assignment Operator"; another would be the implementation of the Singleton Pattern for a given class (if you only want to spare a few minutes for a task assignment).
Another assignment that provides a lot of value for its money ist the implementation of a heap-based linked list. One that adheres to a given interface. Like the following example:
"Write a class that implements a heap-based linked list of CFoo instances that adheres to the following interface (fast execution is more important than memory consumption):

class IFooList {
  public:
    virtual void pushBack(const CFoo& foo) = 0;
    virtual CFoo* getElementAt(int index) = 0;
    virtual void removeAt(int index) = 0;
    virtual void clear() = 0;
    virtual int size() = 0;
};

Additional information: you may assume that constructing CFoo instances always succeeds. Exception handling is therefore not necessary."

Useful Off-Topic Questions

Not all questions need to be strictly code-related - two interesting questions following the end of the C++-related questions are:

  • Which software-development-related books have you read in the last three years?
  • Which blogs, forums etc do you regularly follow on the web?
What suitable answers are for these questions is up to you. But would you hire someone who would answer "none" to both?