A strong answer
A strong candidate frames Why does a class with private data protect its invariant better than a struct? around the exact memory layout, compiler semantics, and hardware execution model covered in Classes and Access Control: Building Your First Type. Explain access control, invariants, and destructor timing for a first class type.
Anyone can set salary to a negative number, id to zero, or forget to set name at all. The struct holds whatever bytes you put in it and promises nothing. Every function that reads an employee must therefore distrust it: check the id, check the salary, handle the empty name. Miss one check in one function and corruption flows downstream.
A class solves this by splitting the world in two. Private members can only be touched by the type's own functions; public members are the doorway everyone else uses. The doorway functions preserve the invariant, the rule the object always satisfies: age sane, name non-empty, file open. If the only way in goes through the doorway, no caller can break the rule.
In a production interview, always connect the specification rule to its concrete runtime consequence: state what lives on the stack versus the heap or static storage segment, how the compiler or operating system validates alignments, lifetimes, and error codes, and what happens under adversarial or boundary inputs (such as zero-length buffers, integer overflow, concurrent mutation, or partial I/O). Backing your explanation with a small trace of register/memory state transitions immediately separates senior systems engineers from candidates who only memorized syntax.
What the interviewer is listening for
- Mastery of core invariant: Explain what private buys over a plain struct.
- Mastery of core invariant: Write a constructor that establishes the class invariant.
- Mastery of core invariant: Predict the lifetime of a stack object and when its destructor runs.
- Hardware and ABI awareness: Explaining how sizes, alignments, calling conventions, or kernel boundaries behave on real 64-bit targets rather than relying on implementation-defined assumptions.
- Defensive engineering: Naming the exact compiler flags (
-Wall -Wextra -Werror), static analyzers, or runtime sanitizers (-fsanitize=address,undefined,thread) that verify correctness in CI.
Common mistakes
- Explaining only surface syntax without tracing the underlying memory addresses, ownership lifetime, or kernel/user-space state transitions.
- Assuming undefined behavior (such as signed overflow, uninitialized reads, strict-aliasing violations, or dangling pointers) will "just work" because it passed an unoptimized
-O0debug build. - Ignoring return codes, partial reads/writes, alignment padding, or cleanup paths when an allocation or syscall fails midway through a function.