Fog Creek Software
Discussion Board




C++ Test - Questions 6 thru 10

It seems like a slow JoS day, so here's more of the C++ test that I use to weed out complete fakers.  Should someone with 5 years of C++ experience be expected to get all these right?

As always, any criticisms, corrections, improvements, flames, or comparisons of my mother to obscure software languages, are appreciated!



6.  What is a 'const' method of a class? 

7.  When should you declare a destructor as virtual, and why?

8.  Why isn't it safe for a function to return a pointer to an object that was created on the stack? 

9.  How can you tell if the new operator fails? 

10.  What is "method hiding"?  (Hint: it's usually a bad thing.) 

Grumpy Old-Timer
Tuesday, August 12, 2003

Yes, someone with 5 years of C++ should probably get these right.

6.  What is a 'const' method of a class?

Const methods are those methods which can be called when you have a const reference, pointer, instance, etc., of an object. They are expected to have no lasting side-effects (although that rule is often bent somewhat).

7.  When should you declare a destructor as virtual, and why?

When your object is deleted from a base pointer, your destructor needs to be virtual. That means that, by and large, almost all public destructors should be virtual to prevent deletion bugs.

8.  Why isn't it safe for a function to return a pointer to an object that was created on the stack?

When you create something on the stack, its lifetime is limited to the lifetime of the method. Once the method is finished, the stack item no longer exists. Passing a pointer to such a thing out of a method basically ensures memory corruption on your stack.

9.  How can you tell if the new operator fails?

Depends on what version of what C++ compiler you're using. Some will throw an exception, and some will return null. You better know what your compiler does. :)

10.  What is "method hiding"?  (Hint: it's usually a bad thing.)

Method hiding happens when derived classes add methods with names that are identical to non-virtual methods in the base class. Instead of overriding, like in the virtual case, the methods are hidden. Users of the base class will end up calling the base methods, and users of the derived class will end up calling the derived methods.

99.44% of the time, you should be using virtuals here, because that's the expected behavior when using the same name (replacing the implementation for ALL calls, not just callers-via-derived-pointers).

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, August 12, 2003

Grumpy Grumpy Grumpy...

I don't think you get it.  Do you?  Do you really get it?  I don't think you do.

It's not that a person with 5 yrs of experience should know the answers to those questions.  It's the fact that you are looking to hire someone who knows the answers to those questions.  Maybe that individual has 1 year of experience, maybe they have 10 or 20.  Years of experience does not correlate with knowledge.  It also doesn't imply that people with X years of experience will know what you want.

Your question here is invalid.  It should be, 'Do you know the answers to my questions?  If so, I want to talk to you.".  Your question, "Should a person with X years of experience know the answer to my questions?", is one that should never be asked, except by yourself to yourself.

Everyones experiences in programming are different.  There is no such thing as "expect to know" questions.

If I came to you with a resume that said I had 10 years of experience coding C++ and the only thing I did during those 10 years was change a couple routines that print some reports to the printer... then that is all I will know. Unless of course I have expanded my knowledge.

I am critizing your approach to hiring.  First, you need to know how the candidate utilized the language.  Only then can you give them a test about it and when you do give them a test you need to inform them what the test is about so they can > study <.  Don't tell them it's about basic C++, because that's only in your mind.  Tell them they are question you believe are important to the job and give general areas they should study.  After all you didn't just think of those questions off the top of your head.  I'm sure you read some books and thought to yourself, i'll be sly, and put these questions on > my < test.

Take the damn test and throw it away unless it emphasizes things people need to know to do the job you want them to do.  Don't give people a test just to see if they know the answers to > your questions < and then label them > fake < if they don't.  You simply can't judge people like that.

I've seen better days.
Tuesday, August 12, 2003

I'm still curious why all the questions are about C++ - wasn't crypto a significant part of the job? I'd be more interested in finding a crypto expert that can learn C++ than a C++ person who may or may not know crypto.

If you have an essential skill for a job that's "tricky" then screen for that skill *first*

I might also recommend lowering your standards in some areas - are you *completely* unwilling to teach a potential winner?

Philo

Philo
Tuesday, August 12, 2003

"I've see better days" - I disagree with so much of what you said that it would take pages to explain it all.  I can sum it all up, though, by saying that "my" test isn't trying to be sly, it's trying to find people that know the basics of the language after 5 years.  Also, "judging people" on the basis of what they know - as opposed to their race, for example - is a *good* thing.  It's part of living in a meritocracy.

Philo - Nope, no "crypto"; you must be thinking of someone else.  I see your point about training a promising beginner, but we don't want to for several reasons.  See what you you think of them:

First, I'm a contractor here, so I have zero interest in training anyone.  I know, that makes me a horrible person, but you know what?  So be it.  I'm at the point in my life that I guard my time very, very jealously.  Do I answer questions?  Sure, love to.  But spending a huge chunk of time educating someone, with no monetary payoff?  Nope -baby needs new shoes.

Second, like all projects, it was suppossed to be done six months ago.  We don't have time to train someone.  So how do we expect to "grow" smart developers?  As far as I can tell, we don't.

Third, I would have thought, going in, that it wouldn't be hard to find good C++/MFC developers.  Who knew?  And, after all, experienced programmers need jobs just as much as beginners, right?  :)

Fourth, we're a small shop, and I need someone who can actually *help* me in areas I'm weak in, or who could *quickly* learn them, and *then* help me.  I need a peer, an equal.

Grumpy Old-Timer
Tuesday, August 12, 2003

GOT - yeah, there was someone else looking for a C++/Java/Crypto person (and lamenting their bad luck); that's who that was really directed at. Sorry about that.

Philo

Philo
Tuesday, August 12, 2003

I've got to stay that first, I wasn't able to get all of the answers, which is embarassing. Obviously my programs have been too simple (in particular, I don't deal with virtual methods very often).

Second, I think that this is an excellent set of questions.  I have been hired in to places where I didn't have the basic knowledge they were looking for; their interview questions didn't expose that or clue me in to the problem.  That's an unpleasant situation, because even if they are willing to train, a month or two of training can't make up for five years of experience.

Even when I didn't need a set amount of experience, and I was willing to train somebody, I've found these tests to be useful.  A test with basic problems to be solved, directly related to the type of work that will be done, is very useful. Unfortunately we only hit on this trick after several disappointing hires.

Clay Dowling
Tuesday, August 12, 2003

I just conducted an experiment here, running those questions past the PFY who has roughly 5 years of experience coding in C++.  I conclude that those questions are a touch advanced.

One of the four was completely outside of his experience (which he was able to identify immediately), one his experience betrayed him on (hooray for Microsoft compliance), and the other three he had mostly right, probably right enough to pass the interview, but he certainly didn't have them cold.

Which is about what I expected.  After all, he's not a C++ expert, he's a domain expert who has been implementing in C++.  Were I to interview him, staring at a resume that claimed 5 years experience, I would be convinced that it was a fair description of his background.

So as a standard to hold candidates to, I think the questions are a bit difficult.  If we are willing to accept imperfect answers, and are using the questions to get a rough approximation of the candidate's skills, I think they are ok.

Danil
Tuesday, August 12, 2003

>> "I can sum it all up, though, by saying that "my" test isn't trying to be sly, it's trying to find people that know the basics of the language after 5 years.  Also, "judging people" on the basis of what they know - as opposed to their race, for example - is a *good* thing."

Did you understand what I said and apply it to your situation?  Or did you simply make up your mind to disagree and then make the above statement?

I've seen better days.
Tuesday, August 12, 2003

It weeds me out (which it should).  I have about 1 year experience in C++ and couldn't completely answer any of them, whereas the first set I got got all of them. 

I'm going to go study now. :)

Lee
Tuesday, August 12, 2003

Well, I have 13 years of professional programming experience, doing mostly C++ for the last six.  I didn't ace your test.  To me, these sorts of tests test semantics more than understanding.  I know the LOGIC behind all these various language constructs, but I may not always know the official name of it.

For example:

6.  What is a 'const' method of a class? 

I know this one, but practically, I never think to make a method const until I accidently call it with a const object.  The compiler lets me know.  Then, I look and see if I want it to be const or not.

7.  When should you declare a destructor as virtual, and why?

You got me on this one.  Actually, given a little more patience, I probably could have answered it.  Again, I fully understand the "plumbing" behind this and would recognize the need when I saw it, but didn't pick up on the terms at first.

8.  Why isn't it safe for a function to return a pointer to an object that was created on the stack? 

Well, this one is a gimme.  Someone who doesn't understand this one (unless the term "stack" confused them) really doesn't understand programming very well.

9.  How can you tell if the new operator fails? 

I'm going with a result of 0 (or NULL).  The funny thing is that I've pretty much stopped checking for this.  In my 13 years, including 5 in embedded where memory limitations make this a more likely problem, I've NEVER had a new (or malloc) fail.  I've had them crash because of loose pointers elsewhere in the code, but I've never had them gracefully reject my request because there wasn't enough memory.  Maybe I've just been lucky.

10.  What is "method hiding"?  (Hint: it's usually a bad thing.) 

I was in the ballpark of this one, but you might not have given me credit for it.  Again though, I would recognize the problem instantly if I ever saw it.  I don't think I've ever seen someone make this mistake though (I guess it could be intentional, but it's stupid, so I still consider that a mistake), so I didn't recognize the term.

So anyway, be careful with tests.  They can be very useful, but be flexible in how you ask and be sure you look for FUNCTIONAL UNDERSTANDING, not just semantic knowledge.

Dave

David
Tuesday, August 12, 2003

Danil -

What's a "PFY"?  (I'm so clueless... sigh...)

Also, which question was Microsoft-specific?  Just curious...

Yeah, this set of 5 questions is somewhat harder than the first set.  The test is designed to start out easy and get harder, mostly just to give the candidate a chance to get a few right before having to face the harder ones.  (Wait'll you see the last set of questions.)

So, the PFY you mentioned did pretty good, in my experience; I woldn't really have expected anyone with five years experience to get them all perfect (although I'm sure many can).  Thanks for the feedback.

"Pretty Fine Yippie"?
"Professional Fromage Ycelpt"?
"Perfectly Fake Yesman"?

Grumpy Old-Timer
Tuesday, August 12, 2003

Probably: Pimply-Faced Youth

.
Tuesday, August 12, 2003

The question with a Microsoft specific answer was "what happens when new fails". Microsoft has, for a long time, returned null. I believe that, when exceptions were added, the language spec was updated to say that a "new" failure should throw an exception. Microsoft added this as a non-default-behavior compiler option, since there was a lot of code that was pre-exceptions that checked for null pointers instead of catching exceptions.

In fact, it's still somewhat "chic" to have code that doesn't rely on exceptions, because it's much smaller. For a long time, the C++ compiler for the Pocket PC didn't support exceptions -- AT ALL. (It may now, I don't know.) Lots of things that were designed in the post exception era, that relied on them (such as the C++ standard library containers) worked difficultly, if at all, when you had exceptions turned off.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, August 12, 2003

Dave -

"I never think to make a method const until I accidentally call it with a const object."  See, that's the point.  There are very good reasons for making methods const as a matter of habit.  I almost always start out making the method const, and if I call something non-const, *then* I make the method non-const.

"… but didn't pick up on the terms at first."  Yes, this is one of those annoying things you just have to know, not reason through each time.  And this only comes with experience, and reading.  Which is what I'm testing for.

"Someone who doesn't understand this one (unless the term "stack" confused them) really doesn't understand programming very well."  So true; and this is one that almost everyone gets right.  Almost.

"I've NEVER had a new (or malloc) fail."  You know what - neither have I.  I'm going to remove this question, not because memory management is not important, but because it's not fundamental enough for my test.  Thanks for pointing that out.  (Now I have to find a new question…)

"I don't think I've ever seen someone make this mistake though (I guess it could be intentional, but it's stupid, so I still consider that a mistake), so I didn't recognize the term."  Yeah, this question is kind of edging into the "hard questions" area - and I've seen this a lot, especially in very large programs.  Maybe I'll rewrite this to present a hierarchy of Animal-based classes, and ask something like, "What would happen if we forgot to make Animal::Move( ) a virtual function?"

"Be sure you look for FUNCTIONAL UNDERSTANDING, not just semantic knowledge."  I hear ya, but the only way to really test that is to make people write code, and I personally hate having to write code, or pseudo-code, during a tech interview.  Maybe it's just me, but I always think they're stupid, and not a good evaluation of my ability.  Maybe I need to rethink that…

Thanks for the feedback.

Grumpy Old-Timer
Tuesday, August 12, 2003

I easily got your first 5 questions from your original post right.  I got 2 out of 5 of these right.  I haven't programmed much in C++ since college (over 5 years ago).  I am, however, a very good programmer and I'm sure I could be productive with C++ development very quickly.  You may be eliminating some good programmers by only asking C++ specific questions. 

Maybe you don't have the time or inclination to wait for a good programmer to get back into C++ mode though?  I think I'd rather have a smart person who's a good programmer, but maybe hasn't worked deeply with C++ instead of a not so smart programmer who has only worked with C++.  I guess the best of both worlds for you is a good programmer who has worked deeply in C++.

The thing I'd worry about with your tests (and I don't know anything about the rest of your interview process) is they don't really test for smarts.  They test for knowledge.  Those 2 things are not equal.  Knowledge is easy for a smart person to gain.  Smarts is not necessarily easy for a knowledgable person to gain.

chris
Tuesday, August 12, 2003

"The thing I'd worry about with your tests (and I don't know anything about the rest of your interview process) is they don't really test for smarts.  They test for knowledge.  Those 2 things are not equal.  Knowledge is easy for a smart person to gain.  Smarts is not necessarily easy for a knowledgable person to gain."

THAT is it! THAT is why I dislike these tests and quizzes. Sure you want your applicant to be able to speak the language, and you want an indicator that they know what they say they know, but so often things like these (I can't address this one - I'm not a C++ guy) turn into "know the secret handshake" things.

For example, if testing a project manager, the difference between:
"What is the mythical man month?"
and
"You're running a project that's behind schedule, what do you do?"

To some degree, these test the same knowledge. However, the first is really doing a sly "have you read our secret manual?" while the latter is testing "experience"

Ditto for other "programming knowledge" tests vs. "write me some code" tests.

GOT - why don't you just give candidates some coding problems and see how they do? Or better yet, keep your test but *also* throw them some coding problems and see if there's a correlation?

Philo

Philo
Tuesday, August 12, 2003

I like what Danil said about a coworker: "After all, he's not a C++ expert, he's a domain expert who has been implementing in C++".

This sums up in ones and zeros why I think these language-lawyer type questions are bad.  It's true that anyone who's fluent with these details probably is also good at mapping problems to code, but the converse need not be true.  With a test like this, there's a good chance you're dumping a whole lot of guys who, although they don't know every detail of the language, are still fluent in using a subset of C++ to solve real-world problems.

I think Windows CE supports exception handling now (eVC 3.0 seems to compile try/catch at any rate).

Alyosha`
Tuesday, August 12, 2003

GOT, I've got ~1 year of cumulative C++ experience, so let's see how I'd do.

6. A 'const' method has read-only access to the class's data. Brad's answer "Const methods are those methods which can be called when you have a const reference, pointer, instance, etc., of an object." threw me off. I thought that any instance (const or non-const) of a object could call a const method, however const instances of an object cannot call non-const methods [I'm not sure of this, but it's how I'd answer in an interview]. Also, const methods cannot call other non-const methods within the function body.

7. I would declare a destructor virtual anytime that I (a) declared a constructor virtual or (b) the non-virtual constructor in the based class called a virtual factory method to create the object (e.g. create()). The reasoning being that if an inherited class can allocate memory then it should be able to deallocate memory.

8. Being a visual communicator I'd probably explain this by drawing a picture of the stack and what happens at the assembly level to locals when functions are called and returned.

9. I would've answered test it for NULL. As far as I know the allocate() method of the STL allocator class doesn't throw an exception. So, I wouldn't think to catch an exception in my own code.

10. This one got me. I wouldn't have associated it with overriding non-virtual base class functions. To be honest, I thought this was a Microsoft question. I think if you prefix a method name with an underscore it won't show up in the intellisense popup. I'm not even sure if that's correct, but if so - it does hide the method, right? :)

Personally, I think the difference between smarts and knowledge = hours of reading and debugging time. So knowledge shouldn't be discounted. On the other hand, I assume that this is only one portion of the interview process and that you mix this with other criteria as well. For example, let's say that you were satisfied enough with my answers above. Would I be able to step is as a peer? No. I'd be the first to tell you that I don't have that level of experience, so knowledge tests alone would not be a good selection technique.

Nick
Tuesday, August 12, 2003

Responding to a few people's comments:

There's no such thing as a virtual constructor.  Rule of thumb is if your class has any virtual methods, it should have a virtual destructor.

You can provide a function to be called when allocation fails (_set_new_handler).  Usually, it would free some memory (e.g. clear a cache) and retry.  But you can also have it throw std::bad_alloc, to easily make MSVC++ compliant.

re: the domain expert who can't answer the questions:
Obviously, everyone brings something different to the table, and we're not all language lawyers.  But returning a pointer (or more likely, a reference) to an object on the stack, or not understanding when (and why) you need a virtual destructor is an accident waiting to happen.  Hopefully, your organization has code reviews in place to catch this.

Brian
Tuesday, August 12, 2003

Brian - the point is that often with questions like these a person might do them without knowing that's what they're doing. Again, I can't speak to these particular questions, but an example from .Net might be: "when should you use a strongly-typed dataset?"

Now a self-taught type might know that he can create a dataset from a database table, and that he can use it as an object, and that it's not really good when all that's needed is a quick read of a table. But he's never known that to be called "strongly typed datasets" - he knows the hows and whys, but not necessarily the "common language"

You see this a LOT in self-taught people, who are often diamonds in the rough.

Philo

Philo
Tuesday, August 12, 2003

Alyosha` - "Language-lawyer?"  But these are *not* hard questions for good, experienced C++ programmers!  I am not asking, for example, "Which operators cannot be overloaded?"  (Did you remember the four cast operators?  The scope resolution operator?  The "sizeof" operator?)  If someone doesn't know why destructors should be virtual, they need to stay the hell away from my code.  Would you hire a surgeon that knew a "subset" of Gray's Anatomy?

Philo - Regarding knowledge vs. smarts, geez, I wouldn't begin to know how to test someone for smarts.  After reading Gould's "Mismeasure of Man", I'm not even sure there *is* such as thing as smarts.  There's also the problem of objectivity to consider, which, if we're honest, is always a factor.  Do I think this girl is smart because she's cute?  Am I giving this guy a break because he went to the same school I did?  Part of the advantage of a "knowledge" test is that it's hopefully more objective.  But, yeah, I see your point - maybe I'll include a short, simple code test - any ideas, y'all?  And I mean *simple* - all I'm trying to do is weed out fakers, remember.

Nick - I would grade you thusly: 6 is right; 7 I'd give you half for (there is something informally called a "virtual constructor", but that ain't it); 8 fine; 9 half-credit; 10 um, no, but no one ever answers that one, so I'm going to rewrite it. 

Brian - Thank you!  Finally someone who gets it!  But... "code reviews"?  Your strange words confuse and frighten me...  Just kidding, I love code reviews, but I've worked exactly one place that did them.  Once I finally get someone hired, you can bet we'll be doing them weekly.

Philo again - Hey, I'm a self-taught person, and I take umbrage (not really) at your characterization.  "Self-taught" doesn't *have* to mean you just hack and hack and hack until it compiles, does it?  Self-taught people have access to the same books, magazines, classes, websites, code examples, etc., that educated people do.  Being self-taught is not an excuse for not knowing the "common language".  Hell, we're all "self-taught" - how much of what you learned in college even exists today?  (Anyone seen my stack of punch cards?)

Grumpy Old-Timer
Tuesday, August 12, 2003

Brian - no, he's good enough.  Just that there is a difference between knowing the Right Answer[patent pending], and knowing enough to stay out of trouble.

I wonder if some here are reading the questions the wrong way around.  Yes, somebody who cannot answer these questions may be a better hire than someone who can - that does not (in my mind) discredit these questions as a test to determine that a candidate's claimed experience matches his actual experience.

After all, my expectations have been set by your resume - I'm just trying to determine that you meet them.

It does get challenging, I have found, to increase the difficulty of the questions without sacrificing the practicality of them.  Are you looking for candidates who can use C++, or write it?

Danil
Tuesday, August 12, 2003

"Brad's answer 'Const methods are those methods which can be called when you have a const reference, pointer, instance, etc., of an object.' threw me off."

Yeah, I suppose I could've worded that better by inverting it. :) When you have a const reference, pointer, etc., then the only methods you can call are const methods. With a mutable reference, pointer, etc., you can call both const and non-const methods.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, August 12, 2003

"But these are *not* hard questions for good, experienced C++ programmers!"

I agree. These are no hard questions for a seasoned C++ programmer. They're not trick questions. They address things that are used pretty regularly (or should be). I would be suspicious of someone who couldn't answer many or any of these.

I think people are over-reacting when people ask technical questions. Joel says you need someone who is "Smart and Gets Things Done". I agree.

HOWEVER, you may have additional needs, like making sure the person is already up to speed on your currently used technologies, because you're hiring him or her to jump in right away and be productive. You also don't want to hire someone who would stretch the truth (or even outright lie) on their resume. I think it's appropriate to ask some tech questions, as long as they are not your primary means of determining whether to hire someone.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, August 12, 2003

GOT - 3/5, I'll take it! That ~1 year cumulative experience I stated is probably closer to 6 months equivalent for a full time C++ programmer.

I think the tests are a good tool. If I can get 1-5 correct and about half of 6-10 correct, then I would certainly expect someone with 5 years of experience to get all 5. On the other hand, you can't screen for this alone because you could get someone whose put forth the effort to learn the language rules but still doesn't have the hands-on experience necessary.

Maybe it's just me, but of all the languages I've programmed in, C++ is the only one that I felt I had to be somewhat of a language lawyer to be effective with it.

Nick
Tuesday, August 12, 2003

Well, I've done C++ for about 7 years, and my responses were:

6.  A const method cannot modify its class's data members, unless they are declared mutable.

Good corrolary question:  why does one use a const method?

Better question:  how does one ensure const-correctness when defining class interfaces?

Even better:  define a class interface for x.  Make sure they ask more about the business requirements of x.  Then make sure they use const methods. :)

7.  Always.  Because the destructor has no vtable entry otherwise, and will not be called if a subclass is destroyed.  In other words, it's a memory leak.

This is an absolute essential.  People just shouldn't write C++ without knowing this, but they do...

8.  Because stack memory is deallocated when the function returns.

Maybe a little too basic, IMO, but a good weed-out.

9.  My gut here was that it returns 0, but as has been pointed out many times, it actually throws an exception.  Actually, I knew it threw an exception too, but the exception has varied from compiler to compiler...I remember MSVC used to throw xalloc, and now I think the standard dictates bad_alloc or a subclass.

Honestly, I've never had new fail in practice.  I know that my code will fail because of new under certain circumstances (like armageddon, for instance), but I think like most ppl here, I'm writing business software and not aircraft controllers.

10.  I really don't like this question.  When I was first introduced to "method hiding," it was just as the first step in a chapter on classes.  It wasn't called method hiding there; it was just given as a contrast to the virtual function.  If it was, I don't remember.

Honestly, off the top of my head, the term "method hiding" sounds more like:

namespace
{
  void foo( ) { }
}

But I digress. :)

Anyway, a better question is:  when do you use the keyword virtual?    And if the answer isn't something a lot like "always," dump 'em. :)

