C++ is a high level language and when we are preparing for our interviews we mainly focus only on the data structures and algorithms but apart from that there are a variety of built-in functions and classes which are predefined in the standard C language.
So many people code in C++ and make it their primary language but most of them don’t know concepts required for C++ interview rounds! Companies like Microsoft, Adobe, Intuit, Oracle, Tower Research Capital and many others have these specific interview rounds.
Personally, when preparing for my Intuit job interview, I had a friend who really helped me get ready for the rigorous process. And so it made perfect sense to bring him in for a mock C++ interview to help all of you with the same.
Amit is a working professional who has been in the industry for more than 9 years and has previously worked in big companies like Adobe and Oracle. In this interview, I will be interviewing him, covering a lot of very common C++ questions that I’ve personally faced in some interview or the other.
Let’s dive right in!
Interviewer[Keerti]: Amit, I’d like for you to implement a String Class.
Amit: Okay, well as you know Keerti, in standard C++ implementation, the String class has a lot of functionalities in it. I’d like to demonstrate some of them, and you can let me know if you’d like to see more functionalities. Let me start by describing what functionalities I’ll be implementing. Is that alright?
Interviewer: Sure.
Amit: So I’ll start by writing a simple class template for String. I’ll be using capitalized ‘String’ here to differentiate from the standard implementation of a string, which is in lowercase ‘string’. And this is what a class looks like.
Class String
{
}
Interviewer: Amit, before you get started with the implementation, could you please walk me through what the functionalities will be from a user’s perspective?
Amit: Yeah, that’s what I’ll write here first. So what we can do is, in addition to writing the String class, we can also write the main() function and see how a user will use and implement this class. As for the functionalities, some users can just instantiate with the empty string, using the default constructor. That’s how we’ve initialized str1 here. What I’ll do Keerti, is I’ll write down all the functionalities I plan to implement, and using comments I’ll show you how we are going to define each functionality in the class.
When you are asked to implement an existing C++ library code, it is good to write down the existing functionalities and understand the expectations of the interviewer.
Amit: Secondly, a user could use the ‘equal-to’ operator and initialize the string, say str2, with some kind of a constant string like “hello”. This will be implemented through a parameterised constructor.
I know the question so far can be confusing for some people but it is an extremely common question. Amit is basically writing a new string class. If you feel confused, have a little bit of patience – he will do a brilliant explaining everything in detail.
Amit: The third way a user can initialize the string, say str3, is by using another string that has already been declared or defined. And this will be done by what we call a copy constructor.
Interviewer: Okay.
Amit: One more functionality the user will have is to assign a new string value to a string that has already been defined. For instance, the user could assign str2 to str3. And this will basically be implemented by something called the copy assignment operator.
Amit: Another feature the user might want is to find the length of a string, so let’s have a length() function.
Notice how structured Amit’s approach is. He is first thinking from the user’s point of view and then thinking of how he will implement that in his class. He is also writing it down for clarity and future reference as well.
Interviewer: I also want you to take input from the user and output strings.
Amit: So you want someone to be able to output the class object on screen? Like printing str2 to screen, maybe.
I would recommend that you keep thinking of cases from the user’s point of view and think about how you would have implemented in the class. Would you have followed a similar approach?
Interviewer: Yes, and be able to take input as well.
Amit: Okay, so the user can also input a string using the cin function. So for these functionalities, we have to overload the input and output operators. What else would you like the user to use our String class for?
Interviewer: I also want to be able to initialize it by saying something like str3(str1).
Amit:: Okay, so what you’d like is to be able to do something like this:
So Keerti, this functionality will be taken care of by the copy constructor itself, like we saw earlier. We won’t have to do anything special for that.
Interviewer: Alright.
Amit: Apart from all these, do you want to implement any other functionality? There are N number of functionalities in the string class. Should we add any more?
int main()
{
String str1; // default constructor
String str2 = "hello"; // parameterised constructor
String str3 = str1; // copy constructor
str3 = str2; // copy assignment operator
int len = str3.length(); // return the length of the string
cout << str2; // overloading << operator
cin >> str1; // overloading >> operator
return 0;
}
Interviewer: So let’s focus on implementing these first since we are having a time constraint of 45 mins. If there’s time left, we’ll consider extending the implementation.
Amit: Okay, so let’s start with the default constructor. We’ll create the public and the private areas of the class, and we’ll define the member variables – a buffer we use to store whatever resource is passed to the string (res), and a variable to store the length of our string (len). Since the length will never be less than 0, we will use an unsigned int to save memory.
And now, let’s implement the default constructor. There are 2 ways we can do this but in C++, it’s better to use the initializer list. Since we don’t have any parameters at the moment, we can initialize the variables with default values.
Interviewer: What is initializer list and why do we need it here?
Amit: As the name suggests, the initializer list is used to initialize the members of the class. In this particular case it won’t make a difference, and we can in fact go ahead and initialize our variables in the body of the constructor. But in some cases, we cannot simply initialize the members inside the function and have to use initializer list.
class String
{
public:
// default constructor
String() : res(nullptr), len(0)
{
// will take care of String str1;
}
private:
char *res; // character buffer
unsigned int len; // for storing the length of the char buffer
};
Interviewer: Can you give some examples for such cases?
Amit: Yeah, suppose you have some kind of constant member or any reference variable you cannot initialize them inside the function definition because they will only be initialized once in the class.
Interviewer: Can you think of any other cases where the initializer list is a must?
Amit: Yeah, suppose there is some other class which only has a parameterized constructor and not a default constructor, and an object of that class is a member of our String class. If we try to initialize this in the body of our default constructor, the compiler will look for the default constructor of the other class, which is not present in that class and will thus generate an error. So we have to use the initializer list in this case as well.
Just knowing a particular concept or feature might not be enough in some cases. You should remember the use cases and be able to state them in the interview. Also, see how the interviewer can have cross questions about anything you mention? You should not get scared and must be able to answer!
Interviewer: Okay let’s continue with the default constructor.
Amit: Alright so this is our default constructor. Let’s move on to the parameterised constructor.
Interviewer: When you were writing down the use cases, you had mentioned that str1 would be equal to empty string. But here I see you’ve assigned to nullptr. So are you assigning an empty string or nullptr?
Amit: It will be nullptr, since there is no resource available to the string.
Interviewer: Okay.
Did you notice that he had said “empty string” when he was talking about functionalities? Be very mindful of what you are saying. I understand the pressure of interviews (especially for YouTube) but this just shows that the interviewer can catch the smallest of things. You should be very clear about small details of the implementation. Some interviewers like confusing candidates.
Amit: Now moving to the parameterized constructor. We will have a similar function signature, and will take parameters. We’re going to use the initializer list here as well but isn’t necessary here either. We will take the length of the string that is passed to this constructor and assign it to our len variable. We’ll then assign new memory to the res buffer (of size ‘len+1’ because of the additional ‘/0’ null char to mark the ending of the char buffer). What we want is to copy the characters in the ch parameter to our res. We simply use the strcpy to do this.
String(const char *ch)
{
len = strlen(ch);
res = new char[len + 1];
strcpy(res, ch);
}
Don’t forget to keep thinking of edge cases like (len+1) here! And also, we are so used to using STL that many times we forget the basics like strcpy. We need these basics for C++ interviews!
Amit: Now we’re moving to the copy constructor, to implement String str3 = str1. So for our signature, we’ll use a const String parameter.
String(const String &str)
{
}
Interviewer: Why we are using const parameters in both copy and parameterized constructors?
Amit: See, how will we use the parameterized constructor? We will pass some kind of a constant string. Have I answered your question?
Interviewer: In copy constructor also, you’ve used ‘const’ as well as ‘&’. Why is that?
Amit: We are using const because our string gets initialized with the string that the user provides and later no developer should be able to modify this string. And ‘&‘ denotes passing by reference – if we don’t use it, we will be passing a copy of the string to the function which itself would require a copy constructor.
Interviewer: So what if we don’t use ‘&’?
Amit: It will throw an error because it will be in an infinite loop as the copy constructor will keep calling itself.
Just remembering the syntax is not enough. You should understand why exactly we are writing each word. Be ready for all types of cross-questioning!
Amit: What I’m going to do here in the copy constructor is take the length we require, which is str.len. If a String has been passed, we’ll already know the length because we must have changed the len of it. Similarly we’ll assign a new resource to this string.
String(const String &str)
{
len = str.len;
res = new char[len + 1];
strcpy(res, str.res);
}
Interviewer: How are we able to access the len variable since it is a private member?
Amit: That’s correct, but we can access all the private members within the same class, but not from outside the class.
Amit: Now we’ll do an strcpy like before from str.res. This will take care of our copy constructor, and we’ll move on to the copy assignment operator.
Interviewer: What is the difference between copy constructor and copy assignment operator?
Amit: Copy constructor is only called when we are declaring the string for the first time but for the copy assignment operator the string has already been declared and we are just assigning a new value to it.
Interviewer: So if I’m not copying the value from any other string and I’m doing something str3=”hello”, will it call the copy assignment operator?
Amit: Yes, we will call copy assignment operator. The constructor will only be called when we are initializing for the first time. And here is the implementation of the copy assignment operator.
A lot of people get confused between constructor and assignment operator! I hope this makes it clear. Always notice the little details.
Amit: Here we need to consider some edge cases as well. For instance, someone can try to do str3=str3 (self-assignment). And we can’t really stop a user from doing this, instead we need to check if the user is assigning the same value or not. For that we will use the this pointer.
Interviewer: What is this?
Amit: The this pointer refers to the present object that has called this function. If the statement is str3 = str2, this is referring to str3.
Interviewer: Okay.
String& operator=(const String& str)
{
if(this != str)
{
}
return *this;
}
A very important point here again. Many, many candidates miss this self-assignment check!
Amit: So, we’ve taken care of self-assignment. In str3=str2, we know that str3 must have been having an assigned resource before. So it is occupying memory. Since we are assigning a new value to a previously declared string it is very important to delete the previous values otherwise it will create a memory leak. So we will first delete[] this resource.
Interviewer: Can you explain why we are using delete[] instead of delete?
Amit: Since the resource is an array of memory, we need to delete with delete[] so the whole memory array gets deleted.
Amit: After deleting the resource, we have to identify the new length to be assigned to str3. That will be str.len. Of course, we’ll need to assign a new res as well. And follow it with strcpy. And finally all we do is return *this.
String& operator=(const String& str)
{
if(this != str)
{
delete[] res;
len = str.len;
res = new char[len+1];
strcpy(res, str.res);
}
return *this;
}
Even though Amit knows all these things, I am asking him such questions so that you also notice these details. I have made these small mistakes in interviews. Delete vs delete[] is another very common question.
Amit: Next, we have the length function which is very simple. We just return len.
unsigned int length()
{
return len;
}
After length, we come to the input and output operations. We cannot define them inside the class the same way as we did with the ‘=’ operator. Here’s why. In str3=str2, str3 is the object calling the operator and passing str2 as a parameter. Similarly, in ‘cout<<str2’, cout will be interpreted by the compiler as the object using the << operator and passing str2 as the parameter. But we cannot go and manipulate the cout (the object that is calling) which is given in the standard C++ library.
Amit: So what we will be doing is creating a global function for the ” <<” operator and pass the cout and the string object both as a parameter, returning an ostream reference. So now when there is a call for this << function, it will look for any overloaded << operator function, and if the signature matches, the cout and str2 will go as parameters, without an object calling it.
ostream &operator<<(ostream &out, const String &str)
{
out << str.res;
return out;
}
Amit: The cout, passed as out, will be used to print str.res. And cout knows how to print a constant string. And we need to return the out, since our function has to return the ostream. And the reason we are returning an ostream is so that we can chain this operation like cout<<str2<<str3<<str4. But there is one more thing. Since we are directly using str.res, a private member, we can’t actually use it like this. So just to access this str.res, we have to define this overloaded function as a friend function in String class.
ostream &operator<<(ostream &out, const String &str)
{
out << str.res;
return out;
}
Interviewer: What is a friend function?
Amit: It’s a function we mention in the class that can directly access the private members of the class even outside the class.
The first time I came to know about this, I took a lot of time to understand it. If you’re hearing it for the first time – pause and think, go back a bit and watch again. It is an important concept that can really help you!
Amit: So, now that’s done. The same will be for the ‘>>’ operator as well, changing the ostream to istream, and adding this function as a friend as well.
istream &operator>>(istream &in, const String &str)
{
in >> str.res;
return in;
}
class String
{
...
friend ostream &operator<<(ostream &out, const String &str);
friend istream &operator>>(istream &in, const String &str);
...
}
If Amit hadn’t returned the ostream/istream operator, my next question would have been “How do you handle chaining?” Keep these cases in mind! Also, friend functions are a topic many of us ignore (at least, I did!). His explanation here is a good example of friend function implementation. Once you understand it, you will feel like a pro.
Amit: So I have implemented all the functionalities. Anything else you wanted me to implement?
Interviewer: Yes, so I have a couple of questions. If we move to the copy assignment operator, what we are doing is deleting the res buffer and assigning it to the new one but suppose for some reason that the new memory allocation fails. So in that case we will be losing the previously stored value as well – what can we do to tackle that?
Amit: Okay, so one way will be to store the previous value in some other temp buffer, allocate the current buffer with the new one and then delete the temp buffer at the end.
String& operator=(const String& str)
{
if(this != str)
{
char* temp = res;
len = str.len;
res = new char[len+1];
strcpy(res, str.res);
delete[] temp;
}
return *this;
}
Another way of doing this thing more efficiently is something known as the Copy And Swap idiom(CAS) in C++. How this will work is instead of passing the string by reference, we will pass it by value. Then when the copy assignment operator is called, the copy of the string is passed ,which in turn will call the copy constructor. Hence now, we can just simply swap these two with each other – swap(this,str). Then we return *this. This fulfils my aim of making sure my this object contains the resource in the passed string. The only thing we need to do here is create a swap() function, which takes references to the 2 strings, and use the C++ swap() function to swap the res and len of these 2 strings
String &operator=(String str)
{
swap(this, str);
return *this; // CAS idiom
}
void swap(String & str1, String & str2)
{
std::swap(str1.res, str2.res);
std::swap(str1.len, str2.len);
}
You can be asked a direct question about CAS. It is again a common question in C++ interviews.
Interviewer: Also, there is something known as the Move Constructor. Do you know about that?
Amit: This is a modern concept that’s been introduced since C++ 11. Let me try to explain how it’s implemented. The user can write something like
String str6 = std::move(str2);
Taking a look at our copy constructor, we move the resource from one string to another but both strings still exist at the end of that operation. The idea behind the move constructor is that we are moving all the resources from one object to another and killing the second object from where it is passed. So after moving, if you try to access that you will get a nullptr. This is an optimization that was introduced by C++ because sometimes what happens is that you call a function, and maybe create a temporary object that you return. Since you don’t need that object after returning it, instead of creating a copy of it you can just move it.
He is not just talking about what is move constructor, he is also talking about why we need it and how it is an optimization!
Amit: The implementation is similar to the copy constructor, except the parameter is a double ampersand String.
Interviewer: What is a double ampersand?
Amit: It’s called an Rvalue reference. A single ampersand is a simple reference, but an Rvalue ampersand is when you’re passing something as a temporary object. So there’s the whole concept of Rvalue and Lvalue there.
Amit: In our implementation, we won’t create a new resource, we’ll just have res point to str.res. We’ll assign nullptr to str so that it isn’t pointing to anything, and assign its len to 0. Thus after this, str will not show you anything but our res points to what str used to hold. Similarly, we can also say Str6 = str::move(str2);
String(const String &&str)
{
len = str.len;
res = str.res; // just pointing and not copying
str = nullptr;
str.len = 0;
}
It’s a similar concept where we overload the move copy assignment operator, which is almost similar. Should I implement this?
Interviewer: No, that’s okay but is there anything else that’s missing from the class that you would like to implement?
Amit: Keerti, well one thing we could implement here is a Destructor which is actually needed. If we don’t, any resource we assign will become a memory leak. Let me quickly do this. We’ll do it simply, checking if there is a res, deleting it, assigning it a nullptr and turning len to 0. Anything else?
~String()
{
if(res)
{
delete[] res;
res = nullptr;
len = 0;
}
}
Dangling pointer – yet another very important concept! I know I’m calling everything important, but trust me, you’ll need this.
Keerti: No, looks good! Thank you so much for coming and doing this, Amit. I’m sure this will help a lot of our readers. Since you’ve given and taken so many C++ interviews, do you have any tips for our readers?
Amit: First of all, thanks so much for calling me here! This is kind of a privilege and also very scary haha, especially since I’m camera-shy. As for tips, given my experience:
- You should be thoroughly prepared with the Pointers and References concepts
- You should be thorough with the Inheritance, VTable, VPointers etc. You simply can’t skip it
- Questions like what we did today, you have to be really thorough with String class kind of implementation from scratch, write everything down. In fact, I’d say understand and make sure you remember this because you’ll have to tackle this somehow in an interview. There are a lot of concepts we’ve covered here, so remember all these tiny details.
Keerti: Exactly, not only understand but remember the concepts and points. Even though you understood it, you have to be able to mention it in the interview.
Amit: Correct! When you do it, you just need to think for instance, why did I give ampersand and not just const? So before your interviews, do a quick review and practice it once.
Well, that’s a wrap for our super-thorough and intense C++ interview, we hope you were able to get a glimpse into the kind of preparation you might have to do for your own.
Would you like to watch a video of this mock interview? Check out our video on the official YouTube channel!