Katkat

Compare C Generic Techniques and C++ Smart-Pointer Ownership

NetappSystem Software Engineer · Technical Screen

Last updated

Tests understanding of low-level generic programming patterns in C and memory ownership semantics in modern C++, including runtime safety and resource management trade-offs.

Context

Systems software often requires reusable, generic data structures and strict, leak-free memory management. While C relies on manual idiom-based abstractions, C++ formalizes resource ownership in its type system.

In this technical discussion question, address the following sections in detail with brief code snippets illustrating the concepts:

Part 1: Generic Programming in C

  1. Explain how C achieves type genericity using void*, function pointer callbacks (e.g., standard qsort), and macro metaprogramming (such as macro-instantiated containers or C11 _Generic).
  2. Explain why C historically lacks native function overloading, referencing symbol generation, linkage, and compiler ABI design.
  3. Identify the major safety, performance, and maintenance trade-offs of using void* casts versus macro-expanded inline generics.

Part 2: Modern C++ Ownership Semantics

  1. Explain the concept of Resource Acquisition Is Initialization (RAII) and contrast exclusive ownership (std::unique_ptr) with shared ownership (std::shared_ptr). Detail why move semantics are mandatory for std::unique_ptr.
  2. Describe the internal layout of std::shared_ptr, specifically explaining the role of the control block, reference counts (strong vs. weak), and memory allocation overhead (e.g., std::make_shared vs. separate allocations).
  3. Explain how cyclic references occur with std::shared_ptr, how std::weak_ptr breaks cycles, and how a std::weak_ptr safely accesses the underlying object when ownership might have already expired.

Expected Response Format

Structure your answer systematically, addressing each numbered prompt under both parts. Provide short C and C++ code examples to demonstrate custom comparators in C and safe std::weak_ptr::lock usage in C++.

Hints

Hint 1

Consider how the C linker resolves external symbols compared to the name-mangling required for C++ function overloading.

Hint 2

For void* generics, focus on type erasure at compile time versus code bloat and type checking with macro template expansions.

Hint 3

Remember that std::shared_ptr control blocks must track two separate counters: the strong count that dictates object lifetime, and the weak count that dictates control block lifetime.

Follow-up questions

  • What is the performance and cache-locality difference between creating a `std::shared_ptr` via `std::make_shared` versus passing a raw pointer to its constructor?
  • How does the deleter implementation in `std::unique_ptr` differ in type footprint from the custom deleter in `std::shared_ptr`?
  • If you had to implement an intrusive reference-counted pointer in C, how would you design the structure and lifetime API?