And I'm serious about that--I've seen a _lot_ of C++ code in my work without the keyword virtual, and a _lot_ of people without a clue why it's used.

smkr45
Tuesday, August 12, 2003

This is the problem with C++ - there isn't a single feature in the language where "always" is the right answer.

For the virtual keyword, there is a large set of classes where virtual is the wrong thing to have: value classes. Things like point. Or complex. Classes that are designed to be passed by value, not by reference. Classes that are designed to be used as-is, not as base classes.

Adding virtual all over the place just adds virtual function call overhead and a vtbl to what are supposed to be a small, quick class.

I do wish C++ had something along the lines of "sealed" so you can say "gosh darn it, this wasn't designed to be a base class so you can't derive from it".

Actually, no I don't - I don't use C++ anymore if I can help it. :-)

Chris Tavares
Tuesday, August 12, 2003

Uh... I agree that C++ users should know what virtual means but "always" would be a very bad answer for when to use it. 

SomeBody
Tuesday, August 12, 2003

Chris:

I get this "virtual function overhead" line everytime I bring up the subject.  Even from Java programmers.  It's nuts.

Are we now magically back in the 1980s where the extra n bytes of space per class and a table lookup produce unbearable overhead?

Who gives a damn?  Really?  Maybe game programmers or aircraft controller authors, but for the rest of us who don't use assembly to optimize tight loops, who cares?  Really?

