Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++ Module 7

Classes, objects and RAII interview questions

Interview questions on classes, objects and RAII: short warm-ups, then what each harder question tests, the first step of a good answer and its follow-ups.

29 questions: 10 warm-ups and 19 with model answers · Levels: Fresher to staff

Warm-up questions

Short questions with short answers, each answered in full.

  1. Fresher

    What does const after a member function's parameter list mean?

    Answered in the lesson C++ classes, member functions and access control

  2. Fresher

    Why should most constructors that take one argument be explicit?

    Answered in the lesson Constructors, member initialiser lists and explicit

  3. Fresher

    What does std::vector<int> v{3, 7}; contain, and how is that different from v(3, 7)?

    Answered in the lesson Brace initialisation and initializer_list traps

  4. Fresher

    What does RAII mean, and what does it guarantee?

    Answered in the lesson Destructors and RAII in C++

  5. Fresher

    What are the rule of zero, the rule of three and the rule of five?

    Answered in the lesson The rule of zero, three and five in C++

  6. Fresher

    Why is operator+= usually a member function while operator+ is a non-member?

    Answered in the lesson C++ operator overloading and the spaceship operator

  7. Fresher

    What is the difference between writing operator<< for a type and specialising std::formatter for it?

    Answered in the lesson Printable types: operator<< and std::formatter

  8. Fresher

    How does a static member function differ from an ordinary member function?

    Answered in the lesson C++ static members, friends and nested types

  9. Fresher

    What does this auto&& self deduce for a non-const lvalue, a const lvalue and an rvalue object?

    Answered in the lesson Deducing this: explicit object parameters in C++23

  10. Fresher

    What is a strong type, and why is using Paise = std::int64_t; not one?

    Answered in the lesson Designing value types and strong types in C++

Questions with model answers

