Some typical questions in C++ that often appear in interview sessions. This ought to guide us in dealing with C++ problems and the worlds they describe. Good luck. Q1) An array is instantiated with the new[] operator. Is it sufficient to delete the array using a delete operator or should a delete[] operator be used? Justify your answer. A1) The delete[] operator should always be used. This restriction applies for both built-in types (char, int and others) and for aggregate types (structs and classes). This is required by the C++ language definition (the Annotated C++ Reference Manual). Q2) Can I drop the [] when deleting an array of some built-in type (char, int, etc)? A2) No. Marshall Cline's C++ FAQ Lite contains a good explanation at parashift.com. Q3) What is an example of when a destructor is NOT called at the end of scope? A3) Bjarne Stroustrup's C++ Style and Technique FAQ provides an excellent explanation at att.com. The short answer is that objects that are created on the heap are not automatically destroyed at the end of the scope that created the object. In addition to Bjarne Stroustrup's answer, I would also add that statically created objects are also not destroyed at the end of the scope. Q4) Explain stack unwinding. A4) Stack unwinding occurs when an exception is thrown and control passes from a try block to a handler. Automatic objects are destroyed in the reverse order of their construction. If a destructor throws an exception during the stack unwinding process, terminate is called. From the Borland C++ Builder help: When an exception is thrown, the runtime library takes the thrown object, gets the type of the object, and looks upward in the call stack for a handler whose type matches the type of the thrown object. Once a handler is found, the RTL unwinds the stack to the point of the handler, and executes the handler. In the unwind process, the RTL calls destructors for all local objects in the stack frames between where the exception was thrown and where it is caught. If a destructor causes an exception to be raised during stack unwinding and does not handle it, terminate is called. Destructors are called by default, but you can switch off the default by using the -xd compiler option. Objects that are not allocated on the stack, such as heap allocations, are not automatically released. This can cause memory leaks unless the programmer takes extra precautions to release the allocated memory when an exception is thrown. There are various ways to prevent heap-memory leaks caused by stack unwinding. One way is a user-defined garbagecollection; a second way is to specifically deallocate those resources in the exception handler. Q5) When I write a derived class's destructor, do I need to explicitly call the destructor for my base class? A5) Marshall Cline's C++ FAQ Lite answers this question with "No". A more explicit explanation with an example is at parashift.com. Q6) Explain the difference between a class and an object. A6) A class is a blueprint for an object. It defines how the object will be created, what data is stored, how the data can be manipulated and how the object will be destroyed. An object is an instantiation of a class. There can be multiple objects instantiated from one class. Every object has one and only one class that it was instantiated from. Q7) Explain the difference between a struct and a class. A7) The default members and base classes of a class are private. The default members and base classes of a struct are public. Other than the default protection, struct and class are equivalent. An unwritten practice amongst C++ programmers is to define a class for objects that have few or no public data members and to define a struct for objects that have few or no public methods. Q8) What is difference between malloc()/free() and new/delete? A8) malloc() and new both allocate space from the heap. free() and delete both release previously allocated heap space. free() should only be used with malloc'd allocations and delete should only be used with new allocations. There are two varieties of new: array allocation through a new[] operator and single object allocation through a new operator. It's the programmer's responsibility to know and track which allocation method was used in order to apply the correct deallocation: free(), delete or array delete (operator delete[]). Q. Provide the definitions of each of these words or phrases. For the words that are C++ keywords, describe all of the ways that the keyword could be used. What is polymorphism? What is a virtual keyword used for? What is the mutable keyword used for? What is the explicit keyword used for? What is template metaprogramming? What do the public, private and protected keywords mean? What is the static keyword used for? What is an assignment operator? What is a dangling pointer? What is a functor? 1) Polymorphism is the ability of a pointer to a derived object to be type-compatible with a pointer to its base class. If the interviewer's follow-up question is, "Huh?", you can answer: Polymorphism allows two or more classes derived from the same base class to share a common base member function but have different behaviors. Moving to a whiteboard, you can whip up the sample program in Listing A. It uses a pure virtual base member but that's not a requirement. A regular virtual base member would also have been acceptable. #include <iostream> using namespace std; class Vehicle { public: virtual string getFuel() = 0; }; class Car : public Vehicle { public: virtual string getFuel() { return "gasoline"; } }; class Truck : public Vehicle { public: virtual string getFuel() { return "diesel"; } }; void printFuel(Vehicle &v) { cout << v.getFuel() << endl; } int main(int, char **) { Car car; Truck truck; printFuel(car); printFuel(truck); return 0; } Listing A: Example of Polymorphism In Listing A, polymorphism occurs with the Vehicle parameter for the printFuel() function. printFuel() can accept either an instantiation of Car or an instantiation of Truck. A follow-up question might ask if a Vehicle object could be passed to the printFuel() function. The answer is "no" because Vehicle uses a pure-virtual function and classes with pure-virtual functions can not be instantiated. 2) virtual is a C++ keyword that is used for virtual methods and for virtual base classes. 3) mutable is a storage-class specifier. It allows const-member functions to modify the data member. 4) explicit is used on constructors that have one parameter. It prevents automatic type conversion from changing a candidate parameter into the type used in the constructor. 5) template metaprogramming is an idiom that uses templates to generate source code that calculates an answer at compile-time rather than at run-time. Follow-up questions would ask about the Curiously Recurring Template Pattern, the BartonNackman trick, static polymorphism in template metaprogramming and the benefits and drawbacks of template metaprogramming. See wikipedia.org for a good explanation along with cross reference material. 6) public, private and protected are the access control specifiers in class and struct designs. All three of the keywords are used to control the access to methods and data in base classes and in derived classes. Listing A shows an example of a Car class defining public access to a Vehicle class. The Vehicle class defines public access of the getFuel() pure virtual method. 7) The static keyword is used all over the place in the C++ language. When used inside of a method or function, the static keyword preserves the last value of a variable between method or function calls. Inside of a class definition, a data value can be declared static -this causes one version of the data to be shared amongst all of the objects of the class. Static member functions can not be virtual because they have external linkage. External linkage means that the function does not have a this pointer and the function can only call other static member functions and access static data. 8) An assignment operator is a simple equal (=) sign for built-in types or the operator=() method for objects. If your first answer only provided one of those assignment operators, a good interviewer would ask something like "is that all?". Written tests can't do that, of course, so be careful when giving what seems like an obvious answer. 9) A dangling pointer can be an unassigned pointer or it can be a pointer to an object that has been destroyed. More Questions: 1. What executes faster: ++i or i++, where i is an integer variable? 2. Which is generally more efficient: a function that returns a value by reference or a function that returns a value without using a reference? 3. Describe inline functions and their limitations? 4. Why use the STL sort() when we have "good old qsort()"? 5. One programmer wants to sort a linked list using recursion; another programmer wants to do it without recursion. Which one has the better answer? 6. When is an interface "good"? Q1) What executes faster: ++i or i++, where i is an integer variable? A1) ++i probably executes faster because i++ would first fetch the value of i, push it on the stack, increment it and then pop the value of i from the stack. In contrast, ++i fetches the value of i, and then increments it. Yes, I'm leaving a lot of detail out and the push/pop cycle can be optimized away for many situations if the optimizer is good enough. Q2) Which is generally more efficient: a function that returns a reference or a function that does not return a reference? A2) Functions that don't return a reference are returning the object by value. This is accomplished by using the object's copy constructor. For non-trivial objects, the time and resource cost of copying the object can be easily measured. Functions that return an object by reference only return a pointer to the object. The object the reference points to is not copied. Q3) What's the deal with inline functions? A3) The keyword inline is a hint to the compiler that the function should be compiled into the code wherever it is used. This is normally done with a simple function that is called often, such as a getter or setter in a class or struct. The compiler implementer is allowed to ignore the inline hint and substitute a function call instead. Some compilers automatically disable the inlining of functions while debugging. Q4) Why use the STL sort() when we have "good old qsort()"? A4) The following answer is quoted from public.research.att.com: To a novice, qsort(array,asize,sizeof(elem),compare); looks pretty weird, and is harder to understand than sort(vec.begin(),vec.end()); To an expert, the fact that sort() tends to be faster than qsort() for the same elements and the same comparison criteria is often significant. Also, sort() is generic, so that it can be used for any reasonable combination of container type, element type, and comparison criterion. For example: class Array { public: int size() const; float& operator[] (int index); ... }; int main() { Array a; for (int i = 0; i < a.size(); ++i) a[i] = 7; // invoke Array::operator[](int) ... } In addition, most people appreciate that sort() is type safe, that no casts are required to use it, and that they don't have to write a compare() function for standard types. The primary reason that sort() tends to outperform qsort() is that the comparison inlines better. Q5) One programmer wants to sort a linked list using recursion; another programmer wants to do it without recursion. Which one has the better answer? A5) It depends on the maximum number of items in the linked list. An implementation that uses recursion will eventually run out of stack space if the linked list is long enough. If the number of items in the linked list is relatively small, say around 30 or so, then either solution might be faster but both implementations will be so fast that it's usually not worth optimizing that one little routine. If the number of items in the list could ever be more than 30, then a non-recursive solution would probably be better because it will not run out of stack space when compared with the recursive solution. Q6) When is an interface "good"? A6) From parashift.com: When it provides a simplified view of a chunk of software, and it is expressed in the vocabulary of a user (where a "chunk" is normally a class or a tight group of classes, and a "user" is another developer rather than the ultimate customer). The "simplified view" means unnecessary details are intentionally hidden. This reduces the user's defect-rate. The "vocabulary of users" means users don't need to learn a new set of words and concepts. This reduces the user's learning curve. More questions 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. What language feature is available when a function returns a reference? What is name mangling in C++? What is overriding? Can you assign to a reference? What happens if you assign to a reference? What is a static_cast and when should it be used? What are the names of the other cast operators? How do you trap an exception without knowing what exception might be thrown? Describe how you design a program. Most programming projects involve modifying existing code. What things do you look for when you're tasked with modifying an existing program? There is a memory leak in a program. What tools and techniques would you use to isolate, find and fix the memory leak? What's the difference between a linked list and an array? What is the difference between a vector, a list and a map? What is a namespace? What is an anonymous namespace and how is it different than a regular namespace? Q1) What language feature is available when a function returns a reference? A1) The C++ FAQ-lite at parashift.com answers this question: The function call can appear on the left hand side of an assignment operator. This ability may seem strange at first. For example, no one thinks the expression f() = 7 makes sense. Yet, if a is an object of class Array, most people think that a[i] = 7 makes sense even though a[i] is really just a function call in disguise (it calls Array::operator[](int), which is the subscript operator for class Array). class Array { public: int size() const; float& operator[] (int index); ... }; int main() { Array a; for (int i = 0; i < a.size(); ++i) a[i] = 7; // invoke Array::operator[](int) ... } Q2) What is name mangling in C++? A2) Name mangling encodes the type of the function's parameters into the name in order to create a unique name that is distinguished from identical function names with different parameter types. For example, the parameter int *p might be mangled to "intPtr". A function prototype defined as: doit(int *p); could name mangle the doit function into doit_intPtr. Name mangling is compiler specific. The primary reason why object libraries from one compiler can not be linked with object libraries from another compiler is because each compiler vendor uses a slightly different name mangling scheme. Q3) What is overriding? A3) Overriding only occurs in a class hierarchy. An overridden method is one that is hidden from the normal method call hierarchy by a derived class that has a method with the same name, return type and parameter list. When a method is overridden by a subclass's method, the subclass's method is called instead of the parent class's method. Q4) Can you assign to a reference? A4) Yes, you can. Here's an example: int i; int &j = i; j = 5; // changes i to 5 Q5) What happens if you assign to a reference? A5) From parasoft.com: You change the state of the referent (the referent is the object to which the reference refers). Remember: the reference is the referent, so changing the reference changes the state of the referent. In compiler writer lingo, a reference is an "lvalue" (something that can appear on the left hand side of an assignment operator). Q6) What is a static_cast and when should it be used? A6) A static_cast<> is the general replacement for the old-style (cast). A static_cast<> is safer than a (cast) because static_cast<> verifies at compile-time that the conversion makes sense. Q7) What are the names of the other cast operators? A7) const_cast, reinterpret_cast and dynamic_cast. Q8) How do you trap an exception without knowing what exception might be thrown? A8) Use the ... wildcard in the exception handler. Q9) Describe how you design a program. A9) Any reasonable answer that you can justify will work for this question. The better answers will include the phrases "top-down", and "bottom-up". Other phrases that could be used are "divide and conquer", "requirements gathering", budgeting, planning, and testing. A poor answer is along the lines of, "I just sit down and start coding because I get something done right away". Most employers want to know that you spend time designing the program, that you sit at a blank piece of paper or a whiteboard and sketch out even a rough skeleton of how the program will work. Programmers that design programs using extensive unit testing get extra credit points. If you're an advocate of extreme programming (XP) (www.extremeprogramming.org/), this would be a good time to talk about your experiences with XP and how it makes you a more efficient and better software engineer. Q10) Most programming projects involve modifying existing code. What things do you look for when you're tasked with modifying an existing program? A10) The first thing I do when I take over a legacy project is to attempt to compile the project in an isolated environment. I record the compile-time warnings I receive and I pay particular attention to any modules that compile with errors because that's an indication that I have an incomplete or inaccurate development environment. Next, I make a binary comparison between the legacy executable and the newly compiled executable. My goal is to create an executable that is identical to the legacy executable -- at that point I know I have the same environment and settings that the previous developers used. The next thing I usually look at is any existing documentation. Although many C and C++ programming projects are notorious for having little or no documentation (or even inaccurate documentation!), I have seen projects that had extensive, amazing and accurate documentation -- even to the point where the number of lines of useful comments exceeded the number of lines of code. Q11) There is a memory leak in a program. What tools and techniques would you use to isolate, find and fix it? A11) I would first determine how big the memory leak is and look for patterns of when it appears and how quickly it grows. If the memory leak is small or grows slowly and the program terminates soon anyway, then I would say that the problem isn't worth fixing. This isn't out of laziness -- it's a prudent decision that recognizes that some things are too expensive to fix. If a leak was determined to be worthy of fixing, I would next look for the smallest set of code that reproduces the problem. Can the problem be reproduced by launching and then immediately exiting the program? What's the simplest thing I can do that causes the biggest memory leak quickly? I would also realize that there may be multiple memory leaks in the program and they might be interacting with each other in strange ways. An example would be a hash table that leaks whenever the hash table's contents are rehashed. On a Windows system, the primary tool I use to monitor memory usage is the Windows Task Manager. A more aggressive approach is to instrument the malloc/free and new/delete methods to provide a log of memory consumption and releases. Commercial tools are also available to assist in the detection and eradication of memory leaks. If you've used one before that you've liked, mention it in the interview to earn extra points. Q12) What's the difference between a linked list and an array? A12) An array allows random access to the array's elements. A linked list's elements can accessed by dereferencing the head (or tail) pointer and then dereferencing the next (or previous) pointers. Inserting an element into an array is more difficult than inserting an element into a list. Deleting is similarly more difficult in an array than in a list. Data is stored sequentially in an array but that is not a requirement for a list. Lists' data can be stored either sequentially or randomly. Q13) What is the difference between a vector, a list and a map? A13) All three of them are containers in the STL. Maps use key/value pairs for the elements. The keys are sorted within the map to allow quick random access. Insertion and deletion are both very quick. Access to any particular element is also quick but each element requires a unique key. STL vectors and STL lists behave the same as arrays and lists (see Q12). http://www.decompile.com/interview/C%2B%2B_Interview_Answers_Page_07.htm