You're wishing C++ had sealed.  You mean, as in .NET?  :)

As for value classes, I have to ask the same question.  Virtual just gives you more options.  Are we really unable to spare the n bytes of extra space and the lookup on our Pentium {n >= 2}s?

smkr45
Tuesday, August 12, 2003

GOT: But... "code reviews"?  Your strange words confuse and frighten me...

It was totally hypocritical of me to suggest.  I barely know what the words mean myself -  I know of formal code reviews only in theory.

Brian
Tuesday, August 12, 2003

I agree with the poster who said that experience should be irrelevant.  I will say that anyone that works on production C++ MUST be able to answer these questions.  They are fundamental to writting code that works.

You want a difficult question?

If new fails what are you going to do about it?

If you think about it long enough you may reconsider your whole memory management strategy.  Or you may choose to ignore it. 

One pet peeve I have is every beginner C++ book explains that new can fail.  But so what?  What do you do?  Under Windows the answer is often, not a darn thing. 

christopher baus
Tuesday, August 12, 2003

If you're programming in C++, you care, by definition. If you didn't, you wouldn't be wasting your time coding in C++. You've chosen a tool that deliberately exposes all these tiny little knobs and levers to squeeze those last couple of bits of efficiency out of the code.

In C++, you *HAVE* to care, because the semantics of the language are very different between value and reference classes, and the subtleties will bite you in the ass if you aren't paying attention.

