Interview

10 Smart Pointer Interview Questions and Answers – CLIMB

Prepare for your C++ interview with this guide on smart pointers, covering efficient memory management and common interview questions.

Smart pointers are a crucial feature in modern C++ programming, designed to manage dynamic memory more efficiently and safely. They help prevent common issues such as memory leaks and dangling pointers by automatically handling the allocation and deallocation of memory. Smart pointers encapsulate raw pointers, providing a more robust and error-resistant way to manage resources in complex applications.

This article offers a curated selection of smart pointer interview questions, complete with detailed answers. By familiarizing yourself with these questions, you will gain a deeper understanding of smart pointers and be better prepared to demonstrate your expertise in technical interviews.

Smart Pointer Interview Questions and Answers

1. Explain the primary purpose of using smart pointers in C++.

Smart pointers in C++ manage dynamically allocated memory automatically and safely, preventing memory leaks and dangling pointers. The primary types are std::unique_ptr, std::shared_ptr, and std::weak_ptr.

  • std::unique_ptr: Exclusively owns the object it points to. The object is deleted when the std::unique_ptr goes out of scope.
  • std::shared_ptr: Allows multiple pointers to share ownership of the same object, which is deleted when the last std::shared_ptr is destroyed.
  • std::weak_ptr: Used with std::shared_ptr to break circular references. It does not own the object and does not affect its lifetime.

Example:

#include 
#include 

void uniquePtrExample() {
    std::unique_ptr uniquePtr = std::make_unique(10);
    std::cout << "Unique Pointer Value: " << *uniquePtr << std::endl;
}

void sharedPtrExample() {
    std::shared_ptr sharedPtr1 = std::make_shared(20);
    std::shared_ptr sharedPtr2 = sharedPtr1;
    std::cout << "Shared Pointer Value: " << *sharedPtr1 << std::endl;
    std::cout << "Shared Pointer Use Count: " << sharedPtr1.use_count() << std::endl;
}

int main() {
    uniquePtrExample();
    sharedPtrExample();
    return 0;
}

2. Write a simple code snippet to demonstrate how to create and use a unique_ptr.

A unique_ptr ensures exclusive ownership of a dynamically allocated object, automatically deleting it when the unique_ptr goes out of scope.

#include 
#include 

class MyClass {
public:
    MyClass() { std::cout << "MyClass Constructor\n"; }
    ~MyClass() { std::cout << "MyClass Destructor\n"; }
    void display() { std::cout << "Hello from MyClass\n"; }
};

int main() {
    std::unique_ptr ptr = std::make_unique();
    ptr->display();
    return 0;
}

3. What is a weak_ptr, and why would you use it instead of a shared_ptr?

A weak_ptr holds a non-owning reference to an object managed by shared_ptr, preventing circular references that can lead to memory leaks.

#include 
#include 

class Node {
public:
    std::shared_ptr next;
    std::weak_ptr prev; // Prevents circular reference

    ~Node() {
        std::cout << "Node destroyed" << std::endl;
    }
};

int main() {
    auto node1 = std::make_shared();
    auto node2 = std::make_shared();

    node1->next = node2;
    node2->prev = node1; // No circular reference

    return 0;
}

4. Write a code example showing how to avoid a circular reference using weak_ptr.

Circular references occur when objects reference each other, creating a cycle that prevents the reference count from reaching zero. weak_ptr breaks these cycles by holding a non-owning reference.

#include 
#include 

class B; // Forward declaration

class A {
public:
    std::shared_ptr ptrB;
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::weak_ptr ptrA; // Avoids circular reference
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    std::shared_ptr a = std::make_shared();
    std::shared_ptr b = std::make_shared();
    a->ptrB = b;
    b->ptrA = a;

    return 0;
}

5. Explain how custom deleters work with unique_ptr and provide a code example.

Custom deleters in unique_ptr allow specifying a custom function to release resources when the unique_ptr goes out of scope.

#include 
#include 

struct FileDeleter {
    void operator()(FILE* file) const {
        if (file) {
            std::cout << "Closing file\n";
            fclose(file);
        }
    }
};

int main() {
    std::unique_ptr filePtr(fopen("example.txt", "w"));
    if (filePtr) {
        std::cout << "File opened successfully\n";
    }
    return 0;
}

6. Write a code snippet demonstrating exception safety with smart pointers.

#include 
#include 

