PracHub
QuestionsCoachesLearningGuidesInterview Prep
|Home/Software Engineering Fundamentals/Sig

Implement a Simplified std::vector with Manual Memory Management

Last updated: Jul 1, 2026

Quick Overview

This question evaluates a candidate's grasp of low-level memory management in C++, including the distinction between raw allocation and object construction, geometric capacity growth, and the Rule of Five for copy/move semantics. It is commonly used to assess practical systems-programming skill and reasoning about exception safety during reallocation, testing applied understanding rather than pure theory.

  • medium
  • Sig
  • Software Engineering Fundamentals
  • Software Engineer

Implement a Simplified std::vector with Manual Memory Management

Company: Sig

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Onsite

Implement a simplified `Vector<T>` that mirrors the core behavior of `std::vector<T>`. The emphasis is **not** on the public API surface but on correct, low-level memory management: cleanly separating raw allocation from object construction (`::operator new` / placement `new`), an amortized-O(1) growth strategy, correct deep-copy value semantics, and cheap move semantics. You must manage the storage by hand — you may not use `std::vector`, `std::unique_ptr`, or `new T[n]` for the buffer itself. (You may use `std::move`, `std::swap`, and type traits.) ### Constraints & Assumptions - C++17, single-threaded, no `Allocator` template parameter — use global `::operator new(bytes)` / `::operator delete(ptr)` for raw memory. - `T` may be non-trivially copyable/movable, and its constructors may throw. - API to support: default constructor, `push_back(const T&)` and `push_back(T&&)`, `operator[]`, `size()`, `capacity()`, `reserve(n)`, copy constructor, copy assignment, move constructor, move assignment, destructor. - Out of scope: iterators, `insert`/`erase` in the middle, `shrink_to_fit`. `emplace_back` with perfect forwarding is a bonus, not required. - Target: `push_back` is amortized O(1). ### Clarifying Questions to Ask - Should allocation and construction be separated (reserved-but-unconstructed capacity, like the real `std::vector`), or is a simpler "construct every slot" model acceptable? - What growth factor is expected — 2x, 1.5x — and do you care about the realloc/memory-reuse tradeoff? - Must `push_back` provide the **strong** exception guarantee across a reallocation? - Is `T` guaranteed nothrow-move-constructible, or must I handle a throwing move during relocation? - Any limits on standard-library usage beyond the storage (e.g. may I use `std::move_if_noexcept`, `std::uninitialized_copy`)? ### Part 1: Memory model and capacity growth Define the data members and implement raw allocation, `reserve(n)`, and `push_back` with a geometric growth strategy. The key requirement: **acquire raw, uninitialized memory separately from constructing elements into it.** ```hint Where to start Keep exactly three members: a raw `T*` to the buffer, `size_` (number of *constructed* elements), and `capacity_` (number of *allocated* slots). The invariant is `size_ <= capacity_`; allocated is not the same as constructed. ``` ```hint Allocation vs construction Get uninitialized bytes with `::operator new(cap * sizeof(T))` and build each element in place with placement `new (raw + i) T(value)`. Never use `new T[n]` — that default-constructs every slot and forces a default constructor on `T`. ``` ```hint Growth When `size_ == capacity_`, allocate a larger buffer (`capacity_ ? 2 * capacity_ : 1`), relocate the existing elements, destroy the originals, and free the old raw buffer. Geometric growth is what makes `push_back` amortized O(1). ``` #### What This Part Should Cover ```premium-lock What This Part Should Cover ``` ### Part 2: Value and move semantics (Rule of Five) Implement the destructor, copy constructor, copy assignment, move constructor, and move assignment so that `Vector<T>` has independent deep-copy value semantics and O(1) moves. ```hint Rule of Five A type that owns a raw resource and defines *any* of {destructor, copy ctor, copy assign, move ctor, move assign} should define *all five*. The compiler-generated shallow versions copy the pointer, so two vectors free the same buffer. ``` ```hint Copy assignment Prefer copy-and-swap for a clean strong guarantee, or, if you assign member-by-member, guard against self-assignment and free the old buffer in the right order. ``` ```hint Move Steal the pointer/size/capacity from the source and null them out so the moved-from object's destructor is a harmless no-op. Mark the move operations `noexcept`. ``` #### What This Part Should Cover ```premium-lock What This Part Should Cover ``` ### Part 3: Element lifetime and exception safety Make destruction and reallocation correct when `T` can throw. Implement the destructor, and ensure `reserve`/`push_back` neither leak nor corrupt the container if an element constructor or move throws mid-reallocation. ```hint Destruction Destroy exactly the `size_` constructed elements by calling `buf[i].~T()` in a loop, then return the raw memory with `::operator delete(buf)`. Do not use `delete[]` — it was never a `new[]` array. ``` ```hint Strong guarantee On a grow, construct all elements into the NEW buffer first. Only after that fully succeeds do you destroy the old elements and swap pointers. If a copy/move throws partway, destroy whatever you already built in the new buffer, free it, and rethrow — the old buffer is left untouched. ``` ```hint Throwing moves If `T`'s move ctor is not `noexcept`, relocating with moves can wreck both buffers on a throw. Relocate with `std::move_if_noexcept` so non-nothrow-movable types are *copied* (preserving the original) instead. ``` #### What This Part Should Cover ```premium-lock What This Part Should Cover ``` ### What a Strong Answer Covers ```premium-lock What a Strong Answer Covers ``` ### Follow-up Questions - Why might 1.5x be preferred over 2x as a growth factor for a general-purpose allocator? - How would you add `emplace_back` with perfect forwarding, and what does it save compared to `push_back`? - What are the iterator/reference invalidation rules on reallocation, and how would they shape your API contract? - How would introducing an `Allocator` template parameter change where construction and destruction happen?