Try to pass a derived class object by value in C++ and see what happens. That's what I'm talking about. Also, try using any of the STL containers - they store objects *by value*, which means that objects of derived types will get sliced as they go in a container, so you have to store pointers.

Putting a virtual destructor, or any virtual method for that matter, says two things about this class to me.

1) This is a reference class. Do not pass objects of this type by value.

2) This class is designed to be used as a base class.

As for my personal choices, well, I code in pretty much whatever people will pay me for. C# most recently, C++ before that. I usually use Python for my personal stuff. If I'm coding in C++, you can be darn sure I do pay attention to those little details. In C#, I really don't care, the runtime handles it for me.

I'll leave it up to you to deduce which I like working in better. ;-)

Chris Tavares
Tuesday, August 12, 2003

christopher:

Well, you can always call MessageBox( ). :)

smkr45
Tuesday, August 12, 2003

I'm trying to stay off my high horse here, but I am amazed that there are professional C++ developers who don't know why or when to declare a destructor virtual. 

There are very few areas where it is acceptable to not know about virtual destructors, but then you are probably not using the most common features of the language. 

I'm not going to defend C++ as a good language, but if you are going to use it, there are certain things you MUST understand.  This is one of them. 

If you don't want to train a C++ programmer, and they can not answer these questions.  DO NOT hire him or her.  There are plenty of sharp programmers out there now.  There is no excuse to hire poor ones.

