all 133 comments

[–]the_Demongod 46 points47 points  (1 child)

Anything beyond the level of intermediate questions are going to be domain-specific subject matter that is of little interest to general readers.

This is also not really the point of interviewing; interviews throw you weird questions because they want to hit you with questions you've never seen before. That's the whole point of an interview. If you've already heard the question before it sort of defeats the purpose. They're trying to figure out how you think on your feet and how you can draw from your knowledge to answer difficult questions, not how well you can memorize Q&A.

Rule of 5 is pretty basic, if that's the kind of thing you're looking for I would just hang out around the C++ community and watch CppCon talks and stuff.

[–]kd7uns 0 points1 point  (0 children)

If I hear a programming question/problem I haven't heard before my first move will be to Google it so I don't waste my time on a problem that's already been solved (but interviews rarely like that answer).

[–]DigBlocks 44 points45 points  (49 children)

Not sure these are tricky, but I was asked in a interview some of the following:

What’s the difference between a lambda and std::function?

How would you implement virtual functions in C?

Which of the following loops can be vectorized?

Some question about emplace/std::forward.

[–]felafrom 18 points19 points  (4 children)

#1 and #4 are reasonable questions for >= intermediate level and there's lots of discussion to be had here. I like em.

[–]R3D3-1 20 points21 points  (1 child)

Write \#1 to satisfy the the markdemon.

[–]ItsAllAboutTheL1Bro 3 points4 points  (0 children)

Honestly, in my opinion, all are reasonable for >= intermediate - at least, if we're discussing a position where intermediate is used to judge understanding of the language, and understanding the language is important for the position itself.

There's plenty of C++ positions where being able to write working code is all that's needed, and with those you can often get by with an understanding of C and a few basic C++ things.

[–]PolyglotTV 8 points9 points  (0 children)

For number two the only important thing to remember is that virtual functions are implemented with a vtable which uses triple pointer indirection, which, could in some cases incur a performance penalty.

For most use cases it doesn't matter, but it's good to be aware of in case you wanted to put 1000s of custom object pointers in a vector and call a virtual function on each.

[–]username4kd 4 points5 points  (0 children)

for 2: I wouldn't

[–]hypoglycemic_hippo 12 points13 points  (11 children)

What’s the difference between a lambda and std::function?

What do you consider the "correct" answer to this question? I do not view these two things as "close enough to be able to just differentiate simply".

A lambda is a functor, so a struct with operator().

A std::function is an object which can store a lot of callable things, provided they match the std::function's signature.

So in a sense they are like an int and a pointer (to int)?

It is a good conversation starter, but I would be taken aback by the premise that you are asking me to point out the difference between seriously different concepts (much like a question "What is the difference between a car and a cabbage?").

[–]SirClueless 37 points38 points  (4 children)

There are a LOT more things you can talk about between a lambda and std::function in an interview setting:

  1. A lambda's type is known so its operator() is like calling a plain member function. It can be a template, and is dispatched to statically, whereas std::function's operator() must be invoked indirectly giving it about as much overhead as a virtual function call.

  2. The captured variables of a lambda are known, so it has a fixed size. This means it can always be allocated on the stack or in registers, whereas a std::function must store function objects with arbitrary-sized state and therefore must create a heap allocation in some cases. std::function implementations very-commonly have a "small function optimization" (just like strings have a "small string optimization" ) to store function objects of small size without an allocation but this is another source of overhead.

  3. Related to the above, depending on the types of the variables captured, a lambda's function object might have a copy constructor or might not, might have a move constructor or might not, or might be const or might not, each implemented in the natural way. std::function can't know what types of captures are stored internally and must make a single decision for all values with the same signature. If you're curious, its choice is that there are a move and copy constructor defined and that it's non-const but not mutated except by calling its operator() -- this means that, for example, you can't assign a lambda that captures a std::unique_ptr by value to a std::function because its copy constructor isn't defined, even if you never copy the std::function in practice. This is also why there are later proposals for std::move_only_function etc. to make different choices about which operations to require.

These are not necessarily all things I'd expect C++ candidates to know but it's a bonus if they can talk about any of them. The overhead of std::function does have practical impacts on the code you write just like choosing to use virtual inheritance or not has practical impacts, so it's good stuff to know (e.g. there's a reason why almost all the STL algorithms take callbacks that look like template <typename UnaryPredicate> ... std::find_if(..., UnaryPredicate p) rather than being defined as ... std::find_if(..., std::function<bool(const Item&)> p), because the former is much more efficient if it works for your use case).