What each question tests, the first step of a good answer and the follow-ups to expect.

  1. Fresher to mid

    An Account class exposes setBalance(long long) and getBalance(), and a production bug left one balance negative. How would you redesign the class so that this cannot happen again, and how would you move hundreds of existing callers to the new interface safely?

    What it tests
    Designing an interface around an invariant, preferring operations to setters, and migrating many callers in steps without breaking the build.
    First step
    State the invariant first: the balance is never negative and changes only through deposits, withdrawals and transfers that check their arguments before changing anything. Then list every caller of setBalance and group the calls by what they mean, because each group becomes one named operation.
    Follow-ups

    From the lesson C++ classes, member functions and access control

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.

    Opening the model answers with your pass…

    Model answers open with any MySmartCoPilot pass.

    • Pro pass · 1 week₹79$2.99
    • 30 days₹179$5.99
    • 1 year₹1,699$45

    One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

    It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  2. Mid to senior

    A function takes const Order& and fails to compile because it calls order.total(), which is not const. A teammate proposes const_cast to make it build. What do you do instead, and when is const_cast actually safe?

    What it tests
    Reading the const error correctly, const member functions, mutable caches and their thread safety, and why writing to a const object through const_cast is undefined behaviour.
    First step
    Read what total() does. If it only reads, the error is a missing promise: mark total() const, which is the whole fix. If it writes a cached result, the cache does not change what the order represents, so that one member may be declared mutable.
    Follow-ups

    From the lesson C++ classes, member functions and access control

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  3. Mid

    Sensor derives from Labelled and initialises it with one of its own members: Sensor(const std::string& id) : id_{"sensor-" + id}, Labelled{id_} {}. Every label comes out empty, and AddressSanitizer reports nothing. Explain why, and give two fixes.

    What it tests
    The order in which bases and members are initialised, why reading a member before its construction is undefined, which warnings catch it, and the base-from-member idiom.
    First step
    Base classes are initialised before any member, whatever the order of the list, so Labelled{id_} copies a std::string that has not been constructed yet. That read is undefined behaviour; an empty label is one possible result, not a guarantee.
    Follow-ups

    From the lesson Constructors, member initialiser lists and explicit

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  4. Mid

    A logging API has log(bool flag) and log(const Message& m), and Message has a non-explicit constructor from std::string. The call log("disk full") prints "flag: true". Explain why the compiler chose that overload without a warning, and redesign the API so the mistake cannot happen.

    What it tests
    Ranking standard against user-defined conversions, the limit of one user-defined conversion, pointer-to-bool conversion, and API design with exact-match overloads, strong types and explicit.
    First step
    A string literal decays to const char*, and a pointer converts to bool by a standard conversion. Reaching Message needs two user-defined conversions, const char* to std::string and std::string to Message, and an implicit conversion may use only one, so log(const Message&) is not even viable.
    Follow-ups

    From the lesson Constructors, member initialiser lists and explicit

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 2 minutes
  5. Mid

    A helper std::initializer_list<int> default_limits() { return {1, 2, 3}; } passes every unit test, but a release build sums the limits to 0 instead of 6. Explain why, say what the compilers and tools report, and give two safer designs.

    What it tests
    The lifetime of the array behind an initializer_list, why undefined behaviour passes debug tests, default warnings and sanitizer options, and owning alternatives.
    First step
    The braces create a temporary array that lives only until the return statement finishes, and the returned initializer_list is just a pointer and a length into that dead array. Reading it is undefined behaviour, so a debug build may still find the old values while an optimised build does not.
    Follow-ups

    From the lesson Brace initialisation and initializer_list traps

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  6. Senior

    You maintain a widely used Matrix class with a Matrix(int rows, int cols) constructor and want to add a constructor that takes a brace list of values. What can break for existing users, how do you find it, and how would you design the API to avoid the problem?

    What it tests
    API evolution with list initialisation, how a new initializer_list constructor silently changes existing brace calls, and safer designs.
    First step
    Every existing Matrix m{3, 4}; would switch from the rows and columns constructor to the new list constructor without any compile error, because two ints fit a list of doubles. Calls with parentheses keep their meaning, so the brace calls are the ones to find.
    Follow-ups

    From the lesson Brace initialisation and initializer_list traps

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  7. Mid

    A review finds std::lock_guard<std::mutex>{mtx}; at the top of a function that updates shared data, yet a data race shows up under load. Explain why the lock protects nothing, give the fix, and say how you would stop this bug from coming back.

    What it tests
    Temporary lifetimes, RAII objects that must be named, and catching the mistake with warnings, linters and thread sanitizers.
    First step
    The guard has no name, so it is a temporary, destroyed at the end of its full expression: the mutex is unlocked before the next line runs. Give it a name, std::lock_guard guard{mtx};, so that it lives until the end of the scope.
    Follow-ups

    From the lesson Destructors and RAII in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  8. Mid to senior

    A service sometimes crashes after main returns. A global Cache in cache.cpp writes a summary through a global Logger from logger.cpp in its destructor, and the crash appeared after a change to the link order. Explain the cause and give two fixes, with their costs.

    What it tests
    Initialisation and destruction order of globals across translation units, why it depends on the build, and the construct-on-first-use and never-destroyed fixes.
    First step
    Globals in different .cpp files are initialised in an order the standard leaves unspecified, in practice often the link order, and destroyed in the reverse order of their construction. If the Logger was built after the Cache, it is destroyed first, and the Cache destructor uses a dead object.
    Follow-ups

    From the lesson Destructors and RAII in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  9. Fresher to mid

    After a refactor that stores Image objects in a std::vector<Image>, the program crashes with a double free. Image holds a raw unsigned char* from new[] and deletes it in its destructor. Diagnose the crash and give two fixes, with their trade-offs.

    What it tests
    Recognising a rule-of-three violation, how a vector copies its elements, AddressSanitizer, and choosing between the rule of five and the rule of zero.
    First step
    The compiler-generated copy constructor copies the pointer, and the vector copies Image objects on insertion and when it grows, so two Images end up deleting one buffer. AddressSanitizer confirms it: both the first free and the double free happen in ~Image.
    Follow-ups

    From the lesson The rule of zero, three and five in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  10. Mid to senior

    A Shape base class declares only virtual ~Shape() = default; and a name string. Growing a std::vector<Circle> copies every element, and Shape s = circle; compiles and loses the circle's data. Explain both problems and write the special member functions the base class should declare.

    What it tests
    How a user-declared destructor in a base removes its moves, why derived moves then copy and may throw, slicing, and protected copy and move operations.
    First step
    The virtual destructor is user-declared, so Shape has no implicit move operations. Circle's implicit move constructor must then copy the Shape part, which can throw, so Circle's move is not noexcept and the vector copies on growth. The slicing comes from Shape's public copy constructor.
    Follow-ups

    From the lesson The rule of zero, three and five in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  11. Mid to senior

    During a C++20 migration, the six hand-written comparison operators of a Customer class, which compared names without regard to case and ignored a cached hash member, were replaced by one defaulted operator<=>. Unit tests now fail and a std::set of customers holds near-duplicates. What changed, and how do you migrate safely?

    What it tests
    Knowing exactly what a defaulted <=> compares, choosing the comparison category, and changing operators without changing behaviour.
    First step
    Write down what the old operators compared and what the defaulted one compares: every member in declaration order, the cached hash included, with std::string's case-sensitive <=>. Both the members and the kind of order changed, so the fix is a hand-written <=> returning std::weak_ordering, with a matching ==, checked against the old behaviour.
    Follow-ups

    From the lesson C++ operator overloading and the spaceship operator

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  12. Mid to senior

    A rules engine overloads && and || on a Condition type so that rules read like has_user(req) && is_admin(req). After a refactor that made both functions return Condition, rules crash on requests without a user, although the same rule on bools was safe. Explain the crash and propose a design that keeps rules readable.

    What it tests
    Short-circuit evaluation, why overloaded logical operators lose it, and designing lazy predicate objects instead of eager results.
    First step
    With Condition operands, the expression calls operator&&(has_user(req), is_admin(req)), and every argument of a function call is evaluated before the call, so is_admin(req) runs even when there is no user. Keep the readable syntax but make the operands lazy: combine predicates that are evaluated later with the built-in &&.
    Follow-ups

    From the lesson C++ operator overloading and the spaceship operator

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  13. Fresher to mid

    A Money type prints through a std::formatter whose parse() accepts only {}. A teammate writes std::println("{:>12}", amount) to right-align a column of amounts, and it does not compile, although the same placeholder works for a double. Why, and how do you support fill, alignment and width without reimplementing them?

    What it tests
    What parse() decides, composing a standard formatter instead of rewriting its options, and what a width counts.
    First step
    parse() alone defines which specifications exist. This one returns ctx.begin() while it points at >, so the library reports an unparsed specification while compiling. To get fill, alignment and width, format the amount into a string first and give that string to a std::formatter<std::string_view> member that parsed the specification.
    Follow-ups

    From the lesson Printable types: operator<< and std::formatter

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  14. Mid to senior

    A reporting service lets customers save their own message templates, such as "Total {:in} for {}", which the server fills in with the C++ formatting library. Code review blocks the change. What can go wrong with format strings that users supply, and how would you build the feature safely?

    What it tests
    Run-time format strings and std::vformat, exceptions, resource limits such as huge widths, and choosing a template language that exposes only what customers need.
    First step
    A stored template is known only at run time, so the compile-time check cannot help: it must go through std::vformat, which throws std::format_error for a bad template and obeys any width, even {:1000000000}. Treat the template as untrusted input: validate it when it is saved, limit what it may ask for, and catch errors when it is used.
    Follow-ups

    From the lesson Printable types: operator<< and std::formatter

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  15. Mid to senior

    In a large code base, namespace app defines about 300 types, each with operator== and operator<< declared at namespace scope. Builds are slow, one failed comparison prints hundreds of lines of candidates, and two unrelated types once compared equal through an implicit conversion. Explain the common cause and how hidden friends address it.

    What it tests
    How overload resolution collects candidates, argument-dependent lookup, why hidden friends shrink the candidate set, and migrating operators without breaking callers.
    First step
    Every operator== declared at namespace scope in app is a candidate for every == written in that namespace, so the compiler checks each of the 300 against the operands, lists them all when none fits, and accepts any that an implicit conversion makes viable. A hidden friend is found only through an operand of its own class.
    Follow-ups

    From the lesson C++ static members, friends and nested types

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  16. Mid to senior

    An IdGenerator hands out order ids with a static std::uint32_t counter and return ++counter_;. Under load, two orders occasionally get the same id. Explain why, fix it, say what your fix costs, and name what else could still give two orders the same id.

    What it tests
    Data races on static members, atomic read-modify-write operations and memory order, and the limits of an in-process counter: overflow, restarts and several processes.
    First step
    ++counter_ reads, adds and writes back in separate steps, so two threads can read the same value and return the same id; formally it is a data race, which is undefined behaviour. Make the counter a std::atomic and take ids with fetch_add, which performs the whole step as one indivisible operation.
    Follow-ups

    From the lesson C++ static members, friends and nested types

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  17. Mid to senior

    Box::value() is written once with an explicit object parameter and returns std::forward_like<Self>(self.value_). Code such as const std::string& name = make_box().value(); now crashes, while std::string name = make_box().value(); works. Explain both lines, and how you would design value() for rvalue objects.

    What it tests
    Lifetimes of temporaries, when binding a reference extends a lifetime, what forward_like returns for an rvalue object, and returning by value from rvalue-qualified accessors.
    First step
    make_box() returns a temporary Box that dies at the end of the full expression. For that rvalue, value() returns a std::string&& into the temporary, and binding a reference to a function's result does not extend the temporary's life, so name dangles. The second line moves the string into a new object before the Box is destroyed.
    Follow-ups

    From the lesson Deducing this: explicit object parameters in C++23

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  18. Senior to staff

    A team proposes to replace both a CRTP mixin and a virtual plugin interface with C++23 explicit object parameters, to modernise the code base. Which of the two can it replace and which not, and what would you check before you approve the change?

    What it tests
    Static against dynamic polymorphism, deduction from the static type at each call, and the effect on headers, ABI, build time and call sites.
    First step
    Separate the two uses. The CRTP mixin only needs the derived type while compiling, which an explicit object parameter deduces directly. The plugin interface chooses a function at run time from the object's real type, which deduction cannot do, because self is deduced from the type the compiler sees at the call.
    Follow-ups

    From the lesson Deducing this: explicit object parameters in C++23

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  19. Mid to senior

    A Date class throws std::invalid_argument from its constructor for impossible dates. A CSV importer that reads millions of rows, about one in five with a bad date, is now slow and full of try blocks. How would you redesign the API, and what would you keep?

    What it tests
    The cost of throwing, expected failures against exceptional ones, factories returning std::expected, and keeping one validation rule behind every way of creating a Date.
    First step
    Separate two kinds of caller. A bad date in a file is ordinary input, so the importer needs a factory that returns std::expected<Date, DateError> and never throws. Code that builds dates from values it controls can keep a throwing constructor, as long as both paths share one validation function.
    Follow-ups

    From the lesson Designing value types and strong types in C++

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes

All C++ interview questions · The C++ track

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.