Menu
CoddyTech

C++ Memory Management and Smart Pointers Interview Questions

new and delete, stack vs heap, leaks, RAII and the three smart pointers.

10 questions
  1. What is the difference between new/delete and malloc/free?

    new allocates memory and runs the constructor, and delete runs the destructor before freeing; malloc and free only handle raw bytes. new is also type safe and throws std::bad_alloc on failure, where malloc returns NULL.

    Mixing them (new with free, new[] with delete) is undefined behavior.

  2. What is the difference between stack and heap memory in C++?

    Local variables live on the stack and are destroyed when their scope ends; objects from new live on the heap until freed. The stack is fast but small, so deep recursion overflows it: 1 MB by default on Windows (see the /STACK linker option) and usually 8 MB on Linux. The heap is larger and slower, and its bugs are leaks and use after free. std::vector<int> v(1000000); puts the vector object on the stack and its ints on the heap.

  3. What is the difference between delete and delete[]?

    delete p destroys one object from new; delete[] p destroys an array from new T[n], calling every element's destructor. The wrong form is undefined behavior: for elements with a destructor the compiler usually stores the count before the array, and only delete[] reads it. Prefer std::vector or std::make_unique<int[]>(n).

  4. What is a memory leak in C++, and how do you find and prevent one?

    Heap memory that is never freed, because nothing points to it any more or the code that frees it never runs. Typical causes are new without delete on an exception path, a missing virtual destructor, and shared_ptr cycles.

    Find leaks with Valgrind or AddressSanitizer. Prevent them with RAII: containers and std::unique_ptr instead of raw new.

  5. What is RAII in C++?

    RAII ties a resource to an object's lifetime: the constructor acquires it (memory, a file, a mutex) and the destructor releases it. Destructors run at scope end, including during stack unwinding, so release is guaranteed.

    C++
    #include <iostream>
    #include <stdexcept>
    using namespace std;
    
    struct File {
        File()  { cout << "open\n"; }
        ~File() { cout << "close\n"; }
    };
    
    void work() {
        File f;
        throw runtime_error("disk full");
    }
    
    int main() {
        try {
            work();
        } catch (const exception& e) {
            cout << "caught: " << e.what() << "\n";
        }
    }

    This prints open, close, then caught: disk full. std::vector, std::unique_ptr and std::lock_guard work this way, which is why C++ has no finally.

  6. What are smart pointers in C++? Explain unique_ptr, shared_ptr and weak_ptr.

    RAII wrappers that delete the object they own. std::unique_ptr has one owner, can only be moved and costs nothing extra. std::shared_ptr shares ownership through a reference count. std::weak_ptr observes a shared_ptr without owning it.

    C++
    #include <iostream>
    #include <memory>
    #include <string>
    using namespace std;
    
    int main() {
        auto p = make_unique<string>("hi");
        auto q = std::move(p);          // ownership moves, p becomes empty
        cout << (p ? "p owns" : "p is empty") << ", q = " << *q << "\n";
    }

    This prints p is empty, q = hi. Default to unique_ptr. More in the smart pointers docs.

    Practice it: Implement Trie (Prefix Tree)

  7. How do scopes and a weak_ptr affect a shared_ptr's use_count()?

    C++
    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main() {
        auto a = make_shared<int>(42);
        auto b = a;
        {
            auto c = a;
            cout << a.use_count() << " ";
        }
        weak_ptr<int> w = a;
        cout << a.use_count() << "\n";
    }

    It prints 3 2. a, b and c share ownership, so the count is 3 inside the block and 2 after c is destroyed. A weak_ptr does not add to use_count().

    The count is atomic, but two threads writing the same shared_ptr variable is still a data race.

  8. What is a shared_ptr reference cycle and how does weak_ptr fix it?

    If A holds a shared_ptr to B and B holds one back to A, neither count reaches zero and both leak. Make the back pointer (child to parent, prev) a weak_ptr.

    C++
    #include <iostream>
    #include <memory>
    #include <string>
    using namespace std;
    
    struct Node {
        string name;
        shared_ptr<Node> next;   // owns
        weak_ptr<Node> prev;     // observes only
        Node(string n) : name(n) {}
        ~Node() { cout << "free " << name << "\n"; }
    };
    
    int main() {
        auto a = make_shared<Node>("a");
        auto b = make_shared<Node>("b");
        a->next = b;
        b->prev = a;
        if (auto p = b->prev.lock()) cout << "b's prev is " << p->name << "\n";
    }

    This prints b's prev is a, then free a and free b; with a shared_ptr prev, neither destructor runs. See the doubly linked list visualization.

  9. Why prefer std::make_shared over std::shared_ptr<T>(new T)?

    make_shared does one allocation for the object and its control block; shared_ptr<T>(new T) does two. The trade-off: the memory stays allocated until the last weak_ptr is gone, and make_shared cannot take a custom deleter; see the make_shared notes.

  10. What is placement new, and when is it used?

    new (address) T(args) constructs an object in memory you already own, without allocating. std::vector relies on it: the capacity beyond size() is raw memory, and push_back constructs the new element there.

    C++
    #include <iostream>
    #include <new>
    #include <string>
    using namespace std;
    
    int main() {
        alignas(string) unsigned char buffer[sizeof(string)];
        string* s = new (buffer) string("built in my buffer");   // no allocation for the object
        cout << *s << "\n";
        s->~string();   // destroy it yourself; never delete
    }

    It prints built in my buffer. The rules: the buffer must be large enough and aligned for T, you call the destructor yourself, and you never delete the pointer. Memory pools and arena allocators use the same technique, and C++20 adds std::construct_at as a safer spelling.

Coddy programming languages illustration

Learn to code with Coddy

Get started