I think these are decent questions, which should be followed up by system level questions. 

christopher baus
Tuesday, August 12, 2003

"If you're programming in C++, you care, by definition. If you didn't, you wouldn't be wasting your time coding in C++. You've chosen a tool that deliberately exposes all these tiny little knobs and levers to squeeze those last couple of bits of efficiency out of the code."

Well, you're really not answering my question:

"Are we now magically back in the 1980s where the extra n bytes of space per class and a table lookup produce unbearable overhead?"

Virtual function overhead just has no footprint worth worrying about.  C++ is low-level, but it's not machine language.  And there's not much money in my employer's budget for pointless bit-twiddling, how about yours?

It's one thing to say:  you're writing in a low-level language, which facilitates efficiency.  It's quite another to say:  YOU MUST OPTIMIZE EVERY BIT!

:)

smkr45
Tuesday, August 12, 2003

http://visualcpp.net/show.php?qID=14

Anonymous Cowboy
Tuesday, August 12, 2003

> Well, you can always call MessageBox( ). :)

Ha.  That's my favorite message box.

"You are screwed!"

      =====
    |  OK?  |
      =====

christopher baus
Tuesday, August 12, 2003

Grumpy,

These questions should be straight forward for a C++ developer with 5 years experience.  This is a fair test.

Off the top of my head.