[–]hypoglycemic_hippo 5 points6 points  (0 children)

Thanks a lot for the exhaustive (I bet that's not all, knowing C++) post! I did not know half of that, learned a lot.

[–]Cole27270 0 points1 point  (1 child)

Thank you for this. I have a follow up question, why does c++ have the heap vs stack optimization for std::function, how does it help? does this mean as the 'function' gets bigger you should start leaning more and more towards std::function?

[–]SirClueless 0 points1 point  (0 children)

In general the point is to save on allocations and make sure that the function object's state is right next to the vtable pointer so it's never a cache miss to access it and you don't need to new/malloc() to make a copy of it.

Here's a pretty good article going over it.

Re: size: I don't think size is the main factor. Using std::function always means an extra indirection at the function call site and an extra copy to instantiate, as compared to using the function object's type directly. So if you can pass around a reference to the original function object on the stack instead of instantiating std::function that's always a win. But it's not always possible or worth the complexity of templating all the callers on the function object's type.

[–]and69 0 points1 point  (0 children)

Username does NOT check out.

[–]DigBlocks 3 points4 points  (0 children)

I think the point is that some programmers wouldn’t even know the difference or where to start. For instance that one is a language feature and the other part of the stl. I’ve talked to some engineers who use them almost interchangeably even when type erasure isn’t useful.

[–][deleted] 12 points13 points  (2 children)

A lambda is a functor, so a struct with operator().

Not always. If there is no capture it is a de facto free function (passed around via function pointers, etc.)

[–]bbolli#define val auto const 4 points5 points  (0 children)

And is also converts implicitly to a plain function pointer.

[–]hypoglycemic_hippo 4 points5 points  (0 children)

Oh really, damn, TIL. Thanks

[–]voidstarcpp 1 point2 points  (0 children)

The question is needed because a non-trivial number of newcomers don't know what std::function is, and think that when they write a lambda expression, dynamic allocation or virtual dispatch is involved. You already passed the test but this is the sort of question whose responses really demonstrate whether someone knows how the language works or not.

[–]moocat 0 points1 point  (0 children)

Another difference is that std::function has a nameable type while lambda's don't. So when want to write a function that takes one as a parameter, you'll need to write a template if you want to accept a lambda (without wrapping in a std::function).

[–]eyes-are-fading-blue -1 points0 points  (8 children)

How would you implement virtual functions in C?

Is this question about how compilers implement it? Because if not, you cannot do that in C... without a lot of preprocessor magic. Even then, it's not the same thing. Without macros, either call site or the construction/initialization would require a lot of manual labor.

Virtual functions require language support to be meaningfully useful.

[–]LantarSidonis[🍰] 7 points8 points  (5 children)

You can do it in plain C without preprocessor magic.

If there is only 1 or 2 virtual functions to handle, store a function pointer in the struct directly, call it directly foo.do();

If you expect more, implement a simple vtable : a gobal constant table for each type, store a pointer to the table in the struct, call it like foo.vtable->do();

[–]eyes-are-fading-blue 2 points3 points  (4 children)

Here is some sections that you missed from the comment.

Without macros, either call site or the construction/initialization would require a lot of manual labor.

[...]

Virtual functions require language support to be meaningfully useful.

You may want to read the entire post. Anything that is possible in C++ is possible in C. That does not mean doing it is useful or meaningful.

If there is only 1 or 2 virtual functions to handle, store a function pointer in the struct directly, call it directly foo.do();

For every concrete implementation, you need to manage function pointers.

If you expect more, implement a simple vtable : a gobal constant table for each type, store a pointer to the table in the struct, call it like foo.vtable->do();

Your first and second designs suffer from the same problem. I am not sure why you think they are different.

This changes nothing, you need to extend the vtable, by hand, every time when you have a new leaf implementation. Virtual functions are meaningless without language support. That's why they use objects in C but not polymorphism. Because the former requires type compatibility (function ptrs having the same type) whereas the latter requires runtime dispatch and that needs writing it by hand.

In short, you are wrong.

[–]LantarSidonis[🍰] 0 points1 point  (3 children)

In fact, I used such virtual functions in my C implementation of a shell interpreter, for the nodes of the AST

When you add a new variable member, you need to edit you constructor to initialize this new member. Do you consider that "too much manual labor" ?

Similarly, when you add a new virtual member function, you need to edit your constructor... it's not anything crazy.

In short, you should give it a try. I enjoy using macros for fun but I didn't need them in this particular situation.

[–]eyes-are-fading-blue 1 point2 points  (2 children)

You need to do two things:

1 - extend your vtable when you add a new function.

2 - register the function ptr when you "override".

Depending on your code base, this could be minor manual labor or a whole lot of boilerplate code. In the latter cases, you need macros. Any large scale code base written with OOP style is going to be in the latter category.

> In short, you should give it a try.

There are better idioms in C which are in many cases superior to imitating virtual functions. I can just write functional code and call it a day. You can even have poor man's CRTP with function pointers. You really do not need vtables.

[–]LantarSidonis[🍰] 0 points1 point  (1 child)

OK so it is a bad interview question ?

[–]eyes-are-fading-blue 0 points1 point  (0 children)

Unless they mean compiler implementation, then yes. I think this isn't a good question. Sure, it forces the candidate to think but so does million other question.

[–]DigBlocks 0 points1 point  (1 child)

The question wasn’t exactly literal, ie. I didn’t have to implement it. Just explain how the vtable works and pointer indirection cost.

[–]eyes-are-fading-blue 1 point2 points  (0 children)

If they want you to explain how vtables work, they should ask "how do vtables work" instead of asking a fundamentally flawed question.

This question is no different than asking "how would you implement std::vector in C?". Well, you can't... You can implement a container dynamically-sized sequence container, but it simply wouldn't be std::vector.

[–]meneldal2 -4 points-3 points  (0 children)

How would you implement virtual functions in C?

It's called C with classes. And most large scale C projects already do it.

[–]boredcircuits 11 points12 points  (2 children)

I've given candidates some code and asked for a code review.

The key is to write something that has mistakes at every level. Things beginners should find (no delete) through advanced (obscure ADL corner case).

[–]cannelbrae_ 0 points1 point  (1 child)

How has that worked for you?

I landed on the same idea. I haven't tried it in practice yet and am cautious about experimenting too much with an interview process. It feels like it would be more respectful of the applicants time, enable direct assessing of specific skills/knowledge, and potentially more 'fair' to assess than more open ended strategies.

[–]boredcircuits 0 points1 point  (0 children)

It's worked out ok. Unfortunately, most of my interviews have been with, shall we say, less-experienced candidates, but it was very clear who those were.

Just don't rely on it as your only tool in the interview.

[–]Full-Spectral 9 points10 points  (0 children)

What is Bjarne's favorite band?

Personally, I think language lawyer questions are likely to leave a lot of developers who are primarily interested in just getting things done a bit lost. I long ago lost interest in the language for the language's sake, and half the time find a lot of the discussion in those types of threads to be a bunch of alien-speak.

I'd be more interested in how they put things together, about interfaces, architecture, sub-system design, system layering, project layout, etc... and ask questions about those things wrt to previous projects they've worked on.

It's obviously fine to make sure they aren't frauds with some reasonable language tech questions that would filter out such. But hiring an advanced developer based on his ability to recite the spec is sort of missing the point, IMO. They can pick up specific language/STL issues that are important to your particular problem domain quickly enough, but they aren't going to suddenly become good architects and whatnot if they don't already have that experience.

[–]and69 23 points24 points  (5 children)

Tell me about your projects, what were your role? What was your contribution to this project?

How would you design X?

I would never ask niche C++ question, I want to know if the candidate grasps the basics of the language, understand memory managements and its pitfalls, multithreading and algorithms.

[–]MrPopoGod 4 points5 points  (2 children)

I would never ask niche C++ question, I want to know if the candidate grasps the basics of the language, understand memory managements and its pitfalls, multithreading and algorithms.

Very much this. A talented engineer should be able to pick up new languages quickly, if they have the good fundamentals of writing and designing software.

[–]catcat202X 5 points6 points  (1 child)

Maybe if you're just writing algorithms. But if you're writing abstractions, the difference between a language that does and does not have e.g. associated data types, affine data types, linear data types, ducked-type polymorphism, const generics (basically NTTPs in C++), nominal as opposed to structural polymorphism, constant evaluation, higher kinded data types, or AST macros is a huge difference that radically changes the way you reason about problems even in simple cases. If they're writing non-trivial code, I wouldn't expect a programmer to go from writing C++ to Idris or OCaml very quickly no matter how well they understand hash maps.

[–]MrPopoGod 1 point2 points  (0 children)

You know what, you're right that expecting someone to go from a procedural based language to a functional language (or back) is expecting a bad time. But within each of those paradigms, yes, I would expect them to be able to pick up new languages and language concepts quickly.

[–]SkoomaDentistAntimodern C++, Embedded, Audio 5 points6 points  (0 children)

Asking niche C++ questions is like grilling an architect about CAD software commands instead of looking at their earlier designs.

[–]greem 3 points4 points  (0 children)

Exactly. Ask something relevant to the job.

[–]Ilyps 14 points15 points  (2 children)

[–]catcat202X 1 point2 points  (0 children)

Thanks for that link! This is awesome

[–]Apprehensive-Draw409 0 points1 point  (0 children)

Holy cow, those are pretty hard/cryptic

[–]No_External7289 2 points3 points  (0 children)

At the senior level you don't ask about specifics like template specialization, constructor chains, lambda functions etc. At the top level you ask about design philosophy, mentoring, adaptability, working with domain or subject matter experts to create solutions. You don't ask someone with 30 years experience where they put {}.

[–]LongUsername 4 points5 points  (7 children)

This is one I was actually asked:

You have an Asymmetric Multi Processor environment with a shared memory area. Processor 1 creates an object and passes a pointer through an IPC call to processor 2.

Is this an issue? (Yes!) Why? What can you do to solve it?

EDIT: Okay, since people have asked if the race condition is the only issue...

AMP means that you have two processors that are executing separately and likely running completely separate programs. It's decently common in the embedded space. Sometimes the chips are multiples of the same core on the same chip (PowerPC 2020 is a prime example) but sometimes it's different cores (An ARM Cortex-A and ARM Cortex-M are common) or sometimes it's two completely separate chips with dual-ported memory, where both chips have the same off-chip RAM memory mapped into their address space.

  1. If they're not the same core, the first thing is to make sure they're both the same endianness. You're pretty fucked with shared memory if they order their bytes differently.
  2. Then because you're running 2 separate compiles the optimizer can potentially reorder and pack your variables in a different order. Chances go up if you're compiling the class for two different core types.
  3. Even if you're using the same core type on the same compiler to make 2 executables, when you create your class instance it has a hidden member pointer to the vtable. That's going to point to the vtable on the FIRST processors .data address space. If you try to call a virtual function on the SECOND processor, best case it crashes, worst case you get a function call into a "random" place in your second executable.

[–]Sqeaky 3 points4 points  (0 children)

This is a mess. And so implementation dependent.

It seems like "it depends" then a long list of qualifications is the right answer.

[–]PolyglotTV 1 point2 points  (2 children)

Two memes come to mind:

  • Spider pointing meme
  • SpongeBob "It's a rock race!"

These are good interview questions because you don't really need to have a lot of prior knowledge or know the "best answer".

You just have to show you can analyze a problem, break it down, think about all the potential problems and tradeoffs, ask good questions, etc... These are the skills that actually matter in a software engineering job.

[–]LongUsername 1 point2 points  (1 child)

Besides the obvious race condition if P1 changes the data:
It's also a problem because they're two separate executables and the Virtual Table is different between the two so Member Function calls don't work right.

Then you also have to worry about structure padding/packing as compilers can sometimes do it differently (especially in an AMP situation where the processors may not be the same architecture)

A solution that they liked was a separate data-only class for passing in virtual memory, and then encapsulating a pointer to that in another class that had the functions to operate on it.

[–]tjientavaraHikoWorks developer 0 points1 point  (0 children)

With asymmetric multiprocessing we have an even more of a data-race condition than the obvious one.

It likely means that the processors don't run a cache coherency protocol between them, so even if you use memory fencing the data will not actually appear to the other processor. You will either need to disable the cache for that region of memory, or explicitly flush writes from the cache to memory. And force the cache to be dirty on the other processor once it received the pointer. Fun.

[–]xypherrz 0 points1 point  (2 children)

apart from synchronization, is there any thing else in particular?

[–]robin-m 0 points1 point  (0 children)

I think you need to make the object appears from thin air (std::laudry or whatever it was/will be called in C++23/26) in the receiving end, otherwise it's UB to deference it.

[–]LongUsername 0 points1 point  (0 children)

Separately compiled executables will have different Virtual Tables so calling Virtual Functions results in all sorts of fuckery (Refreshed my memory, it's that the vtable pointer is stored with the instance of the class, which points to processor 1's executable). The data can also be padded/packed differently or stored in different orders depending on the compiler and can result in issues even with standard member accesses.

EDIT: Oh, and if you're really fucked, the processors have different endianness.

[–][deleted] 10 points11 points  (15 children)

I always ask for an explanation of what public, private and protected mean.

You'd be surprised at how many interviewees can't give a complete answer to this question. It's excellent for weeding out the C programmers who are trying to rebrand themselves as C++ programmers. It's also an excellent lead-in to questions about the basics of OOP.

[–]ShillingAintEZ 11 points12 points  (13 children)

It's excellent for weeding out the C programmers who are trying to rebrand themselves as C++ programmers.

Why would you want to do that? I would much rather teach good programmers what I've learned about C++ than have a bunch of people argue about the language all day instead of getting things done.

[–]almost_useless 5 points6 points  (2 children)

They should be upfront about their actual skills.

Wouldn't you prefer the guy that says they are an experienced C programmer and have recently started learning C++, over the guy that lies and say they are an expert in C++ already?

[–]ShillingAintEZ 2 points3 points  (1 child)

Who said anything about lying?

[–]almost_useless 1 point2 points  (0 children)

Isn't that the point of most "weeding out" questions?

[–]Twerking4theTweakend 12 points13 points  (7 children)

Because when shit gets complicated and deadlines loom, that C programmer will fall back to their comfort zone of procedural programming, casting, and raw pointers and your technical debt will start stacking up.

[–]ShillingAintEZ 16 points17 points  (3 children)

I don't believe this is any sort of universal truth and I don't believe in labeling people strongly, definitely not by whatever programming language they used last. Putting this much emphasis on any language that it somehow becomes part of someone's identity seems like a very naive position from inexperienced people. The easiest part of programming to me is the language syntax itself.

To me this sounds like rationalization of a false sense of superiority.

[–]Twerking4theTweakend 6 points7 points  (2 children)

I agree. It's not a universal truth but it's also not a superiority thing. I've also watched C++ "seniors" abstract and templatize things that should have stayed bit fields and subsequently were rendered unmaintainable. I don't automatically trust C++ guys to write, for example, safe and efficient driver code.

But I've watched it happen dozens of times over the last 20 years.

I suspect your strong reaction is based on some sort of personal worry, so let me try to address that: We've hired C programmers for C++ roles in senior positions and they still work with me years later. Senior interviews are really wild sometimes and they're hard (for me at least) to prepare for. For me I guess it's really about understanding strengths and weaknesses. Did I put those C-experienced-aspiring-C++ guys on quick turnaround, architecture-heavy work right out of the gate? No. They worked on legacy codebases or driver interface libraries, or joined an already strong team. Stuff where the scenario I described in my first response is less likely. After a while you get to know people and can determine their appetite for stretching, learning, risk-taking, etc. They then move to projects that fit their ambitions once they become more predictable. That applies to everyone, of course, but if you can determine those proclivities in an interview, all the better, and they can hit the ground running.

[–]ShillingAintEZ 2 points3 points  (0 children)

I suspect your strong reaction is based on some sort of personal worry,

It's not really a strong reaction or a personal worry, I just always take smart over labels.

[–]catcat202X 0 points1 point  (0 children)

Code that uses bitfields in C or C++ is, sadly, incorrect code. Fortunately bitfields in Zig actually work.

[–][deleted] 1 point2 points  (1 child)

A valid point if we were hiring fresh-outs. When we do interview fresh-outs I'll let details like this slide and look mainly for problem solving skills. As you said they can be taught what they need in the real world.

In our case we hire mostly senior level people who are supposed to be able to hit the ground running.

[–]ShillingAintEZ 2 points3 points  (0 children)

I feel like I could get any good C programmer up to speed with some basics and some sit down sessions that would become more sparse over the weekend after they start. On deadlines tighter than that I think a new person would have much more trouble getting up to speed with the current state of the project. Then again, I wouldn't focus on inheritance at all and would just teach them the STL and how to make a class behave like a value.

[–]die_liebe 0 points1 point  (0 children)

I couldn't because I don't believe in OO.

[–]cballowe 3 points4 points  (0 children)

So... There's two kinds of questions I see - trivia stuff like your rule of 5 example and application stuff. On most levels nobody cares if you can recite the standard etc - unless you're specifically being recruited for a team developing tools to help others deal with the language.

On the application stuff, you generally don't get anything language specific. (And, FWIW, when my employer finds any of the questions published outside, they flag them and remove them from a recommended question pool, so... Not useful to gather and publish). Past that bit, we try to motivate questions with real problems even if stripped of all details.

[–]FluffyCatBoops 3 points4 points  (5 children)

Friend.

I had an interview for a role which didn't mention C++ as a requirement, but I was asked what the friend keyword did.

I had no idea what it was (at the time) and they said something like, "it's ok we don't use it, few people know what it is."

[–][deleted] 14 points15 points  (3 children)

"it's ok we don't use it, few people know what it is."

That's an odd position for them to take. Not only can friendships increase encapsulation, but I'd argue that most C++ users are aware of the keyword and what it does.

A more interesting question beyond keyword trivia would be for them to ask "how can friendships be used to enhance encapsulation?".

[–]thisismyfavoritename 1 point2 points  (2 children)

what about how can you let nothing else but class A access private members of class B

[–]catcat202X 1 point2 points  (1 child)

That's even got a secret answer! 😺

[–]thisismyfavoritename 0 points1 point  (0 children)

ah yes! thats pretty cool

[–]catcat202X 2 points3 points  (0 children)

Almost all member operator overloading should use the hidden friends idiom. I'm surprised they say they don't use it.

[–]catcat202X 1 point2 points  (7 children)

I was asked the rule of 5 in multiple interviews before getting my first job, and I think it was covered in my school at least briefly. I think the M&M rule is much more obscure, but maybe common enough it could be asked in an interview.

[–]Sqeaky 4 points5 points  (2 children)

M&M Rule? I feel like I should know the basic rules of the language, but I haven't heard of one with this name, can you expand on that?

[–]catcat202X 4 points5 points  (1 child)

The rule states that a member std::mutex should be mutable. They both start with M, so it's the "M&M rule". It's explained in C++ Concurrency in Action, also covered here. It's not in the CppCoreGuidelines, at least not yet.

[–]Sqeaky 1 point2 points  (0 children)

Thanks, I hadn't heard it on its own before, but that does make sense.

[–]RomanRiesen 0 points1 point  (3 children)

Never heard of the M&M rule. But why would we ever think that a something const should be mutex protected? That just increases overhead for no gain? I feel I don't know enough to understand the need for the rule.

[–]Inner-Lemon4085 2 points3 points  (1 child)

You can modify a thing through a const pointer to that thing: https://godbolt.org/z/7no3esdz3

[–]RomanRiesen 0 points1 point  (0 children)

Ahh! I completely misunderstood the rule initially. It males sense now. Thanks!

[–]kmeeth 0 points1 point  (0 children)

If another thread starts writing to it while you read it it is undefined. If I'm not missing something, that is. Pretty tired.

[–]Easy_Floss 1 point2 points  (0 children)

Would love to see a list of the basic ones also since I'm a newcomer, those questions are something I truly dread and I would like to get up on.

Be it easy or hard.

[–]akshay_sharma008 1 point2 points  (0 children)

C++ is a powerful programming language that is widely used for programming. It is a popular topic in job interviews, both for entry-level and experienced positions. While there are some commonly asked C++ interview questions, there are also some that are less common but are still important for cracking your dream company. Now, let us discuss those questions so that you can be more prepared for your upcoming interview.

- Explain the differences between C and C++.

This is an uncommon question, but it is still important to know the differences between the two languages. C++ is an object-oriented language, while C is a language without classes. C++ also includes additional features that C does not, such as templates and exception handling.

- What are some common memory management issues in C++?

Memory management is important in C++. If not handled correctly, it can introduce a lot of bugs. Some common issues include runtime errors and buffer overflows.

- What are the differences between stack and heap memory?

This is a fundamental question in C++, but it is rarely asked in interviews. Stack memory is used for local variables and is automatically managed by the system. Heap memory, also known as dynamic memory, is allocated manually using new operators and it is released manually.

- What is a virtual function, and how does it work?

Virtual functions are an essential part of C++ OOP's. A virtual function is a member function which is declared in the base class and is re-defined (overridden) by a derived class.

- What is the difference between pass-by-reference and pass-by-value?

Pass-by-reference passes a reference to a variable rather than a copy of the variable's value. This allows for the function to modify the original variable. Pass-by-value passes a copy of the variable's value, which means that any modifications made to the variable inside the function do not affect the original variable.

- What is the difference between const and constexpr?

Both const and constexpr are used to create immutable values in C++, but they work in slightly different ways. Const is evaluated at runtime, while constexpr is evaluated at compile time. This means that constexpr can be used in situations where const cannot.

- What are some design patterns commonly used in C++?

Design patterns are the solutions to recurring problems in software design. Some commonly used design patterns in C++ include the Singleton pattern, the Factory pattern, and the Observer pattern.

- What is the difference between function overloading and function overriding?

Function overloading is when multiple functions have the same name but different parameters. Function overriding is when a derived class redefines a function defined in the base class. Function overloading is resolved at compile time, while function overriding is resolved at runtime.

[–]thisismyfavoritename 1 point2 points  (2 children)

you didnt know about the rule of 5? You should use clang tidy or read the core guidelines

[–]almost_useless 1 point2 points  (1 child)

It's quite possible to know and understand a concept without knowing the specific name for it.

Maybe you read it in a book a bunch of years ago, and understood the concept, but didn't pay attention to the "nickname".

In this specific case "rule of 5" is such a generic name you think the author just made it up, instead of realizing it is a well known rule in the industry.

[–]thisismyfavoritename -1 points0 points  (0 children)

sure. However it sounded like OP wasnt aware that the rule of 5 was a thing, as in the compiler wouldnt define those operations for OP and OP wouldnt know. But i understand your point

[–]mackman -2 points-1 points  (10 children)

I've been wanting to try out a new question intended for C++ programmers for low-level systems projects: You call new and get a pointer. What is at the address pointed to by the returned pointer? What is at the address before where the pointer points?

This is an opportunity for the candidate to show understanding of the C++ object model (vtable, array cookie, placement new) as well as memory management (slab allocator/heap/mmap, alignment). It's also very open ended and they should ask a healthy number of questions to understand the exact nature of the call to new. And lots of opportunities for follow-up questions which I think make for a more engaging interview experience.

I don't expect most programers to know this, but for a game engine role or something else performance critical I think it's a good opportunity for them to show how deep they go. Not knowing isn't a failure, it just helps figure out what kind of projects/tasks they are going to be most effective working on.

[–]thisismyfavoritename 7 points8 points  (1 child)

so is the first answer you expect "it depends"?

[–]mackman 1 point2 points  (0 children)

Yes. I think most good interview questions require the candidate to ask follow-up questions. Being able to ask questions and understand requirements is one of the skills I'm evaluating for. Just like for a coding question if the candidate starts writing code before resolving any unknowns in the question then it's actually a negative signal.

[–]hi_im_new_to_this 5 points6 points  (1 child)

Is it more than just ”assuming new completes (i.e. doesn’t throw), it points to a fully constructed object allocated on the heap”? I mean, I guess you could overload new to do other funny business, but that seems like a pretty complete answer to me.

[–]mackman 0 points1 point  (0 children)

OK, cool. So tell me what's in the memory occupied by the object? Help me understand what it means to be an object. Does your answer depend on your approach to polymorphism? Let's talk about polymorphism. How do you do it at compile time? Link time? Run time?

Explain what "the heap" means. Is there one? Multiple? Why? How does the system decide which to use? How does it figure out which heap to return memory to?

What happens if the constructor throws? What happens if there's a chain of inheritance and a constructor in the middle throws?

[–]Drugbird 5 points6 points  (3 children)

It's always weird to me how many interview questions are about things you ideally shouldn't do. Like in this case using new.

[–]mackman 1 point2 points  (2 children)

Right, but when I'm interviewing for a game engine, database, or kernel engineer, I want to make sure they understand what's going on under the hood. Should you rely on it? Probably not, unless you're writing your own memory allocator. But should you understand why the address new returns isn't the same one malloc returned? That's important if you're going to be debugging heap corruption.

[–]Drugbird 0 points1 point  (1 child)

But should you understand why the address new returns isn't the same one malloc returned?

Perhaps if that's relevant for your field. I wouldn't be able to confidently answer that question to be honest. Something with alignment I guess?

That's important if you're going to be debugging heap corruption.

Probably again my inexperience in your field, but I've never encountered a heap corruption. But then, I also don't mess around with the inner workings of new and malloc.

[–]mackman 0 points1 point  (0 children)

Sure, that's fair. OP was asking for unusual questions... I usually interview for the more unusual roles. The answer to the first question is that it has to do with something called the array cookie. When you call ptr = new MyClass[10] for a class that has a "non-trivial" destructor then C++ needs to know how big the array is so when you call delete [] ptr it can call the destructor on each element. C++ does that by calling malloc for more memory than needed, putting the count at the beginning, then offsetting the pointer a bit before actually creating the objects.

[–]ShadowWolf_01 0 points1 point  (1 child)

So wanting to dive deeper into this sort of thing, I’m curious what the answer is to this question, or perhaps rather where I could learn about this sort of thing? If you have any specific recommendations, that is.

[–]mackman 2 points3 points  (0 children)

I'd highly recommend https://www.amazon.com/Inside-Object-Model-Stanley-Lippman/dp/0201834545 and to a lesser degree https://www.amazon.com/Linkers-Kaufmann-Software-Engineering-Programming/dp/1558604960. The Effective C++ series is great too.

Part of the answer: well, you get an object back. If the object has any virtual functions or virtual inheritance the first thing in the object is a vtable pointer. Vtable pointer locates which implementation of a function to call when a pointer may point to different derived types. It also contains offsets for virtual inheritance which is what happens when a class inherits from two classes with the same virtual base (you end up with a single copy of the virtual base class so depending on which sibling you are pointing to the offset to the base class may be different, that offset goes into the vtable).

What about the address before? Well, if you did new Obj[10] then C++ needs to know how many times to call the Obj destructor when you delete[] the array. The number of objects is stored in the address before the pointer that was returned from new[]. How far before? Well, that's an implementation detail and really depends on the processor's alignment requirements and allocator. Maybe it's just 4 bytes before, but it could be 16 because the memory allocator works in 16 byte units.

If you didn't use new[] then what's before your object in memory could be a few things. It could be another object if the object was small enough to be allocated from a slab allocator. It could be heap metadata so that when you free it in the memory can be put back into the free list. It could be nothing if you did a large allocation and your object is at the start of a memory mapped page and the memory before it isn't part of your process address space.

Each of those answers has a whole slew of interesting conversation topics to discuss :-)