Quick Answer: This question evaluates a candidate's grasp of low-level memory management in C++, including the distinction between raw allocation and object construction, geometric capacity growth, and the Rule of Five for copy/move semantics. It is commonly used to assess practical systems-programming skill and reasoning about exception safety during reallocation, testing applied understanding rather than pure theory.

Related Interview Questions

  • Trees and Binary Search Trees: Taxonomy, Invariant, and Traversal/Search - Sig (medium)
  • Find and Fix C++ Ownership Bugs: Double Free, Shallow Copy, and Object Slicing - Sig (medium)
|Home/Software Engineering Fundamentals/Sig

Implement a Simplified std::vector with Manual Memory Management

Sig logo
Sig
Jun 15, 2026, 12:00 AM
mediumSoftware EngineerOnsiteSoftware Engineering Fundamentals
0
0

Implement a simplified Vector<T> that mirrors the core behavior of std::vector<T>. The emphasis is not on the public API surface but on correct, low-level memory management: cleanly separating raw allocation from object construction (::operator new / placement new), an amortized-O(1) growth strategy, correct deep-copy value semantics, and cheap move semantics.

You must manage the storage by hand — you may not use std::vector, std::unique_ptr, or new T[n] for the buffer itself. (You may use std::move, std::swap, and type traits.)

Constraints & Assumptions

  • C++17, single-threaded, no Allocator template parameter — use global ::operator new(bytes) / ::operator delete(ptr) for raw memory.
  • T may be non-trivially copyable/movable, and its constructors may throw.
  • API to support: default constructor, push_back(const T&) and push_back(T&&) , operator[] , size() , capacity() , reserve(n) , copy constructor, copy assignment, move constructor, move assignment, destructor.
  • Out of scope: iterators, insert / erase in the middle, shrink_to_fit . emplace_back with perfect forwarding is a bonus, not required.
  • Target: push_back is amortized O(1).

Clarifying Questions to Ask

  • Should allocation and construction be separated (reserved-but-unconstructed capacity, like the real std::vector ), or is a simpler "construct every slot" model acceptable?
  • What growth factor is expected — 2x, 1.5x — and do you care about the realloc/memory-reuse tradeoff?
  • Must push_back provide the strong exception guarantee across a reallocation?
  • Is T guaranteed nothrow-move-constructible, or must I handle a throwing move during relocation?
  • Any limits on standard-library usage beyond the storage (e.g. may I use std::move_if_noexcept , std::uninitialized_copy )?

Part 1: Memory model and capacity growth

Define the data members and implement raw allocation, reserve(n), and push_back with a geometric growth strategy. The key requirement: acquire raw, uninitialized memory separately from constructing elements into it.

What This Part Should Cover Premium

Part 2: Value and move semantics (Rule of Five)

Implement the destructor, copy constructor, copy assignment, move constructor, and move assignment so that Vector<T> has independent deep-copy value semantics and O(1) moves.

What This Part Should Cover Premium

Part 3: Element lifetime and exception safety

Make destruction and reallocation correct when T can throw. Implement the destructor, and ensure reserve/push_back neither leak nor corrupt the container if an element constructor or move throws mid-reallocation.

What This Part Should Cover Premium

What a Strong Answer Covers Premium

Follow-up Questions

  • Why might 1.5x be preferred over 2x as a growth factor for a general-purpose allocator?
  • How would you add emplace_back with perfect forwarding, and what does it save compared to push_back ?
  • What are the iterator/reference invalidation rules on reallocation, and how would they shape your API contract?
  • How would introducing an Allocator template parameter change where construction and destruction happen?
Loading comments...

Browse More Questions

More Software Engineering Fundamentals•More Sig•More Software Engineer•Sig Software Engineer•Sig Software Engineering Fundamentals•Software Engineer Software Engineering Fundamentals

Write your answer

Your first approved answer each day earns 20 XP.

Sign in to write your answer.
PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.