6.  a "const" method cannot change the state of an object directly (changing a variable) or indirectly (by calling another non-const method).  The exception is variables that are declared as "mutable", which can be altered by "const" methods.

7.  all destructors should be virtual - no exceptions. 
a) It is the only way that you can guarantee correct destruction of the object if assigned to variable of a base class type.

  eg.  Object* o = new File();
        delete o;

where File is derived from Object.

b) It is essential to ensure that your existing code does break when you subclass and use your subclass in place of the original.  ie. future proofing example a) above.

8.  The object is out of scope.  Its destructor has been called.  Its contents have most likely been overwritten.

9.  new throws a bad_alloc exception.  You can call set_new_handler() to change this behaviour to return NULL if you prefer.  You can also override the new operator to change this behaviour.

10.  declaring a non-virtual method "x" in a class and then declaring the same non-virtual method "x" in a subclass. 

The subclass version of "x" will never be called from a polymorphic assignment.

  eg.  class A {
            void x();
        };

        class B {
            void x();
        };

        A* a = new B();
        a->x();            <-- calls A::x not B::x

This is particular problem with static methods which cannot be declared virtual.       

James Thorpe
Tuesday, August 12, 2003

er, 7b should read "does NOT break"

James Thorpe
Tuesday, August 12, 2003

"all destructors should be virtual - no exceptions."

I disagree. In limited circumstances, a virtual destructor is not required. An example would be when an object is only createable and destroyable by itself (private constructors and destructor). It's rare, but it happens.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, August 12, 2003

James: all destructors should be virtual - no exceptions.

In your example, Object is required to have a virtual destructor, File is not.  A class T must have a virtual destructor if an instance of a subclass is deleted through a pointer to T.  In practice, this means you need a virtual destructor if any method is virtual.  If T has no virtual methods, pointers to T to not behave polymorphically (so there is no real reason to have one).

Brian
Tuesday, August 12, 2003

Joining in the piling on....

1) If *all* destructors should be virtual; it probably would have been built into the language that way.

2) If *all* destructors should be virtual, the standard would not specify classes with non virtual destructors.

If you are an *very* experienced C++ programmer, I'd expect you to know what happens when err by failing to make a destructor virtual, but I wouldn't expect it in five years [hint: memory leak is NOT the right answer, see 5.3.5 p3 - yes, I know that without looking it up].


Oh, and there has yet to appear on this thread a correct answer to #10.  The answer is in the FAQ, people....


Danil
Wednesday, August 13, 2003

Maybe your test isn't so good. I have almost no c++ experience but only got one question wrong.

I was always good at exams and I've got some good c++ books. You obviously need extra questions to weed out people like me because the c++ questions alone aren't enough.

RB
Wednesday, August 13, 2003

"Would you hire a surgeon that knew a "subset" of Gray's Anatomy?"

Actually yes.  I wouldn't want a brain surgeon that knew a lot about the anatomy of my big toe.

Joe AA
Wednesday, August 13, 2003

Sorry - I stand by all destructors should be virtual.  I knew this would stir up debate.

The only exception is classes that cannot be subclassed (as Brad points out).  This is not a language issue - it is a design issue.  Just because the language does not enforce virtual destructors does not mean that non-virtual destructors are a good idea.

Brian states "A class T must have a virtual destructor if an instance of a subclass is deleted through a pointer to T".  This is the exactly reason all destructors (with Brad's exception) should be virtual.

The issue is future proofing your code.  Anyone who does not code with maintenance in mind should be re-educated with a 4x2.  You simply cannot tell when you will need to subclass in the future.  The addition of the "virtual" keyword saves you no end of hassle. 

This is the whole rationale behind the "canonical object".  You don't have to have one.  But boy, does it make your life easier further down the track if you do.

James Thorpe
Wednesday, August 13, 2003

"Oh, and there has yet to appear on this thread a correct answer to #10.  The answer is in the FAQ, people...."

What FAQ?

The other definition I have seen of method hiding is to change the visibility of a method to something more restrictive.

ie.  public -> protected -> private.

James Thorpe
Wednesday, August 13, 2003

"In your example, Object is required to have a virtual destructor, File is not."

If Object has a virtual destructor, and File derives from Object, File has a virtual destructor as well, whether you like it or not.

Frederik Slijkerman
Wednesday, August 13, 2003

GOT - Answer to question 10.

What is method hiding? It's when you have a function in the derived class that has the same name as a function in the base class and yet it doesn't share the same function signature. The method in the base isn't overridden or overloaded but simply hidden. There's no way to access it unless you bring it into scope in the derived class by using a using statement.

For example

struct Base
{
  int Foo(int bar) const;
  int Foo(int bar);
};

class Derived1 : public Base
{
};

class Derived2 : public Base
{
  public :
      int Foo(int bar, int bar2);      // hides Base::Foo(int)
};

class Derived3 : public Base
{
  public :
      using Base::Foo;               
      int Foo(int bar, int bar2);
};

Derived1 d1;
d1.Foo(1);

Derived2 d2;
d2.Foo(1,1);
d2.Foo(1);          // compile error, int Foo(int) hidden

Derived3 d3;
d3.Foo(1,1);
d3.Foo(1);     

Oh, and the reason that everyone who mentioned virtual is wrong IMHO ;) is that you can stick virtual in front of all of the Foo's above and get exactly the same results.

