The Ultimate Guide to Job Interview Questions
C++ Interview question
Interview Questions for Programming in the C++ Language
When it comes to interviewing candidates for a C++ programming position, it is important to remember that it is a very popular language with a long and proven history. Simple code syntax and common algorithms can be easily referenced online, and it would be a terrible mistake to discount a programmer in response to not knowing something that is commonplace or trivial. In this context, code-specific questions can sometimes be ineffective.
That said, however, it is critical that the candidate is able to recognize situations where errors are likely, or specific techniques are best applied. This demands a certain degree of skill and comfort with the intricacies of the language. Without this, project development time will increase along with the chances of errors occurring. These two viewpoints may seem to be at odds with each other, but a good set of interview questions can cover the subject from both perspectives.
This article includes 14 interview questions that vary in type and difficulty. It is important to remember that the final score can sometimes depend on how you structure interview questions. For example, code snippets or term definitions from one area may provide hints for something that is asked later. The first section contains 5 beginner interview questions and answers. These are intended to examine the candidate’s basic knowledge, and can be used to immediately rule out those who are wholly inexperienced with the language. The remaining questions are more difficult, and help to probe different areas within the interviewee’s knowledge and experience.
Aside from ensuring that the prospective programmer has an understanding of the essentials, such as basic data types, it is worthwhile to pose questions that test specific C++ programming constructs. It is not necessary for the programming interview questions to include a component where the candidate has to write a program, although certainly, familiarity with code must be assessed. For this purpose, tracing through a program will highlight the ability to spot errors quickly. Ultimately, this strength can save valuable time in the debugging stage.
Questions, Answers, What to Expect and Why
Each of the interview questions has several related points. In addition to the answer explanation, the rationale for each question is explained. This is important because questions asked during an interview should be not only relevant, but address specific topics. If the interviewer does not know what to look for, then the candidate’s programming knowledge may not be properly evaluated.
Check out the C++ question below. There are multiple ways to solve it, so the candidate’s choices can reveal how they code. How would you solve it? Click on the “Instructions” tab and try it out:
Beginner-level Interview Questions and Answers
1 – Consider the following program:
/* No programming interview would be complete without performing a code tracing exercise.
The int is one of the basic data types that can be manipulated by using the unary ++ operator. */
int main()
{
int a, b, c; // int data type for simple math
a = 9;
c = a + 1 + 1 * 0;
b = c++;
return 0; // return main int as Exit Status 0 (success)
}
What are the final values of the variables a, b, and c?
Rationale
This short test covers both simple operator precedence and basic knowledge about the unary “++” operator. Unary operators in C++ are common, and confusion sometimes arises depending on what side of the variable they are on (i.e. ++a versus a++). A misunderstanding here can lead to errors in algorithmic math, but more importantly it can lead to unexpected results while iterating through loops or accessing elements within an array.
Correct answer:
a = 9 (was not modified)
b = 10
c = 11
Common mistakes
c = 0 or c = 1
This will occur if there is an error in the evaluation of precedence. Because the multiplication operator (*) has a higher precedence than addition (+), and the order of operation is otherwise left to right, this equation translates to the following:
c = ((a + 1) + (1 * 0))
؞c = ((9 + 1) + 0)
؞c = 10 [Correct at this stage]
If the correct order of precedence is not followed then one possibility is:
c = a + 1 + 1 * 0
؞c = 9 + 1 + 1 * 0
؞c = 10 + 1 * 0
؞c = 11 * 0
؞c = 0 [Wrong]
To understand how somebody may incorrectly calculate that c = 1, consider what happens after the value for b is determined. To begin, b was assigned the value: c++
Whether the candidate knows that c = 10 at this point, or incorrectly believes that c = 0, it is a common mistake to apply the assignment operation (=) after the unary addition operation (++) is performed. The correct procedure for b = c++ is:
- b is assigned the value of c, leaving b = 10 [Correct]
- Then the value of c is incremented, resulting in c = 11 [Correct]
If the candidate mistakenly increments the value of c first, then:
- c is incremented, leaving c = 11 [Correct]
- b is assigned the value of c, making it also 11 [Wrong]
Other mistakes, such as incorrectly reasoning that the “++” operator is analogous to “+1”, are also common and will be ruled out by this question.
Red flags:
If the candidate has never used a unary operator before, or for whatever reason cannot trace the few lines of code, then it is unlikely they have any C++ experience.
2 – What are the differences between a class and a structure? For example, do they store data differently, and does a struct support member functions?
Rationale
Structures in C++ are not basic types, although they are very commonly used. In actuality, there are many programmers who do not understand the difference between these two types. Interestingly enough, the choice to use one over the other will rarely be of consequence. However, understanding that there is a difference, and what it is, can be important for debugging.
Another programming language, such as Java, will have more differences when comparing these two entities. Differences will exists for other types of storage class or data type as well. The answers given for this question may indicate what languages the programmer is most comfortable with. This would be made clear, for example, if the differences between Java structures and classes are listed.
Correct answer:
In C++, there is very little difference between classes and structures. Specifically, the default accessibility of member variables and methods are public in a structure, and private in a class. Nothing more. In practice, many programmers use the struct type as a storage class, only. This is perhaps a throwback to C, where structures did not support functions (or methods). A candidate who describes structures as unable to support member functions may be new to C++, or alternatively taken to develop primarily using the class paradigm.
Red flags:
Not knowing at least a little bit about both structures and classes would indicate a very poor understanding of C++. Even providing answers that are incorrect with respect to the differences, but do in fact include details about the two types, is positive.
3 – Definition of structures; consider the following two definitions for the class, “apple”:
enum colour = {Red, Green, Yellow}; // Normally in header file
struct apple
{
float weight;
colour c;
};
struct
{
float weight;
colour c;
} apple;
- What is the difference between these two definitions? Are they both legal?
- Which will work correctly in the following function?
void createApple()
{
apple myApple;
myApple.weight = 75.5;
myApple.c = Red;
}
Rationale
Good C++ programmers are familiar with structures, whether they use them regularly or not. There are variations in how they are defined, and a solid grasp with respect to these subtleties will help to ensure efficiency during development.
Correct answer:
- The first definition defines the type, only. It does not instantiate the structure. The second does not create a type at all. Rather, it instantiates “apple” for use in the current scope.
- The sample code requires an “apple” type, which means that the first definition is the correct one.
Red flags:
Not knowing the difference between these two snippets of code is not critical, as the issue is trivial and will be caught during compile time. However, it tends to suggest that the candidate does not have a great deal of experience with the language.
4 – Define the following terms that are commonly used in the language:
- Friend class
- Overloaded operator
- Static variable
- Protected variable
Rationale
C++ shares terminology with other languages, so it is important that interview questions probe the candidate’s knowledge with respect to areas for which there may be ambiguities or differences. A similar situation occurs when database developers migrate to other platforms. Both Oracle and MySQL support the structured query language (SQL), but there are dissimilarities that may cause delays in transitioning. This question will, at a minimum, indicate the prospect’s familiarity with the terminology.
Correct answer:
- A friend class is one that is allowed access to the private and protected members of any class that has declared it a friend. The property is not inherited (subclasses of friends do not become friends automatically), and not transitive (friends of friends are not friends).
Having an understanding of friend classes is valuable for both developing and debugging, and it would be a red flag if the candidate has never heard of it in the context of OO design. That said, the syntax and usage is straightforward, and can easily be referenced as long as the programmer knows that the technique exists.
- Overloaded operators are those for which the default functionality has been altered. This functionality allows for the customization of programming language operators such as the plug sign (+).
A trivial example is the use of a plus sign in string concatenation:
int: 1 + 1 = 2
string: “Amazon” + “ programming interview” = “Amazon programming interview”Certainly, any C++ programmer will be well familiar with overloaded operators. The approach is commonly used in custom storage classes for accessing and modifying data. The syntax for overloading a specific operator for a class is straightforward, so a demonstration of coding one without using a reference is not important. However, knowing that “+” can be used instead of writing an ugly “plus(…)” function is essential.
- Static variables are stored in static memory, sometimes referred to as the heap, rather than on the stack. It is important to know that these variables are only initialized once, and the scope persists throughout the entire execution.
A static variable inside a function will keep track of its value after the function ends, and remains unchanged with subsequent calls to the same function. It is similar to a global variable, but with the accessibility limited to the function.
All C++ programmers should know what the “static” keyword means, and how to declare this storage class. Static variables are not used in every application, but having never heard of them should be considered a red flag.
- When a variable or member function is defined as protected inside its class, it implies a type of restricted access that is similar to private. The difference is that it is accessible by its subclasses, or derived classes.
The keyword is quite common, and C++ programmers should be well aware of it. In very simply class hierarchies with no derived classes, clearly the functionality is not required. At the same time, it is an important specifier to consider when designing a class hierarchy.
5 – In an object oriented class hierarchy, what is multiple inheritance? In what circumstance will the Diamond problem arise?
Rationale
This question tests the candidate’s depth of knowledge with respect to the object oriented principles that are commonly used in C++. While the question is not specifically about the language, this programming interview covers multiple perspectives. As such, evaluating the familiarity with the paradigm is valuable.
Correct answer:
Multiple inheritance is not supported by all OOP languages. It is, however, supported in C++. It describes a situation where an object or class inherits features, such as member functions or variables, from more than one parent class. Compare this to single inheritance, where an object or class is restricted to inheriting from a single parent.
While this functionality is powerful, it can lead to the Diamond problem.
Consider the top-level class: “A”, which has two sub-classes “B1” and “B2”. Each of the child classes singly inherits from the parent class, “A”. It is when a third level, “C”, is added that the problem may arise. It can occur only if sub-class “C” inherits from both “B1” and “B2”.
Furthermore, it will only occur if there is a method defined in “A” that is overridden in both “B1” and “B2”, but is not overridden in “C”. If this method is invoked by “C”, then there is ambiguity concerning which method to use.
It is not important to know how this problem is solved in C++. Rather, one should recognize that the problem exists and should be considered when designing a class hierarchy.
Red flags:
If the candidate does not know what multiple inheritance is, then it suggests a lack of objected oriented design experience. While not critical for all applications, OOP is an integral part of C++.
6 – Recursion is a common technique used in C++ programming. What are the advantages and disadvantages of using recursion? Provide an example for which recursion is well suited.
Rationale
Recursion is an oft-used technique by programmers that helps to simplify coding. Whether programming from a clean slate, adding functionality, or maintaining an existing code base, a developer is virtually guaranteed to see or use recursion. There are specific things to consider when developing or debugging a recursive function, so familiarity with the technique is important.
Correct answer:
The simplest definition is that a recursive function is one that calls itself.
The main advantage is that it leads to simpler, more elegant code. This in turn is easier to develop and, at least theoretically, leads to fewer errors.
The main disadvantage is that recursion is often slower in practice compared to its iterative counterpart. This performance hit is not attributable to complexity, but rather, a recursive function uses more memory. Each recursive function call reserves additional space on the stack, causing it to grow. If the recursion is too “deep”, meaning that it goes for too long before returning, it can result in a “stack overflow” error.
Examples of well suited situations
Tree structures, such as a binary search tree, are very well suited to recursion. Similarly, when there is a variable number (or a very large number) of nested loops, the recursive version will be much more elegant. Sorting algorithms such as Merge sort, Quick sort, and Bubble sort are all easier to implement recursively.
Red flags:
Recursion is a technique that is covered early in a programmer’s education. The candidate should certainly be able to describe it, and also know that it consumes more memory (stack space) than the iterative version of the same algorithm. Also, knowing that it is well suited for tree structures is trivial.
Bonus
The way to avoid excess memory consumption and the stack overflow error is to use tail-recursion (also known as tail-recursion optimization), as long as it is supported by the compiler. Knowing that this exists, without describing how it works or how to code for it, should still be considered a plus. Obviously, an in-depth knowledge of tail-recursion subsumes the purpose of the original question.
7 – Smart pointers were introduced in C++11 to assist with memory management and prevent memory leaks. The following questions address the allocation of memory for data used within the program, irrespective of type.
- Briefly describe the difference between:
unique_ptr<>
shared_ptr<>
weak_ptr<> - In the following code, what is the best choice of smart pointer (replace ??? with the correct choice), and why? What happens to the memory reserved for “p” when the function exits?
void printModifiedArray(int *nums, int length) { // Allocate memory for a new array used only in this function std::???_ptr p(new int[length]); // Copy the int data with the proper size of data type std::memcpy(p.get(), nums, sizeof(int)*length); // Iterate through the elements in the array for (int a = 0; a < length; a++) { // Call the destructive shiftInt() function shiftInt(p[a]); // Output the modified numbers from the array std::cout << p[a] << " "; } // Conclude with a newline escape sequence std::cout << endl; }
Rationale
Memory allocation and pointer errors are traditionally some of the most difficult problems in C++ to detect and repair. A large number of independently developed memory management libraries assisted developers for many years. With the release of the C++11 standard, the introduction of Smart pointers went a long way towards fixing these long-standing and difficult problems. Developers at every level should be proficient in their use.
Correct answer:
- A unique pointer (unique_ptr) is a smart pointer that will automatically deallocate the reserved memory as soon as it is out of scope. Only one unique_ptr can point to a resource, so it is illegal (at compile time) to make a copy of a unique_ptr.
- A shared pointer (shared_ptr) is also a smart pointer that automatically deallocates the reserved memory once it is out of scope. However, a single resource can be simultaneously referenced by many shared pointers. An internal reference counter is used to keep track of the shared_ptr count for any given resource. If there are no references then the memory is freed. This is susceptible to the problem of circular dependency in cases where two or more shared pointers reference each other.
- A weak pointer (weak_ptr) indeed references the memory, but is not able to use it before being converted into a shared pointer. This is done by using the lock() member function. Using weak pointers is a common way to deal with the problem of circular dependencies. There is no reference count, but as such, the resource is first verified as soon as lock() is called. If the memory is still available then it is usable as a shared pointer. However, had it been deallocated previously then the lock() would fail.
In the code snippet, the best choice is the unique_ptr. Certainly, a shared_ptr will work, but having to keep track of references when the function does not call for a shared resource is inefficient. C++ developers have been known to use exclusively shared pointers, although this is frowned upon due to the accompanying performance degradation. A suggestion to use the weak_ptr would indicate that the candidate has no experience with smart pointers.
When the function returns, if the memory reserved for p is held by a unique_ptr or a shared_ptr then it will be automatically deallocated.
Red flags:
If the candidate has no knowledge of smart pointers or how to use them, it indicates a lack of experience in an important part of the programming language. Smart pointers were introduced in C++11, and although a veteran C/C++ developer can get by without them, the benefit that comes with their use is not something that should be ignored.
8 – The explicit type conversion of a variable, also know as casting, is an operation that is used often in C++. There are several functions, each with its own properties.
- Under what circumstances should dynamic_cast be used?
- Why is reinterpret_cast considered to be dangerous?
- In the following code, what type of cast, if any, would you choose to make the password variable writable?
int main()
{
// Password to access file data
const char password[16] = { “Dennis ritchie”}; // Data annot be changed
char *changePass;
// Cast to make it modifiable
??????
// Change the password to decrypt the sequential access file
changePass[7] = ‘R’;
void *address = static_cast<void*>(changePass);
// Open access file using the password and display the contents
return openAccessFile(password);
- Is it possible for the contents of password and changePass to now be: “Dennis Ritchie”?
- Is the definition and assignment of the address variable legal? What memory location will it contain?
- Which of the casts, if any, perform runtime type checking?
Rationale
Explicit type conversion between variables and objects has always been a part of C++, and it will continue into the future. A firm understanding of typecasting is essential, and just as importantly, the newer styles of cast should be utilized instead of the original C-style, parenthesized casting syntax.
Correct answer:
- A dynamic_cast is used only within the context of a class hierarchy. It can be used to cast the pointer of a class into one of its subclasses. It is also capable of casting class references in the same way.
- It is considered dangerous to use reinterpret_cast because the compiler makes the assumption that the programmer knows exactly what to expect. There is no compile-time or run-time validity check performed. This is similar to the original C-style casting.
- This code is a trick question, because technically the behaviour is undefined.
To be clear, if the answer is:
changePass = const_cast<char*>(password);Then some compilers will allow it, and the execution will succeed. So, the candidate should receive credit for a correct syntax, as well as choosing the “const_cast”. However, if the candidate does not recognize that modifying a variable that was originally defined as “const” is undefined behaviour then this should be penalized.
- A dynamic_cast performs a type safety check at runtime to validate the operation. If the pointers or references are not of the same type (as allowed by this cast) then the cast fails.
Red flags:
If the prospective programmer is not familiar with casting then the amount of actual C++ language programming experience comes into question. Less gravely is the case where the interviewee is only familiar with C-style casting. In this situation, it would not take long to get up to speed with the proper use and syntax of modern day casting functions.
9 – In addition to the array functionality built into the language, the STL provides for a number of container classes. Two of these are List (std::list) and Vector (std::vector). What are the differences between these two container types, and in what circumstances would you choose one over the other?
Rationale
During a programming interview, it is worthwhile to question the candidate about the use of library functions, as well as higher-level constructs such as pre-built container classes. When dealing with a dynamic data structure, it is important to recognize that different choices are better suited for certain applications. Having experience with these data types will help to choose the best approach, and it will save time in the development stage.
Correct answer:
The vector and list classes are both sequentially stored containers, but the internal implementation is significantly different. The vector is stored in contiguous memory locations like an array. A list, on the other hand, is stored internally as a doubly linked list. Both of these structures have their own caveats.
Vector
- Insertion and deletion is expensive because:
- If either occurs in the middle off the list then all of the elements have to be shifted, either to make room or to clear unused space.
- If an element is added to the end then it may require re-allocation of memory and the copying of all of the elements.
- C++ Iterators can be invalidated.
- Random access is both possible and efficient, using the array operator (eg: tasks[12]).
- Other STL algorithms that require Random Access Iterators can safely be used with the vector class. An example of this is std::sort(…), which on a vector can be called: std::sort(myVector.begin(), myVector.end());
List
- Insertion or deletion at any point (start, middle, end) only requires changing a few pointers. During insertion, of course, new memory will have to be allocated for the incoming element.
- Random access is not possible in a list. In order to access the tasks in the previous example, tasks [12], it requires starting at the beginning of the list and iterating through the first 11 elements one at a time.
- Iterators are not invalidated by list insertion or deleting because no element is moved from its position.
- Random access iterators are by default invalid for lists. Consequently, custom functions using non-random access iterators need to be implemented. Due to how commonly lists are sorted, the functionality is part of the STL:
std::sort(..) does not work for lists; However, std::list::sort() is part of the STL
Red flags:
If the candidate has never heard of std::list or std::vector, then they may be unaware of the existence of the standard template library. While many applications are written without it, certainly, it is a useful and proven library that assists in a variety of applications. Debugging or maintaining an existing code base will almost certainly demand a more broad knowledge of the language and standard libraries.
10 – Prior to C++11, the “auto” keyword was a storage class specifier used to declare that a variable should have an automatic duration. What is it used for in the modern C++ programming language? Provide a simple example for a basic data type.
Rationale
The use of “auto” to declare a variable type simplifies coding, speeds development time, and leads to fewer bugs. Its use should be natural for modern day C++ programmers.
Correct answer:
Simply put, the “auto” keyword is used to have the compiler determine the type of variable.
This storage class specifier was so rarely used that the keyword was given this new functionality starting in C++11.
Examples
auto simple = 5.5; // Simple will be a double
auto count = 2; // Count will be an integer
Bonus
Knowing that as of C++14, the auto keyword can be used to declare a return type.
auto testFunction()
{
return 5; // The return value will be an integer
}
Telling (and perhaps very bad)
Not knowing that the “auto” keyword was replaced is usually a bad thing. The candidate may have extensive knowledge of C++, but not be fully up to date with respect to C++11/14. This should be considered a disadvantage, as the language has been extended significantly since C++98 and C++03.
11 – A lambda function is a miniature program that is defined within a standard function. As written, the lambda function inside this program will accept two int parameters and output another int as an answer.
int getSum(int a, int b)
{
int bias = 3;
int sum;
auto lf = [](int x, int y) { return x + y; };
// Calculate the sum using the lambda function
sum = lf(a, b);
return(sum);
}
- What modifications can be made to the lambda function so that it has access to the local variable, bias, and it is able to return “x + y + bias” instead of “x + y”?
- Within the [square brackets], what is the difference between using the equal sign [=] and the ampersand [&]?
Correct answer:
- auto lf = [=](int x, int y) { return x + y; };
- auto lf = [&](int x, int y) { return x + y; };
- auto lf = [bias](int x, int y) { return x + y; };
- auto lf = [&bias](int x, int y) { return x + y; };
- Specifying the equal sign will capture all of the variables by value, while the ampersand will captures the values by reference.
Red flags:
Lambda functions are becoming more commonplace, and C++ developers need to be aware of how they are used. Having no experience with the functionality suggests a limited knowledge of the language.
12 – In the context of parameters for function calls, compare call by reference to call by value. Which one represents a pointer to the data?
- What is the difference between call by reference and call by value? Which one has the potential for side effects?
- What is syntax used to distinguish them?
Rationale
Functions are the backbone of C++, and they are instructed by their parameters. Knowing the difference between passing parameters by value, or alternatively by reference, is a key part of the language. Both for development and debugging, this is absolutely essential to understand.
Correct answer:
- Call by value works by passing a copy of the value into the function. Changes made inside of the function do not affect the value that was used to specify the parameter. This is the default method used by the language in parameters for function calls.
void myValueFunction(int value_1, float value_2);
- Call by reference works by passing the reference into the function. The reference behaves in the way that a dereferenced pointer would, and as such, changes made inside the function are reflected in the calling function (or scope). This leaves the possibility for side effects, so care should be taken when passing parameters by reference.
void myReferenceFunction(int &reference_1, float &reference_2);
Red flags:
A candidate who is unfamiliar with the difference between these two concepts has had very little, if any, C++ development experience.
13 – What is a class template? Comment on how using a template promotes code reuse.
Rationale
Class templates are an important functionality used for generalization, abstraction, and code reuse in C++. Having a good understanding of class templates is beneficial at all stages of development, including making use of third party libraries.
Correct answer:
A class template allows classes to be abstract in the sense that they do not know what datatype will be passed in and handled by its operations. A template does not depend on the characteristics of a particular datatype; rather, the focus is on the logic.
The std:vector template class is an example provided by the STL. It is generic because a vector of any datatype can be instantiated. This ability allows for efficient code reuse, and consequently the entire system is easier to implement and maintain.
Red flags:
If the candidate has no idea of what a class template is then it indicates a lack of experience in C++. There are plenty of applications that do not make use of templates. However, their use is not uncommon. In some cases, candidates will have used templates such as std::vector and have been unaware that they were making use of one.
14 – C++ contains the functionality for returning an Rvalue from a called function. For example, it may contain a pointer to a string that was read from a file. In this context, what are the advantages of returning an Rvalue?
Rationale
Rvalue “Right value” references were added to the language in C++11, and they are a useful tool for improving performance. Unfortunately, there is often misunderstanding about what their purpose is and how they are used. As a result, they are underused in some projects.
Correct answer:
An rvalue solves two problems: move semantics and perfect forwarding. The problem being addressed in this question is move semantics, where data should be “moved” between functions, rather than copied.
In move semantics, the called function has memory reserved for a resource that will be returned to the calling function. Returning an rvalue allows the calling function to “swap” the data between functions, by way of reference. In the alternative, the above mentioned string will be copied from the called function’s scope to the calling function’s scope. This requires the allocation of memory by the calling function, a copy procedure, and the subsequent deallocation of memory in the called function. Clearly, this is a less efficient process. Therefore, using an rvalue is faster and more efficient.
Red flags:
Candidates that have not heard of rvalues will create less efficient code in some cases. It may be an indication that they are not fully up to date with the additional functionality provided by C++11.
Conclusion
The questions provided here are intended to evaluate the competency of a prospective C++ programmer. It is important to remember that the answers do not have to be perfect. Rather, when a candidate demonstrates an understanding of the topic, but perhaps is lacking knowledge about specific details, it should be considered a credit.
At the same time, candidates need to be qualified and their skills assessed. What skills they need will depend on the job at hand, and as such, the variation provided by these interview questions will help to make that assessment. Don’t forget that one of the best ways to evaluate the candidate’s skills for your project is to perform a hands-on programming assessment.