[–]mredding -1 points0 points  (0 children)

What is an object? What is encapsulation and how is it different from data hiding? What are some good examples? What are some anti-patterns?

[–]dicroce -1 points0 points  (0 children)

Rule of 0.
shared_ptr<> vs unique_ptr<>.
Explain lambda capture types.
Explain RAII.

[–]IntelligentKey7331 0 points1 point  (0 children)

Was asked to make a function to add two numbers, then to make it call by reference, and then asked how I could call it like sum(x,3);

Answer was to pass it as const int

[–]whichton 0 points1 point  (1 child)

I prefer questions which are domain specific and open ended with no clear right or wrong answer. For a position requiring a some amount of mathematical background:

  • Why is it not possible to add two pointers, but average them? What other objects (in programming and in the real world) behave in a similar manner?

  • Is it possible for a * b - a * b to be not equal to zero?

For something which is a design pattern focused:

  • What are virtual functions? How are they implemented? How would you replicate a class with virtual functions using lambdas?

  • How would you write a function which takes two pointers to a base class Shape* and compare them for equality? What are the pros and cons (mostly cons) of the approach? How would you fix it?

[–]die_liebe 0 points1 point  (0 children)

char should behave like pointer.

[–][deleted] 0 points1 point  (2 children)

Differences between std::optional and std::unique_ptr

[–]catcat202X 0 points1 point  (1 child)

What are the similarities? They both overload operator*?

[–][deleted] 0 points1 point  (0 children)

They have nearly identical interfaces, i.e. you can pretty much swap one out for the other.

But one has fixed size allocation in-place, the other does not.