I don't think it's a particularly good question though, it's the language lawyer phrasing that I don't like. I'd probably use the example above as the question, with the virtuals in there, and ask why you get a compile error for d2.Foo(1)...

Brian

"In practice, this means you need a virtual destructor if any method is virtual."

I'd add "and the destructor is public" as it's only a problem if the derived class can be deleted through a pointer to the base; if the base's destructor isn't public then this can't happen. I seem to be using abstract classes with protected destructors quite a lot lately, probably just a fad...

People who dont like technical tests -

In general I quite like technical tests as long as you talk to the candidate about their answers after. I've hired quite a few people who 'fail' the written test but can discuss the test after and prove that they know enough for us to consider it a hire. Tests are often good jumping off points for longer questioning about design and other stuff. I've also failed people who aced the test when they then couldnt convince me that they hadn't just cramming by reading Meyers all night. So test, yes, but also talk. You cant give a test to an HR bod and expect them to sort candidates with it. You need someone who knows the subject and the level of skill you're looking for; they can then use the test as they see fit.

Let's have the hard questions now :)

Len Holgate (www.lenholgate.com)
Wednesday, August 13, 2003

Danil:

OK, the behavior is undefined.  But out here in the real world, I haven't seen much code outright blow up from a lack of virtual destructors.

One can reasonably infer what the practical effect of not  having a virtual destructor is...

And for #10, I presume you're expecting some salient commentary on private inheritance.  Although I wouldn't consider that the right answer, since private inheritance is not "a bad thing" in the general case.

smkr45
Wednesday, August 13, 2003

How about a hard question:

11. Take the following template function:

template T min(const T a, const T b)
{
  return (a < b) ? a : b;
}

What instantiation of T will be selected by the compiler for the following call?

  float a = 0.1;
  float result = min(a, 1.0);

What will be stored in result?

Frederik Slijkerman
Wednesday, August 13, 2003

Um, that should be 'template<T> min'...

Frederik Slijkerman
Wednesday, August 13, 2003

Perhaps:
template <typename T> T min(const T a, const T b)
{
  return (a < b) ? a : b;
}

Recommended: http://www.comeaucomputing.com/tryitout/

Danil
Wednesday, August 13, 2003

Thanks everyone, this has been a big help.  Based on the feedback, I've removed question 9 entirely, and re-written question 10 to not use the phrase "method hiding".

Grumpy Old-Timer
Wednesday, August 13, 2003

Grumpy Old-Timer: surgeons DO know only subsets of Gray's Anatomy. That is the reason you have specializations like brain surgeon or heart surgeon, or even “sport injury knee ligament surgeon”.

Alyosha and I've seen better days: I completely agree with you.

I found out that usually after one year of programming the core skill set is already formed. If the skills are not in place after that period you can wait 10 years more and nothing will improve. I have colleagues that after 1 year of work easily overtake seniors with 10 years experience, both in code productivity and design.

The C++ questions posted now and then are all basic questions. Since I work more using C# an VB.Net nowadays, I forget sometimes the answer to questions like what operators can be overloaded or the operators precedence. I am always careful to read a C++ book before a job interview (like Marshall Cline’s C++ FAQs). The reading refreshes my memory to more than acceptable levels.

If somebody won’t answer the questions posted, you can accuse him/her more of being sloppy in preparing for interview than a lack of knowledge.

Evidently, hiring a good programmer as compared to hiring a good C++ coder is always to be preferred. And one that knows the business domain is gold.

19th floor
Wednesday, August 13, 2003

Regarding the "new" operator... I may completely off my rocker since it's been a while that I've been writing C++ code but isn't the point of catching exceptions from "new" that your *constructor* might throw an exception?

Sure, the memory allocation itself will either succeed or the system is doomed anyway. But who knows what the constructor of a complex custom type will do?  And the type constructor (default or parameterized) is automatically invoked by "new", right?

Chris Nahr
Wednesday, August 13, 2003

I'm curious to see the rewritten version of #10.

The previous version I think is pretty effective descriminator - people who know the right answer recognize the question at once, those who don't are unlikely to guess correctly (as demonstrated above). 

Probably not a fair 6-10 question, and it definitely suspect on "is that really practical?" grounds.

The FAQ is the C++ FAQ, maintained by Marshall Cline.  At least two versions have been published in dead tree form, in addition to the "LITE" version maintained on-line.

The relevant entry is here:
http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6

Danil
Wednesday, August 13, 2003

Frederik:  Ok, I should have been more clear between having a virtual destructor and declaring one.

James: "The issue is future proofing your code."
I agree, but the point remains that if a class has no virtual methods, it is not sensible for someone to be deleting a subclass via pointer-to-base.  If you can come up with a reasonable counterexample, I'd like to see it.  I'm not saying it's wrong to always declare your destructors virtual, but it's at least excessive.

Brian
Wednesday, August 13, 2003

Believe it or not, there are times when the overhead of virtual desctructors is not acceptable.  Think millions of point objects in a 3D graph.

christopher baus
Wednesday, August 13, 2003

^^

Marshall Cline, btw, has a nice insight on this very topic (31.6 from the Lite faq):

"Note that the difference between inlining and non-inlining is normally much more significant than the difference between a regular function call and a virtual function call. For example, the difference between a regular function call and a virtual function call is often just two extra memory references, but the difference between an inline function and a non-inline function can be as much as an order of magnitude (for zillions of calls to insignificant member functions, loss of inlining virtual functions can result in 25X speed degradation! [Doug Lea, "Customization in C++," proc Usenix C++ 1990]).

