C++ Interview Questions Part 1

  • 0

C++ Interview Questions Part 1

Category : Learning

Want create site? Find Free WordPress Themes and plugins.
Do you want to learn C++ interview questions before interview? Either you are employer or programmer, we will help!

Introduction

We all know how hard it is to find good programmers to hire.

This is doubly true in the case of C++ programmers, for one simple reason:

The age, scope and complexity of the C++ language make it one of the hardest mainstream programming languages to learn. Thus, it takes a significant amount of C++ knowledge and experience to even be able to assess the expertise of candidate on interview. This is why it’s helpful to have a canned set of interview questions to turn to.

The article has unusual format (hopefully you will enjoy reading!). C++ programming interview questions have been wrapped into interview story. In case you want to see more formal format, just check this version of C++ interview questions.

C++ Interview Story

My interview candidate today is Rami. Rami started out as a Java programmer working for a major bank, but taught himself C++ in order to get a job in the game industry.

We shake hands and sit down at the interview table. To get the conversation started, I ask Rami how he got into programming. It turns out he was inspired by the video games of the late ‘80s — so he got a computer science degree, but then lost sight of his passion for a while.

After some more chit-chat about how great a game Elite was, we get down to business. Because of Rami’s background in Java, I want to see whether he “thinks like a C++ programmer” now, or whether he still carries around the mental baggage of the Java world, much of which doesn’t apply in C++. So I start the interview proper with a question about the C++ preprocessor, which has no equivalent in most other languages.

Here’s what I ask:

[Question #1]

At what stage of compilation is the preprocessor invoked?” I ask.

“Before actual compilation,” Rami answers promptly. “It basically takes the code, transforms it, then passes it on to the compiler. It’s mostly a dumb lexical transformation, which is also what makes it risky to use.”

Sounds like a good summary, but I want to dig a bit deeper, so I ask: “What kind of directives are there?”

#define, of course,” Rami answers. “And #undef to remove a definition. There’s #ifdef and #endif for conditional compilation. Oh, and #include, of course.”

He missed out a few, but I don’t really care. One never needs to list these out in practice. On to something more interesting; I show him the following line of code and ask him which two things are wrong with it:

#define A 2048*B

“There should be parentheses,” he answers immediately. “Otherwise, things could go wrong. Say, for example, when you write A+4… no, that expands to 2048*B+4, so that’s fine. But if you used some operator that has higher precedence than *, you’re in trouble. So you need parentheses, like (2048*B).”

“True,” I say. “Anything else?”

Rami thinks for a while, then says:

“Depending on the definition of B, you might also want to parenthesize its use. Suppose B is defined as 4+4, then you’d get 2048*4+4. So the correct definition of the macro would be (2048*(B)).”

“Yep,” I nod. I note that he didn’t mention names; for instance, if I use a local variable named A, or more likely, a template type argument A, things will break. But I’m willing to overlook that fact, because the example looks contrived and might have put him on the wrong foot.

[Question #2]

“OK, onwards to the next one of the question,” I say.

Here’s what I ask him next:

Could you explain to me what an automatic object (also known as stack object) is, and what its lifetime is?

Rami looks slightly unsure. “An object on the stack, you mean? It is created at the point of its definition, and lives until the end of its scope. So basically, from its definition until the first closing curly brace.”

“How about an object on the heap?”

Rami seems to have been expecting the question. “These are created with the new keyword. C++ doesn’t have a garbage collector, so the destructor isn’t called automatically. You have to call delete to clean the object up.”

It’s clear that he knows the basics here, but I want to see whether he knows the practice as well as the theory.

That’s why I ask him the following:

“Why is heap storage discouraged? How can you avoid it?”

“You have to be careful to clean up,” Rami answers, “otherwise you leak memory. You should use stack allocation whenever possible. If it’s not possible, always make sure to wrap your code in try/catch so that you clean up even if an exception is thrown.”

Too bad. I was hoping he would mention the use of smart pointers, but it seems his Java background and the fact that he’s an autodidact in C++ have caught up with him. I dig a bit deeper to see what he knows: “To make it concrete, why is this a bad idea?”

auto p = new int[50];

Rami seems a bit unsure what I’m driving at. “You have to delete it afterwards, and you’ll leak memory if you don’t. It’s better to declare it on the stack. But if you want to return it from a function, for instance, you cannot do that; then you’ll have to wrap the array into an object, like this…” He starts writing some example code, until halfway through, he comes to a realization: “Oh, that’s basically just std::vector. So you should use that instead.”

“Good call,” I admit. Still no mention of smart pointers, sadly.

[Question #3]

I decide to move on to a different topic. Here’s my next question:

“On that note, while you’re designing classes, could you write the Rule of Five functions for the following class? That is, copy constructor, move constructor, copy assignment operator and move assignment operator?

class DirectorySearchResult {
public:
    DirectorySearchResult(
        std::vector<std::string> const& files,
        size_t attributes,
        SearchQuery(const* query)
        : files(files),
          attributes(attributes),
          query(new SearchQuery(*query))
    {}

    ~DirectorySearchResult() { delete query; }

    //TODO

private:
    str::vector<std::string> files;
    size_t attributes; // of the directory
    SearchQuery* query;
};

Rami takes a look at the code, scratches his chin. “The files and attributes are copied automatically. That’s easy. For the query member, do you want me to clone it, or keep the pointer to the same object?”

“Take a good look at the existing constructor,” I say, not wanting to give away the entire game. “That should be able to answer your question.”

Rami’s face lights up. “Ah, I see, we’re creating a new object here. So the query is owned by the DirectorySearchResult. I wonder why it needs to be a pointer then?”

“Great observation!” I exclaim, surprised.

I seriously hadn’t even seen that. But if we go that route, the whole question becomes trivial. “So let’s pretend that null values are valid for this pointer as well. There may not always be a query.”

Rami nods. “OK. Let’s start with the copy stuff…”

DirectorySearchResult(const DirectorySearchResult& other) {
    this->files = other.files;
    this->attributes = other.attributes;
    this->query = new SearchQuery(*other.query);
}

I note that he doesn’t use the initializer list, and writes a few needless references to this. Both are typical for Java programmers, but mostly harmless. When I point out that we just said that query may be null, he quickly adapts:

if (other.query) {
    this->query = new SearchQuery(*other.query);
}

Sadly, in C++, this may leave a query uninitialized rather than null.

Rami proceeds with the copy assignment operator:

operator=(const DirectorySearchResult& other) {
    this->files = other.files;
    this->attributes = other.attributes;
    if (this->query) {
        delete this->query;
        this->query = 0;
    }
    if (other.query) {
        this->query = new SearchQuery(*other.query);
    }
}

Usage of 0 instead of nullptr or even NULL is a bit old-fashioned; maybe he learned from resources written before the C++11 days? But at least nulls are handled correctly here. He missed out the return value; but his compiler would tell him that, so I’m not too concerned about it. More concerning is that self-assignment is not being checked for, so it will break.

Edging him on, I say: “OK. How about move construction and move assignment?” These concepts are specific to C++, so especially if he only learned “old style” C++, I might expect him to flounder. And indeed he does:

DirectorySearchResult(DirectorySearchResult&& other) {
    this->files = other.files;
    this->attributes = other.attributes;
    this->query = other.query;
    other.query = 0;
}

While this works, the files vector is still being copied, rather than moved, largely negating the performance benefit of a move constructor in the first place. Similarly in the move assignment operator:

operator=(DirectorySearchResult&& other) {
    this->files = other.files;
    this->attributes = other.attributes;
    if (this->query) {
        delete this->query;
        this->query = 0;
    }
    this->query = other.query;
    other.query = 0;
}

A more succinct and idiomatic approach would have been to use swap. Again he missed the return type and return statement, but that was likely due to copy and paste of his previous code.

[Question #4]

“Alright,” I say, “that should do it. Let’s move on.” I still want to see whether he knows anything about smart pointers.

That’s why I ask:

“What happens when a std::unique_ptr is passed by value to a function? For example, in this code snippet?”

#include <memory>

auto f(std::unique_ptr<int> ptr) {
    *ptr = 42;
    return ptr;
}

int main() {
    auto ptr = std::make_unique<int>();
    ptr = f(ptr);
}

Rami looks at the code. “So it takes a unique_ptr, and returns it as well. I don’t think that would even compile. A unique pointer can’t be copied.”

“Correct,” I say. “How could you make it compile?”

It turns out Rami knows a little about smart pointers after all: “You could use a regular pointer, but of course then you’d need to clean it up afterwards. Better is to use a std::shared_ptr instead. It can be copied and returned.”

“That’s one way of doing it,” I admit, “but what if you really had to use a unique_ptr? How could you make that work?”

Rami scratches his chin. “You could pass it by pointer or by reference. By reference would be nicer, because a pointer to a pointer would be confusing.”

“Sure. Any other ways?” I probe.

&##8220;Hmm… wrap it in an object? That doesn’t make much sense though,” he mutters. Using std::move does not seem to occur to him – another sign that he might not be well-versed in the modern editions of C++.

[Question #5]

I decide not to press the matter. Instead, I ask something else:

“On the subject of iterators, could you tell me what types there are, and how they relate to each other?”

Here, Rami seems to be back on familiar ground. “There are forward iterators and backward iterators. You also have random access iterators, which you can increment with arbitrary integers, rather than just +1 or -1. Most iterators are input iterators, which let you read, but there are also output iterators like std::back_inserter.”

Something he said about backward iterators has caught my attention, because they don’t really exist.

I decide to dig a bit deeper:

“You mentioned backward iterators. Do any backward iterators exist that cannot go forward?”

“Yes, for example std::vector::rbegin(),” he says, confidently at first. “Oh, wait, that’s actually random access, so it can go forward by incrementing with -1. But, for instance, std::list::rbegin() cannot, because it’s not random access; then again, it can just be decremented to go forwards. Maybe std::map::rbegin()? But I’m not sure about that. Maybe they don’t exist.”

I’ll leave it at that and move on: “Will this code work? Why or why not?”

std::list<int> l{1, 2, 3};
std::sort(l.begin(), l.end());

Glancing over the code, Rami gives the answer I was hoping for: “That doesn’t work. Sorting requires random access, which a linked list doesn’t provide.”

“Quite right,” I say, ending the interview on a high note. “We’re almost out of time here, so I’d like to give you the opportunity to ask me questions in return. Anything you want to know about the job, the company, or anything else really?”

This leads to more nostalgic talk about childhood video games, until a polite knock on the door reminds us that the room is booked for another interview. I say goodbye to Rami and leave with mixed feelings.

On the one hand, Rami is passionate and has come a long way. On the other hand, his answers to my interview questions were a mixed bag, and I’m not convinced that he is as experienced in C++ as we need him to be.

Read: Part 2

Author: Mike_My_Name

Did you find apk for android? You can find new Free Android Games and apps.