void process_data(std::shared_ptr ptr) {
    if (!ptr) {
        throw std::runtime_error("Null pointer exception");
    }
    std::cout << "Processing data: " << *ptr << std::endl;
}

int main() {
    try {
        std::shared_ptr data = std::make_shared(42);
        process_data(data);
    } catch (const std::exception& e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
    }
    return 0;
}

7. Explain the purpose and usage of make_unique and make_shared.

make_unique and make_shared simplify the creation of std::unique_ptr and std::shared_ptr, respectively, ensuring resources are properly released.

#include 
#include 

int main() {
    std::unique_ptr uniquePtr = std::make_unique(42);
    std::cout << "Unique Pointer Value: " << *uniquePtr << std::endl;

    std::shared_ptr sharedPtr = std::make_shared(42);
    std::cout << "Shared Pointer Value: " << *sharedPtr << std::endl;

    return 0;
}

8. Discuss the ownership semantics of unique_ptr, shared_ptr, and weak_ptr.

Smart pointers manage the lifetime of dynamically allocated objects with different ownership semantics.

  • unique_ptr: Exclusive ownership. The object is deleted when the unique_ptr goes out of scope. It can be moved to transfer ownership.
#include 
#include 

int main() {
    std::unique_ptr ptr1 = std::make_unique(10);
    std::unique_ptr ptr2 = std::move(ptr1); // Transfer ownership
    std::cout << *ptr2 << std::endl; // Output: 10
    return 0;
}
  • shared_ptr: Shared ownership. The object is deleted when the last shared_ptr is destroyed. Uses reference counting.
#include 
#include 

int main() {
    std::shared_ptr ptr1 = std::make_shared(20);
    std::shared_ptr ptr2 = ptr1; // Shared ownership
    std::cout << *ptr1 << std::endl; // Output: 20
    std::cout << *ptr2 << std::endl; // Output: 20
    return 0;
}
  • weak_ptr: Non-owning reference. Used to break circular references. Can be converted to shared_ptr if needed.
#include 
#include 

int main() {
    std::shared_ptr sharedPtr = std::make_shared(30);
    std::weak_ptr weakPtr = sharedPtr; // Weak reference
    if (auto tempPtr = weakPtr.lock()) { // Convert to shared_ptr
        std::cout << *tempPtr << std::endl; // Output: 30
    } else {
        std::cout << "Object no longer exists" << std::endl;
    }
    return 0;
}

9. Explain how to convert between different types of smart pointers.

To convert between smart pointers:

  • Convert std::unique_ptr to std::shared_ptr using std::move.
  • Convert std::shared_ptr to std::weak_ptr directly.
  • Convert std::weak_ptr back to std::shared_ptr using the lock method.

Example:

#include 
#include 

int main() {
    std::unique_ptr uniquePtr = std::make_unique(10);
    std::shared_ptr sharedPtr = std::move(uniquePtr);

    std::weak_ptr weakPtr = sharedPtr;

    if (auto lockedPtr = weakPtr.lock()) {
        std::cout << "Value: " << *lockedPtr << std::endl;
    } else {
        std::cout << "Pointer is expired." << std::endl;
    }

    return 0;
}

10. Given a scenario where multiple threads need to access a shared resource, how would you use smart pointers to manage this safely?

To manage shared resources safely in a multi-threaded environment, use smart pointers with synchronization primitives like std::mutex. Smart pointers manage the lifetime of objects, while a mutex ensures thread safety.

#include 
#include 
#include 
#include 
#include 

std::shared_ptr sharedResource;
std::mutex resourceMutex;

void accessResource(int id) {
    std::lock_guard lock(resourceMutex);
    if (sharedResource) {
        std::cout << "Thread " << id << " accessing resource: " << *sharedResource << std::endl;
    } else {
        std::cout << "Thread " << id << " found resource uninitialized." << std::endl;
    }
}

int main() {
    sharedResource = std::make_shared(42);

    std::vector threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(accessResource, i);
    }

    for (auto& thread : threads) {
        thread.join();
    }

    return 0;
}

In this example, std::shared_ptr manages the shared resource, and std::mutex ensures only one thread accesses the resource at a time. The std::lock_guard provides a convenient mechanism for acquiring and releasing the mutex.

Previous

20 World Wildlife Fund Interview Questions and Answers - CLIMB

Back to Interview
Next

20 DSM Interview Questions and Answers - CLIMB