A practical consequence of this insight: don't get bogged down in the endless debates (or sales tactics!) of compiler/language vendors who compare the cost of a virtual function call on their language/compiler with the same on another language/compiler. Such comparisons are largely meaningless when compared with the ability of the language/compiler to "inline expand" member function calls. I.e., many language implementation vendors make a big stink about how good their dispatch strategy is, but if these implementations don't inline member function calls, the overall system performance would be poor, since it is inlining —not dispatching— that has the greatest performance impact."

smkr45
Wednesday, August 13, 2003

What Len Holgate answered was what I thought of when you asked about method hiding, not what everyone else has answered about overriding non-virtual methods.

I must be getting tired though, because I can't seem to remember why C++ hides methods like that.

One-Armed Bandit
Wednesday, August 13, 2003

> I can't seem to remember why C++ hides methods like that.

Name lookup stops with the first match in the most local scope, irrespective of potential would-be candidate overloads in other reachable scopes.

Steven E. Harris
Wednesday, August 13, 2003

> isn't the point of catching exceptions from "new"
> that your *constructor* might throw an exception?

Memory allocation failures are but one possible class of exception that may emanate from a "new" expression. If allocation fails, the constructor never runs, but otherwise the potential exception set is the union of std::bad_alloc and any exceptions thrown in all code paths reached in the constructed type's initialization. The "point" of catching exceptions from a "new" expression has less to do with what went wrong than what you might be able to do or have to do in response at current scope.

> Sure, the memory allocation itself will either
> succeed or the system is doomed anyway.

The memory exhaustion could be temporary, and the thrown exception may give your application a chance to warn the user and save data before exiting.

> And the type constructor (default or parameterized)
> is automatically invoked by "new", right?

By a "new" expression, yes, once operator new() succeeds. Don't confuse the "new" expression with operator new(); the latter is independent of the constructor.

Steven E. Harris
Wednesday, August 13, 2003

smkr45, that's a good point.  But if you are comparing virtual vs non-virtual, that just makes the distinction more significant, because virtual functions can't be inlined.

Mike McNertney
Wednesday, August 13, 2003

smkr45: "Virtual function overhead just has no footprint worth worrying about." Well, it DOES add an extra pointer sized member to your class. If your class was only pointer sized to begin with, you've just doubled your memory footprint.

Not that important for some applications, of critical importance for others. It all depends on what and how much you are doing.

C trivia question I had to deal with last week, for those still reading:

What is the _EFFECTIVE_ difference between the two following functions? (and yes, a good compiler will compile them to different code, even if the contents are identical)

void k_r_foo(i)
  float i;
{
....
}

void ansi_foo(float i)
{
....
}

Steven C.
Wednesday, August 13, 2003

"If you can come up with a reasonable counterexample, I'd like to see it.  I'm not saying it's wrong to always declare your destructors virtual, but it's at least excessive."

Brian, you only need two generations of subclass.  The first generation introduces a virtual method, the second overrides it.
 
  class Object {
  };
 
  class Gen1: public Object {
  public:
    virtual void new_fn();
  };

  class Gen2: public Gen1 {
  public:
    virtual void new_fn();
  };

There are always exceptions that can be argued for.  The rule should be "always virtual" unless you can convincingly demonstrate the need not to.  A more reasonable answer to the original question would take into account the specific problem domain.  Someone mentioned a graphical Point class for example.

I work in the finance industry with systems that we expect to operate for 15 years or more.  When you are dealing with billions of dollars and 15 year horizons there is no such thing as excessive.

James Thorpe
Wednesday, August 13, 2003

Did you forget to finish the example?  Why does anyone have a pointer to an Object?  It has no virtual methods, so what would you do with it?  Gen1 introduced the first virtual method in the class hierarchy, and so it should probably have a virtual destructor.

Brian
Thursday, August 14, 2003

Brian: the example is contrived and pseudo-code.  The point was that 2 future subclass generations are all that is needed to mandate a "virtual destructor".

In my problem domain, Object would probably be something like:  Investment, or Brokerage, or Transaction.  I trust you get the idea.

Future proofing does not mean solving tomorrow's problem only.  In means the thinking about next month's, and next year's.  As I mentioned, I assume a 15 year life for a system and design accordingly.

Having a few simple rules in place - canonical object with virtual destructor for example - means that things break far less often down the track.

Here is another "rule" I mandate.  "All protected methods must be virtual".  People might like to think this through.

James Thorpe
Thursday, August 14, 2003

Mr C, what is the difference between the two pieces of code? I would expect both to be compiled identically.

However the call point may be compiled differently (read: wrongly) if the type of the variable being used as the parameter is incorrect... surely?

Tom
Thursday, August 14, 2003

In the first function (the one without a prototype for its argument) will have its argument passed as a double and promoted in the function to a float (in strict k+r mode, the function will actually use a double as its parameter through out, causing some interesting issues if you are comparing to float values).

The second function will have its argument passed as a float.

This is unlikely to matter in most cases, but the k+r mode does produce slightly larger code size if it matters to you.

Really it just annoyed me to have to track this down so I thought I'd share the pain. :)

Steven C.
Thursday, August 14, 2003

Grumpy Old Timer, if you're dropping the "new" question, try the following:

What is the behavior of the default copy contructor and assignment operator?

It's so important, and so few programmers seem to know it. Bonus points should go to anyone who suggests declaring them as private until they are needed.

Gustavo W.
Thursday, August 14, 2003

I have a little over one year C++ experience and found all the questions pretty simple - almost "starter questions".  although "method hiding" is not a well known term.
in fact when we interview people we hammer through about 50 or 60 questions on C++/STL/multi threading.  More questions allows a better picture of the the candidate if they don't happen to know one particular thing.  i'm a firm believer that years of experience means sh*t.  there are loads of 10 year old timers who don't read and don't push themselves when coding that can't answer any of these questions.

odl21
Monday, April 26, 2004

*  Recent Topics

*  Fog Creek Home