← Sig Interview Insights

Sig·Software Engineer·Technical Phone Screen·Senior

SeniorPrefer not to say
Jun 2026

Summary

Sig software engineer interview with a C++ focused technical round. Two scenarios, both designed to look like working code until you actually think about ownership and polymorphism. The kind of stuff that trips you up if you've been writing Python for six months.

Questions Asked (5)

Q1

You're given a C++ class called Buffer that owns a heap-allocated array via a raw pointer. It has a destructor but no copy constructor or copy assignment operator. Find all the bugs and explain what goes wrong when objects are copied or assigned.

Technical Trade-offsRoot Cause Analysis
Author's notes

The double-free is obvious once you see it but I fumbled explaining the rule of three clearly under pressure.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, identify the missing copy operations and explain the shallow copy problem leading to double-free and dangling pointers. Then, discuss the Rule of Three and propose correct implementations using deep copy or move semantics. Finally, mention modern alternatives like smart pointers to avoid manual memory management.

Pro tip: Emphasize that the root cause is the lack of resource ownership semantics, and demonstrate awareness of the Rule of Five and copy-and-swap idiom for exception safety.

1. Identify the missing operations

Point out that the class has a destructor but no copy constructor or copy assignment operator, violating the Rule of Three.

2. Explain the shallow copy problem

Describe how the compiler-generated copy operations perform shallow copies, copying the raw pointer value instead of the pointed-to data.

3. Analyze the consequences

Detail the resulting bugs: double-free when both objects are destroyed, and dangling pointers if one object is modified or destroyed while the other still references the memory.

4. Propose correct solutions

Suggest implementing deep copy semantics (copy constructor and copy assignment) or deleting copy operations and providing move operations (Rule of Five).

5. Discuss modern alternatives

Mention using smart pointers (e.g., std::unique_ptr or std::shared_ptr) to automate memory management and avoid manual resource handling.

Key Points to Mention

  • Rule of Three/Five: if a class needs a destructor, it likely needs copy/move operations.
  • Shallow copy vs. deep copy: default copy operations copy the pointer, not the data.
  • Double-free: both objects' destructors delete the same memory, causing undefined behavior.
  • Dangling pointer: after one object is destroyed, the other's pointer becomes invalid.
  • Copy-and-swap idiom for exception-safe assignment.
  • Smart pointers (unique_ptr, shared_ptr) as a modern alternative to raw pointers.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

A polymorphic Shape base class and a Circle subclass are stored by value in a std::vector<Shape>. What goes wrong, and how do you fix it?

Technical Trade-offsSystem Design
Author's notes

Object slicing.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Explain that storing polymorphic objects by value in a std::vector<Shape> causes object slicing, where the Circle's derived parts are lost, and virtual dispatch fails. Then present the fix: store pointers (preferably smart pointers like std::unique_ptr<Shape>) or use a container of references (e.g., std::reference_wrapper<Shape>) to preserve polymorphism.

Pro tip: Mention that even if you use pointers, you must ensure the base class has a virtual destructor to avoid undefined behavior when deleting derived objects. Also, consider the performance and ownership implications of your chosen fix.

1. Identify the problem

State that storing derived objects by value in a vector of base objects causes object slicing, where only the base part is copied.

2. Explain the consequences

Describe that slicing leads to loss of derived data and behavior, and virtual functions called on sliced objects will not dispatch to the derived implementation.

3. Propose the fix

Recommend storing pointers to the base class instead of values, such as std::vector<std::unique_ptr<Shape>>, to preserve polymorphism.

4. Discuss alternatives and trade-offs

Mention other options like std::reference_wrapper or a variant, and discuss ownership, lifetime, and performance considerations.

5. Highlight best practices

Emphasize the need for a virtual destructor in the base class and using smart pointers for automatic memory management.

Key Points to Mention

  • Object slicing: derived class data and behavior are lost when copied into a base class object.
  • Virtual dispatch fails because the object is no longer of the derived type.
  • Solution: store pointers (e.g., std::unique_ptr<Shape>) in the vector.
  • Base class must have a virtual destructor to safely delete derived objects.
  • Alternatives: std::reference_wrapper<Shape> if objects are stored elsewhere, or std::variant for closed set of types.
  • Consider performance and ownership semantics when choosing the fix.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

How does copy-and-swap achieve a strong exception guarantee, and what does it require from swap and the move constructor?

Technical Trade-offs
Author's notes

Swap has to be noexcept, and you construct the new value before modifying the old one so if construction throws you haven't touched the original.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Explain the copy-and-swap idiom step by step: first create a copy of the source object, then swap the copy with the current object, and finally let the copy's destructor clean up the old state. Emphasize that if any operation during the copy phase throws, the original object remains unchanged, thus providing the strong exception guarantee. Then discuss the requirements: swap must be noexcept, and the move constructor must be noexcept to avoid potential exceptions during reallocation or swapping.

Pro tip: Mention that the strong exception guarantee is only provided if the copy constructor provides it, and that swap should be a non-member function found via ADL to avoid unnecessary copies. Also, note that the move constructor is required to be noexcept for containers to use move semantics during reallocation, which is crucial for maintaining the strong guarantee in operations like vector::push_back.

1. Define the strong exception guarantee

State that the strong exception guarantee means that if an operation throws, the program state remains unchanged (no effects). This is also known as the commit-or-rollback semantics.

2. Describe the copy-and-swap idiom

