Some typical questions in C++

advertisement
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
Download