Post

C++ interviews questions - Part 01

C++ questions I encountered in interviews for game developer job postings.

C++ interviews questions - Part 01

C++ Interview Questions

Part 01


Q1: What is virtual destructor()? Why we need it?

Without it, deleting a derived class object through a base-class ptr results in undefined behavior (UB) because the derived class destructor is never called, leading to resource leaks.

Always declare a destructor as virtual if a class has at least one virtual function or is meant to be inherited.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
public:
    ~A() { std::cout << "A dtor\n"; }
};

class B : public A {
public:
    ~B() { std::cout << "B dtor\n"; }
};

int main() 
{
    A* a = new B();
    delete a; // Only Base destructor called, Derived destructor missed
}

Try here.


Q2: How will you take a decision of choosing array vs linked list for a particular implementation?

  • for quick access, prefer array
  • for frequent insert/remove, prefer linked list

Q3: What is smart pointers? Why we need it?

  • C++11
  • RAII pattern
  • With them, manage the lifetime of dynamically allocated memory automatically.
  • Without them, you have to be careful. If you forget to delete, you get a mem leak.
  • Reducing the chances of memory leaks and dangling pointers.

std::unique_ptr

  • Exclusive ownership. Only 1 can own the resource.
  • Object deleted when it goes out of scope.

std::shared_ptr

  • Shared ownership. Multiple can own the resource.
  • Ref counted. Object deleted when ref count == 0

std::weak_ptr

  • for breaking reference cycle.
  • does not increase the ref count.

What is Cyclic References?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct B;

struct A { std::shared_ptr<B> bptr; };
struct B { std::shared_ptr<A> aptr; };

int main() 
{
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    a->bptr = b;
    b->aptr = a;  // circular reference

    // memory for A and B is never freed!
}

Solution:

1
2
3
4
5
struct B;

// decide who owns who 
struct A { std::shared_ptr<B> bptr; };
struct B { std::weak_ptr<A> aptr; };

Example: weak_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <memory>
 
std::weak_ptr<int> gw;
 
void observe()
{
    std::cout << "gw.use_count() == " << gw.use_count() << "; ";
    // we have to make a copy of shared pointer before usage:
    if (std::shared_ptr<int> spt = gw.lock())
        std::cout << "*spt == " << *spt << '\n';
    else
        std::cout << "gw is expired\n";
}
 
int main()
{
    {
        auto sp = std::make_shared<int>(42);
        gw = sp;
 
        observe();
    }
 
    observe();
}

Q4: Smart pointers as a function argument?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void TransferOwnership(std::unique_ptr<int> ptr) {
    std::cout << "Value: " << *ptr << "\n";
}

void ReadOnly(const std::unique_ptr<int>& ptr) {
    std::cout << "Read: " << *ptr << "\n";
}

void ProcessShared(std::shared_ptr<int> ptr) {
    std::cout << "Shared Value: " << *ptr << "\n";
    // ref count increased
}

void ProcessSharedRef(const std::shared_ptr<int>& ptr) {
    std::cout << "By ref: " << *ptr << "\n";
    // ref count WILL NOT increased
}

void CheckWeak(std::weak_ptr<int> weak) {
    if (auto locked = weak.lock()) 
        std::cout << "Value: " << *locked << "\n";
    else
        std::cout << "Pointer expired.\n";
}

int main() {
    std::unique_ptr<int> myPtr = std::make_unique<int>(42);
    TransferOwnership(std::move(myPtr));  // Ownership is transferred
    return 0;
}

This post is licensed under CC BY 4.0 by the author.