Explain the typical implementation: create a temporary copy of the source object, swap the temporary with *this, and let the temporary's destructor release the old resources. This ensures that if the copy constructor throws, *this is untouched.

3. Explain how it achieves the strong guarantee

Highlight that all potentially throwing operations (copy construction, resource allocation) happen before any modification to *this. The swap operation is noexcept, so it cannot throw. Thus, either the operation succeeds completely or *this remains unchanged.

4. Discuss requirements on swap

Swap must be noexcept because if it could throw, the strong guarantee would be violated. It should also be efficient (constant time) and typically implemented as a non-member function to avoid unnecessary copies.

5. Discuss requirements on the move constructor

The move constructor must be noexcept to ensure that containers can use move semantics during reallocation without risking the strong guarantee. If the move constructor can throw, containers may fall back to copying, which could be less efficient and might not provide the strong guarantee.

Key Points to Mention

  • The strong exception guarantee ensures commit-or-rollback semantics: if an exception is thrown, the object remains unchanged.
  • Copy-and-swap works by performing all throwing operations on a temporary copy before modifying the original object.
  • swap must be noexcept; otherwise, the strong guarantee cannot be maintained.
  • The move constructor should be noexcept to allow containers to use move semantics during reallocation, preserving the strong guarantee efficiently.
  • The copy constructor must provide the strong guarantee for the idiom to work; if it only provides the basic guarantee, the overall guarantee is weakened.
  • Non-member swap found via ADL is preferred to avoid unnecessary copies and to support user-defined types.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

Why is deleting a derived object through a base pointer with a non-virtual destructor undefined behavior?

Root Cause AnalysisTechnical Trade-offs
Author's notes

Standard answer: the destructor called is determined statically, so only the base destructor runs, derived members don't get cleaned up, and the standard explicitly calls it UB.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by explaining the mechanism: when deleting through a base pointer, the compiler uses the static type to determine which destructor to call. If the destructor is non-virtual, only the base destructor runs, leaving the derived part un-destroyed. Then connect this to undefined behavior: the derived object's resources are not released, and the memory deallocation may use the wrong size or alignment, leading to heap corruption or crashes.

Pro tip: Mention that this is a classic example of why virtual destructors are essential in polymorphic base classes, and note that even if it seems to work in a simple test, it's still undefined behavior and can break with different compilers or optimizations.

1. Explain the deletion mechanism

Describe how delete expression works: it calls the destructor based on the static type of the pointer, then deallocates memory. With a non-virtual destructor, only the base destructor is invoked.

2. Identify the missing derived destructor

Point out that the derived class destructor is never called, so any resources it owns (memory, file handles, etc.) are leaked, and its specific cleanup logic is skipped.

3. Connect to undefined behavior

Explain that the C++ standard explicitly states that deleting a derived object through a base pointer with a non-virtual destructor is undefined behavior. This is because the deallocation function may not know the correct size or alignment of the derived object.

4. Discuss practical consequences

Mention that in practice, this can cause heap corruption, crashes, or silent resource leaks. The behavior may vary across compilers and platforms, making it unpredictable.

5. Provide the solution

Conclude that the fix is to declare the base class destructor as virtual, ensuring the correct derived destructor is called and proper deallocation occurs.

Key Points to Mention

  • Static vs. dynamic type of the pointer
  • Virtual destructor ensures derived destructor is called
  • Undefined behavior per C++ standard
  • Resource leaks and heap corruption
  • Deallocation size and alignment issues
  • Best practice: virtual destructor in polymorphic base classes

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

When would you make Buffer move-only instead of deep-copyable, and how would that change the call sites?

Technical Trade-offsAlgorithms & Data Structures
Author's notes

Delete the copy constructor and copy assignment, keep or default the move versions.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining the trade-off: move-only types eliminate accidental copies and enforce unique ownership, but restrict flexibility. Then explain scenarios where move-only is beneficial (e.g., resource-owning types, performance-critical code) and how call sites must change to use std::move and avoid copies. Finally, discuss the impact on APIs and error handling.

Pro tip: Mention that move-only types can improve performance and safety, but also consider the ripple effect on existing code and the need for clear documentation. Also, note that move-only doesn't mean immovable—it can still be moved, which is often sufficient.

1. Clarify the trade-off

Explain that move-only types prevent copying, which can avoid expensive deep copies and enforce unique ownership, but at the cost of reduced flexibility.

2. Identify when to choose move-only

Discuss scenarios such as managing unique resources (file handles, sockets), large data structures where copying is expensive, or when you want to enforce single ownership semantics.

3. Describe call site changes

Explain that call sites must use std::move to transfer ownership, and that functions taking the type by value will require an rvalue. Also, APIs may need to change to accept rvalue references or return by value.

4. Address error handling and exceptions

Mention that move-only types can affect exception safety and error propagation, as you cannot copy to roll back. Consider using smart pointers or optional for nullable move-only types.

5. Summarize with a concrete example

Provide a brief example, such as a Buffer class that owns a large array, and show how making it move-only changes a function that previously took it by value.

Key Points to Mention

  • Move-only types prevent accidental copies and enforce unique ownership.
  • Use std::move at call sites to transfer ownership, leaving the source in a valid but unspecified state.
  • APIs may need to change to accept rvalue references (T&&) or return by value to support move semantics.
  • Consider the impact on exception safety and the need for clear documentation.
  • Move-only types are common in resource management (e.g., std::unique_ptr, std::thread).
  • Performance benefits: avoid deep copies, especially for large buffers.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.