I got a big C++ interview coming up....and I am arrogant and think I am a C++ god...so see if you can stump my knowledge of the language. I solemnly swear I won't cheat and look up the answer, and you can feel free to rip me a new one in your responses. The only requirement is that you can't include anything about particular libraries or interfaces that aren't intrinsic to C++ (stl stuff is ok). The interview won't include things like that or other trivialities which can simply be referenced. They will be asking me things like "name the 3 ways you can blah blah" or "how could the following code be improved...what is the security or memory problem...what weaknesses does it have etc"
Show me what you got! Put me in my place and make me feel like I am going into this interview a lot dumber than I think!
Thanks to all who decide to participate...I owe you one.
GCDEF
July 5th, 2008, 06:30 PM
Knowledge of the syntax doesn't make you a good programmer.
Duoas
July 5th, 2008, 06:47 PM
Yes, but it is often indicative of one. The more a person know about a language's features the more time that person has probably spent using the language. And the more comprehensive that knowledge, the more likely he is a good programmer (or at least not a cheating, lazy one).
In any case, interviewers like dumb questions like:
Why is the following bad:
class foo
{
...
foo( const foo& x )
{
*this = x;
}
...
};
(Not an actual quote.)
Hope this helps.
[edit] Oh, BTW, that is a very real, very valid question.
GCDEF
July 5th, 2008, 07:02 PM
Good programming is about logic, problem solving and decomposition. Syntax is secondary. When I interview people, I don't ask any C++ or syntax questions at all. Who cares. I want to know what problems they've solved, how successful they were and how they went about it.
Paul McKenzie
July 5th, 2008, 07:28 PM
Good programming is about logic, problem solving and decomposition. Syntax is secondary. When I interview people, I don't ask any C++ or syntax questions at all. Who cares. I want to know what problems they've solved, how successful they were and how they went about it.I knew someone who knew all the nuances of the Windows API. The problem is he couldn't write a real program if his life depended on it...
Regards,
Paul McKenzie
GCDEF
July 5th, 2008, 07:51 PM
I knew someone who knew all the nuances of the Windows API. The problem is he couldn't write a real program if his life depended on it...
Regards,
Paul McKenzie
Me too. Whatever new technology came out, he immersed himself in it. STL, Com, whatever it was, he knew it inside out. His knowledge of syntax and tricks is way beyond what mine will ever be. Despite his immense knowledge, his code is unmaintainable junk. I remember taking a very long time trying to explain to him how to calculate an arithmetic mean.
TheCPUWizard
July 5th, 2008, 10:14 PM
Complete agreement with all of the previous assesments. :wave: :thumb:
But this is a favorite of mine...
static int X = f();
int f() { return X; }
int main()
{
cout << X << end;
}
andersod2
July 5th, 2008, 10:16 PM
thanks for your reply duoas,
Ok without cheating....basically you never want to modify the "this" pointer, expecially this way. In fact, this could be an example of the worst case scenario use of *this. It is usurping the allocation of the class (major memory leak). It's also potentially wreaking havoc with the "new"-ing of this class, potentially forcing some kind of access violation. Lot's of negative implications with modifying *this, and should almost never be done. Alternatively, to properly copy an instance of a class, do a member-wise copy in your copy constructor, or simply leave out the copy constructor and the operator= overload and the default behavior will be do implement those functions behind the scenes. Or, if it really was one's intention to simply create a second pointer to the same exact memory space, one can just declare a pointer to that class (or it's parent if applicable), and set the pointer to the first instance's address....
That was a good question...hope I got it right...
Any more?
andersod2
July 5th, 2008, 10:26 PM
haha...good one CPUWizard....ok, you got me on that one....I knew it was a compile error, but I didn't see it clearly....once I compiled it though, it made sense....comes down to compiler choices I would think..
Duoas
July 6th, 2008, 06:27 AM
@GCDEF, Paul McKenzie
I said more likely, not absolutely. I've known my share of 'real programmers' too...
Quite simply, much of the time people doing the hiring are mid-level managers who know very little about actually good programming. If your company doesn't hire that way then you seem to be in a good place.
I would hire people the same way you do.
@CPUWizard
LOL Nice one!
@andersod2
You did, and very comprehensively too. :-)
Zaccheus
July 7th, 2008, 03:58 AM
"Is it at all possible to create a local function inside a function in C++? If so, how?"
"Why might you write a pure virtual destructor with inline implementation?"
Zaccheus
July 7th, 2008, 04:17 AM
@andersod2
You did, and very comprehensively too. :-)
Did he? Personally I found the answer a bit vague but then so was the question. What are the data members? How is operator= implemented.
My question to andersod2 would be: What exactly do you think might happen if the only data member were an std::string or a boost smart pointer?
My answer to Duoas' question would have been that operator= should be implemented via the copy constructor, not the other way around. Using operator= in the copy constructor is either wasteful or downright catastrophic. Because the operator= will assume that the class is already fully constructed, it might try to delete a data member pointer which has not yet been initialised.
And if operator= has correctly been implemented using the copy constructor, an infinite recursion would very quickly crash the application.
andersod2
July 7th, 2008, 05:20 AM
> "Is it at all possible to create a local function inside a function in C++? If
> so, how?" "Why might you write a pure virtual destructor with inline
> implementation?"
All right! Now that's the kind of BS I'm talking about. Ok, I won't cheat.
(1) The important part is, in any case, you would never want to do that. But I'm going to go ahead and say "yes, you can do it." I'm going to say use a label, some brackets, and a jumping method of some type (be it with an inline assembly jmp or...does C++ have a goto statement? haha)....then manage the stack yourself etc. That would be the hard way. If you can use built-in syntax is something I don't even want to know, but my instincts tell me that yes you can because a function call is merely a standard set of actions that are performed according to a calling convention. If one of my employees ever tried it, I'd make them wear their underwear on their head outside in the cold.
(2) Well as with anything in C++, there's always a lot of good reasons to do something no matter how crazy it seems...and this does seem crazy at first glance, but let's just look at it according to what we know. Of course, virtual destructors are what you want just about any time you are using derivation (and definitely if you're using virtual functions). So if we extend that idea out to pure virtual, it just means that we don't want our base-class doing any destruction whatsoever. That could mean the base class was composed of no data to begin with, or for some reason we want to usurp the typical destruction order. But I think the main reason would be that somebody with no knowledge of the class in question is responsible for destroying it via a base class pointer. It would be part of a high-level architecture. The base-class pointer is all the user code would have, so it couldn't know which destructor to call. This is also a good high-performance/efficiency thing since there's no function overhead, and no multiple function calls.
Great questions! Keep 'em comin'!
andersod2
July 7th, 2008, 05:31 AM
> Did he? Personally I found the answer a bit vague...
yes, and vague usually gets the job ;)
> My question to andersod2 would be: What exactly do you think might
> happen if the only data member were an std::string or a boost smart
> pointer?
Well I think you were right in your operator= comments. My comments were based on the assumption that the operation was possible, or that you didn't dereference the "this" pointer and were just casting away the uniqueness of everything to make it a legal operation. If that were the case, then the same comments would be in effect, namely memory leak for sure, and possibly a program crash on destruction. Otherwise, like you say, I think this operation would probably not even be possible to compile/link at best, and catastrophic failure at worst. For it to work, the operator= would have to be defined without the need for any copy construction....sort of like a copy constructor :P
Zaccheus
July 7th, 2008, 06:38 AM
Local function:
void f()
{
struct Local
{
/* Can only be called from inside f(). */
static int average(int a, int b)
{
return (a+b)/2;
}
};
int x1 = 3;
int y1 = 5;
int x2 = 6;
int y2 = 8;
int z1 = Local::average(x1,y1);
int z2 = Local::average(x2,y2);
}
Regarding the pure virtual destructor with inline implementation.
Defining a pure virtual destructor can be useful if (for some reason) you want an abstract base class when there are no other pure virtual functions in that class (I know some would argue against doing that, but that's another matter).
The important thing to remember is that even a pure virtual destructor will always need to be defined, even though it is pure virtual, because the derived class will call it. And if it is trivial (or even empty) then it might as well be inlined.
TheCPUWizard
July 7th, 2008, 06:46 AM
The important thing to remember is that even a pure virtual destructor will always need to be defined, even though it is pure virtual, because the derived class will call it. And if it is trivial (or even empty) then it might as well be inlined.
Depends on the definition of "call". :D
Yes the derived class will "invoke" it, but when declared as inline (and the compiler infact in-lines ir) there will no "call" (transfer of control to antherseries of instructions and back, invalidating the processor pipeline). The invocation will simply be optimized out out existance.
Mr Schroedinger is still looking for his cat... ;) ;)
Zaccheus
July 7th, 2008, 06:55 AM
Yeah that was the whole point, getting rid of the invocation by making it inline. :wave:
My comments were based on the assumption that the operation was possible
as were mine :)
, or that you didn't dereference the "this" pointer
but the code does do that.
and were just casting away the uniqueness of everything to make it a legal operation.
Hmm, I don't see how casting comes into it.
Otherwise, like you say, I think this operation would probably not even be possible to compile/link at best
why not?
, and catastrophic failure at worst.
Which takes us back to my question: "What exactly do you think might happen if the only data member were an std::string or a boost smart pointer?"
class foo
{
std::string data;
public:
foo( const std::string& str )
: data(str)
{
}
foo( const foo& x )
{
operator=(x); // same as: *this = x;
}
Ignoring the fact that the copy constructor & assignment operator are unnecessary in this simple example.
andersod2
July 7th, 2008, 02:16 PM
as were mine :)
but the code does do that.
Hmm, I don't see how casting comes into it.
The original question was, as you say, ambiguous, so it was difficult to determine directly from the code what was intended. So I did not so much look exactly at the code as I did what the poster's point was. I believe the question was intended to reveal a memory leak, though the code does not *exactly* prove this...it's just implied due to lack of other info. The more exact answer would have been your comments, but I didn't get into that.
why not?
Well, if the operator equals also had a copy construction in it, as we fear, then the best case scenario is that the compiler says "you did some circular stuff" or whatever and fails.
Which takes us back to my question: "What exactly do you think might happen if the only data member were an std::string or a boost smart pointer?"
Hmm...at first glance I'm not seeing it, other than the same problem as before. I would need to double check the copy constructors and assignment operators of std::string, but I'm guessing that isn't the problem.
Hermit
July 7th, 2008, 03:30 PM
Hmm...at first glance I'm not seeing it, other than the same problem as before. I would need to double check the copy constructors and assignment operators of std::string, but I'm guessing that isn't the problem.
If you need a hint, the example Zaccheus gave will compile and run fine. The answer you're looking for is more of a stylistic guideline (or "moral") than a specific compile- or run-time problem. Mostly it involves the difference between initialization and assignment, as well as what it means for an object to be "fully constructed".
Can you at least explain the difference between these two pieces of code, and the importance of that distinction?
// A
int a = 0;
// B
int b;
b = 0;
TheCPUWizard
July 7th, 2008, 04:27 PM
Another hint is "Exception Safety".
Try to write a (significant) assignment operator that is guarenteed to not have any memory leaks or any partially initialized objects if an exception is thrown at any sequence point.
andersod2
July 7th, 2008, 04:42 PM
The answer you're looking for is more of a stylistic guideline (or "moral")
Yes, I'm getting the moral part of it, which I think was kind of implied in what Zaccheus was saying before about the problem with the original code. You just don't want your copy constructor and your operator= being dependant on one another. Otherwise the code you posted could produce drastically different results since the first one was copy constructed and the second one was just constructed then assigned with operator=....so but my confusion is perhaps semantic whereas Zacc is saying "what if there was a single member of std::string or a boost smart pointer" which makes it sound as though the problem comes down to having a single member or a specific kind of member. If that's not it, and it's just what you were saying, then, I guess "oh, I see" :P C++ makes it easy to invent new problems that don't exist :)
TheCPUWizard
July 7th, 2008, 04:52 PM
OK, one more hint...
IF you have a class whos members are either POD, or classes which properly implement their respective copy constructors....
How much code do you have to write ti implement a copy constructor?
Between the last two hints, that should be sufficient...
IF not, please provide your physical location, and I will send someone to do a "Mark Harmon" (reference to TV show NCIS) on you. :D :wave: ;)
Arjay
July 7th, 2008, 04:59 PM
IF not, please provide your physical location, and I will send someone to do a "Mark Harmon" (reference to TV show NCIS) on you. :D :wave: ;)Better a 'Mark Harmon' than a 'Tanya Harding' any day of the week.
Hermit
July 7th, 2008, 05:43 PM
Another hint is "Exception Safety".
That's a good point, and a subtle one at that. It is possible to write an exception safe assignment operator without invoking the copy constructor (which would invoke the assignment operator, and so on ad infinitum), even using the no-throw swap method... but it wouldn't be pretty. Basically you'd need a way of effectively creating a copy without using the copy constructor.
TheCPUWizard
July 7th, 2008, 05:46 PM
That's a good point, and a subtle one at that. It is possible to write an exception safe assignment operator without invoking the copy constructor (which would invoke the assignment operator, and so on ad infinitum), even using the no-throw swap method... but it wouldn't be pretty. Basically you'd need a way of effectively creating a copy without using the copy constructor.
1) Unfortunately many people do not consider excptions as they are writing each line of code.. :mad: :sick:
2) There is a big difference between "possible" and "practical", which i think we agree on... :wave:
andersod2
July 7th, 2008, 06:15 PM
> Another hint is "Exception Safety".
True. You would need a well-defined constructor to begin with and a routine that equates to a destructor called from your catch block.
> IF you have a class whos members are either POD, or classes which
> properly implement their respective copy constructors....
> How much code do you have to write ti implement a copy constructor?
I think there is some confusion here as to whether you and Zacc are referring to the same thing. I alluded to the answer to this question earlier when saying that the copy constructor will be implemented automatically. What I didn't say was that the implementation will include a member-wise copy (with operator=), which, under the conditions you gave, should work out well, requiring no real code to be written. Like I say I don't recall the specifics of the operator= for strings and what not. At this point I don't even know what the question is anymore, so go ahead and send Clay Aiken over to sing me a song. I'm certain I'll end up saying "that's what I said, right?"
Zaccheus
July 8th, 2008, 03:58 AM
Yes, I'm getting the moral part of it, which I think was kind of implied in what Zaccheus was saying before about the problem with the original code. You just don't want your copy constructor and your operator= being dependant on one another.
Just to be clear, I was saying that the operator= SHOULD be dependent on the copy constructor, but for very practical reasons - the "Exception Safety" which TheCPUWizard mentioned.
....so but my confusion is perhaps semantic whereas Zacc is saying "what if there was a single member of std::string or a boost smart pointer" which makes it sound as though the problem comes down to having a single member or a specific kind of member. If that's not it, and it's just what you were saying, then, I guess "oh, I see" :P C++ makes it easy to invent new problems that don't exist :)
I wasn't saying there would be a problem, I was asking what you thought would actually happen. Basically I'm trying to understand what you mean by your answer: basically you never want to modify the "this" pointer, expecially this way. In fact, this could be an example of the worst case scenario use of *this. It is usurping the allocation of the class (major memory leak). It's also potentially wreaking havoc with the "new"-ing of this class, potentially forcing some kind of access violation. Lot's of negative implications with modifying *this, and should almost never be done"
Now my comment on the full class I posted would be this: It will compile, link and run just fine, because std::string default constructs itself to a valid object. However it is not-optimal for the data members to first construct themselves and then be overwritten immediately afterwards. On the other hand, if one of the data members is not of a type which default constructs to a valid object (like a raw pointer which will by default contain garbage) then the program will very likely crash.
Mitsukai
July 8th, 2008, 06:19 AM
Just to be clear, I was saying that the operator= SHOULD be dependent on the copy constructor, but for very practical reasons - the "Exception Safety" which TheCPUWizard mentioned.
i disagree, first of all it breaks reservation of memory in allocators, second of all this is redundent code. also where is the logic? the same that it would not be logic to call operator= in the copy constructor for many reasons. This all for a "state" of an object, does it really "beak" the instance? as far as i know exceptions have nothing todo with objects, and as far as i know there isnt really a object or instance.
same goes for protecting the copy assignment for assigning to itself, cus first of all there should not be a problem with assigning to itself, i agree there might be a rare case that a class manages it so it cannot assign to itself. second of all its the programmers fault for assigning to itself. third of all the chance that a instance actually assigns to itself its very rare. It only wastes cpu cycles. And trust me when your doing lots of copying these cpu cycles become precious. and dont tell me to goto C then or ASM cause that is bull ****.
also dont tell me these cpu cycles are nothing in modern computers cus if your programming for a 1 watt processor for a laptop this is a diffrenrt story
Zaccheus
July 8th, 2008, 06:40 AM
Mitsukai, I was talking about the following technique:
MyClass& MyClass::operator=(const MyClass& other)
{
// use copy constructor to create a copy.
// If an exception happens, then 'this will remain unchanged.
MyClass temp(other);
// Use a member function called swap to exchange the internal representations of 'this' and temp, just like std::vector<T>::swap does.
swap(temp); // Cannot throw exceptions!
cause your constructing a whole object on the stack and then wasting another cpu cycles with the swap method... while all u have todo is call a common private function.
Zaccheus
July 8th, 2008, 07:15 AM
I don't see the swap function as a significant waste. Creating the temp on the stack and swapping the internal pointers is almost the same expense as using temporary pointers in the assignment operator function.
How would you write a typical assignment operator then?
Mitsukai
July 8th, 2008, 07:22 AM
I don't see the swap function as a significant waste. Creating the temp on the stack and swapping the internal pointers is almost the same expense as using temporary pointers in the assignment operator function.
first, do you have proof that this is actually true. cause an object is alot heavier than a temp pointer.
and did i mention yet in allocators this breaks the reservation of memory?
TheCPUWizard
July 8th, 2008, 07:24 AM
its bad design...
cause your constructing a whole object on the stack and then wasting another cpu cycles with the swap method... while all u have todo is call a common private function.
No that (Zaccheus post) is a very good design, and the one most recommended for high reliability situations.
The only time to avoid it is if you havwe measured performance and determined that the small differential cost has a significant impact on your documented performance requirements.
No matter where an exception occurs you can never have a corrupted or partial object. Either the assignment will complete 100% or the state of the (externally visible) object will not be impacted. This is guaranteed by design.
Even if you manage to write a "typical" assignment operator that is 100% exception safe, then you still have to develop tests to prove this (and maintain both the code and the tests for the entire lifecycle of the project).
Testing and proving exception safety (in the general case...) is a notoriously hard (and unbelievably tedious) thing to do.
Mitsukai
July 8th, 2008, 07:35 AM
i agree with most of your points but i still stand by my point, and it will probally never change unless i get paid. I still think it as a bad work around. But thats just my point of view.
Zaccheus
July 8th, 2008, 07:35 AM
I would really like to see an exception safe operator= which is a lot faster than the one I showed.
first, do you have proof that this is actually true. cause an object is alot heavier than a temp pointer.
I think it is self evident that an object it is NOT a lot heavier than its individual data members.
and did i mention yet in allocators this breaks the reservation of memory?
How, exactly?
Mitsukai
July 8th, 2008, 07:40 AM
I think it is self evident that it is NOT a lot heavier than equivalent temp pointers.
Prove it to me? and what makes you say that this "lil" bit is not alot on 1watt laptop processors? who are you to say this "lil" overhead is actually not alot?
How, exactly?
cause the whole point of it is to "Adept" to the usage of the class, the more is added the more memory it will reserve. If you create a complete new object all this reserved memory is gone. Also the whole point of the memory reservation is to prevent it from allocating memory every time it needs more space. And then when adding again which is must likely to happen in large applications it has to allocate again all that space. This is for containers ofcourse.
Mitsukai
July 8th, 2008, 07:43 AM
I think it is self evident that an object it is NOT a lot heavier than its individual data members.
one ptr on the stack vs a whole object? there is no need to have tmp vars at all in most cases, so if the obj has 10 var members its even more clear waste of stack and cpu
Zaccheus
July 8th, 2008, 07:46 AM
Please show me the assignment operator implementation you prefer and then we can compare. :)
TheCPUWizard
July 8th, 2008, 07:55 AM
one ptr on the stack vs a whole object? there is no need to have tmp vars at all in most cases, so if the obj has 10 var members its even more clear waste of stack and cpu
Actually adjusting the stack pointer by <32K (signed 16 bit offset) takes FEWER clock cycles than pushing one 32 bit value. So that part of your post is WRONG.
Unless the copy constructor is invoked in the call stack of the maximum stack depth your program ever achieves, then does not waste a single byte of memory. This maximum stack depth will (typically) occur at exactly one point in your program, and if you are working on small systems (embedded, tiny mobile, etc), then you should know exactly where and when this is.
The pre-allocation your are talking about is spefically for the freestore (used with new/delete), this is because there is management overhead of finding the "best" free block, and for collapsing free blocks at the time of delete. It has nothing to do with stack variables.
Zaccheus
July 8th, 2008, 09:53 AM
Here's a concrete example:
#include <cstring>
#include <algorithm>
class Buffer
{
unsigned char* data;
size_t length;
Ok without cheating....basically you never want to modify the "this" pointer, expecially this way.Who is modifying the "this" pointer? Modifying this pointer is a compilation error and the code above doesn't do that.In fact, this could be an example of the worst case scenario use of *this.Meaningless. As for use of the dereferencing of the this pointer, it is perfectly valid.It is usurping the allocation of the class (major memory leak).I don't see a leak forget about it being a major one.It's also potentially wreaking havoc with the "new"-ing of this class, potentially forcing some kind of access violation.No new-ing, and no access violation. What are you talking about?Lot's of negative implications with modifying *this, and should almost never be done.What? What negative implications? You can modify the object pointed to by the this pointer. Why should it almost never be done?Alternatively, to properly copy an instance of a class, do a member-wise copy in your copy constructor,As I see it, I do see a member-wise copy even though the code is not an example of *real* code (in practice). What are you talking about?or simply leave out the copy constructor and the operator= overload and the default behavior will be do implement those functions behind the scenes.To come to a conclusion on that, one needs to know what those (...) stand for in the original snippet. So, a counter-question for clarifications from my side. Should avoid reaching conclusions so early.Or, if it really was one's intention to simply create a second pointer to the same exact memory space, one can just declare a pointer to that class (or it's parent if applicable), and set the pointer to the first instance's address....What? I cannot make out a single sensible meaning out of this.That was a good question...hope I got it right...Horrible, if you frankly ask me. But there's always hope. :)
exterminator
July 8th, 2008, 10:36 AM
@andersod2
You did, and very comprehensively too. :-)Yeah, right. :) It is a bit unfortunate that you say that after seeing that the question came from you.
exterminator
July 8th, 2008, 10:42 AM
My answer to Duoas' question would have been that operator= should be implemented via the copy constructor, not the other way around. Using operator= in the copy constructor is either wasteful or downright catastrophic.Yes. Fully agree. Writing exception safe assignment operators is one advantage you get that way. But I think it is not catastrophic even though there's not enough code to come to a conclusion but it would not be very hard to imagine that the code works except that there's a difference of initialization vs assignment as suggested by Hermit somewhere above I think. Apart from that, I don't see a problem.
Because the operator= will assume that the class is already fully constructed, it might try to delete a data member pointer which has not yet been initialised.That would be the problem with the "other" constructors which left uninitialized pointers. (EDIT: Sorry, on a second thought, yes, it can be a problem).
And if operator= has correctly been implemented using the copy constructor, an infinite recursion would very quickly crash the application.In that case, a simple advice would be "don't work for such a firm whose developers don't know how to write copy constructors and assignment operators". :)
Zaccheus
July 8th, 2008, 11:06 AM
Exterminator, look at my example class here:
http://www.codeguru.com/forum/showpost.php?p=1738543&postcount=42
If the assignment operator in that example were simply ...
Buffer(const Buffer& other)
{
*this = other;
}
... then the application would almost certainly crash when the assignment operator tries to free the existing data in temp's destructor, agreed?
EDIT:
Ah, I see your edit, looks like we are agreed. :)
exterminator
July 8th, 2008, 11:24 AM
Exterminator, look at my example class here:
http://www.codeguru.com/forum/showpost.php?p=1738543&postcount=42
If the assignment operator in that example were simply ...
Buffer(const Buffer& other)
{
*this = other;
}
... then the application would almost certainly crash when the assignment operator tries to free the existing data in temp's destructor, agreed?
EDIT:
Ah, I see your edit, looks like we are agreed. :)Yes, agreed. I am not thinking of as far as the destructor of temp or otherwise. It just slipped my mind that POD types (our pointer member here, considering it is not a smart one but the old, dumb, naked one :) ) remain uninitialized in constructor body if not initialized in the initializer list. And in that case, it would be impossible to predict if a pointer member points to something meaningful or not and hence any free-up on it would be an UB leading to one possibility - a crash. So, we are okay on this one. :)
exterminator
July 8th, 2008, 11:45 AM
haha...good one CPUWizard....ok, you got me on that one....I knew it was a compile error, but I didn't see it clearly....once I compiled it though, it made sense....comes down to compiler choices I would think..Assuming you fix compilation errors related to non inclusion of <iostream> and that you forward declare the function before its use and you qualify namespaces, the code doesn't complain. I am not very sure on this one, but I think it is ill-formed (or atleast that the initialization is meaningless and X has unpredictable value) as it tries using X to initialize X before X is initialized. Comeau compiles the code fine and Visual Studio 2005 prints out "0" with all build configurations and optimization levels. So, I am really unsure what is a definitive answer and I am quite stressed out to look into the standards. Sorry.
ch0co
July 8th, 2008, 02:08 PM
Hey all,
I got a big C++ interview coming up....and I am arrogant and think I am a C++ god...so see if you can stump my knowledge of the language. I solemnly swear I won't cheat and look up the answer, and you can feel free to rip me a new one in your responses. The only requirement is that you can't include anything about particular libraries or interfaces that aren't intrinsic to C++ (stl stuff is ok). The interview won't include things like that or other trivialities which can simply be referenced. They will be asking me things like "name the 3 ways you can blah blah" or "how could the following code be improved...what is the security or memory problem...what weaknesses does it have etc"
Show me what you got! Put me in my place and make me feel like I am going into this interview a lot dumber than I think!
Thanks to all who decide to participate...I owe you one.
Thought this topic was about you ?? now were have you gone ?? lol; no traces of your messages again.
I'm new to C++ (still struggling with it for 2months now :D) what are POD types?? and what is the big deal about this copy constructors??
TheCPUWizard
July 8th, 2008, 02:41 PM
Assuming you fix compilation errors related to non inclusion of <iostream> and that you forward declare the function before its use and you qualify namespaces, the code doesn't complain. I am not very sure on this one, but I think it is ill-formed (or atleast that the initialization is meaningless and X has unpredictable value) as it tries using X to initialize X before X is initialized. Comeau compiles the code fine and Visual Studio 2005 prints out "0" with all build configurations and optimization levels. So, I am really unsure what is a definitive answer and I am quite stressed out to look into the standards. Sorry.
Well I did type the code in raw...thanks for taking the time to get past that... :wave: :thumb:
It actually IS very well defined behavior, although one that some (pre-2003) compilers did not conform to the standard.
disclaimer: I would NOT accept this in any real world design, for two reasons. The defined behaviour is useless, and it takes a significant amount of time to find out that it is useless...
Zaccheus
July 8th, 2008, 05:04 PM
It actually IS very well defined behavior, although one that some (pre-2003) compilers did not conform to the standard.
Hmm, in that case I will have an educated guess and go back to my C roots, where all global data was, by default, initialised to zero. I would therefore think that C++ memsets such global data to zero before any user defined initialisation code is run. Therefore I would guess that X will already be zero when it is returned by f().
Ejaz
July 9th, 2008, 12:39 AM
How should this not be allowed to compile?
CSomeClass obj; // Not allowed
CSomeClass * pObj = new CSomeClass; // Ok
Zaccheus
July 9th, 2008, 03:07 AM
Thought this topic was about you ?? now were have you gone ?? lol; no traces of your messages again.
I hope he'll be coming back.
I'm new to C++ (still struggling with it for 2months now :D) what are POD types?? and what is the big deal about this copy constructors??
POD means plain-old-data, which is (more or less) any type that can safely be copied using the memcpy function. For example, Windows' POINT (http://msdn.microsoft.com/en-us/library/ms536119(VS.85).aspx) structure is a POD type, but std::string is not a POD type.
The big deal with the copy constructors (and assignment operators) is the fact that many many objects will have them, so it is important to get them right consistently.
andersod2
July 9th, 2008, 05:09 AM
Glad to see that the "post in under 24 hours or be subject to sarcasm" rule is still in effect. I'm still here, but have a life so, sorry can't fill up the page with pseudo hi-jacking all day. Anyway, GUYS, I can't believe after the careful scrutiny of my original answer, you didn't see that I had completely misread the code...I was just plain wrong! The original code says:
*this = x;
For some strange reason I saw:
this = &x;
...now you see the memory leak that was so obvious to me, yet so not there. Of course, that code is illegal, but the equivalent is just a small difference with a cast or two and perhaps a void* in there.
Anyway, exterminator, thanks for your comments, even though it's sort of moot by the time you realize I was starting with a bad assumption to begin with. Regarding *this, of course, you'd never (or very rarely) want to change the pointer itself (indirectly - the only way). And, modifying the dereferenced *this, as you know, has some specific, common uses, so I think most C++ coders would recommend not using *this unless you have to as there is almost always another way around it that is clearer, and less error prone.
If I haven't answered somebody else please follow up 'cause I pretty much skimmed the mess you see before you once it started getting livelier. Frankly, I don't feel this thread is super-helpful to any other passers by because there is much miscommunication going on. Would be better to condense the info into something more concise and post it seperately.
BTW: "Effective C++" by Scott Meyers does cover the copy constructor/assignment operator stuff quite clearly...(oops, I cheated).
andersod2
July 9th, 2008, 05:11 AM
Also, again thanks to everyone who is responding. Still waiting for new and wonderful challenges....my interview is just hours away.
Zaccheus
July 9th, 2008, 06:53 AM
Anyway, GUYS, I can't believe after the careful scrutiny of my original answer, you didn't see that I had completely misread the code...
Because you said:
Lot's of negative implications with modifying *this, and should almost never be done.
As you actually wrote "*" in front of "this" yourself, yet you had not noticed the "*"?
Especially as you responded to me saying (http://www.codeguru.com/forum/showpost.php?p=1738041&postcount=17) that "*this = x" is the same as "operator=(x)", without you saying anything about misreading the code in your post #18 (http://www.codeguru.com/forum/showpost.php?p=1738190&postcount=18).
:confused:
Zaccheus
July 9th, 2008, 07:15 AM
Also, again thanks to everyone who is responding. Still waiting for new and wonderful challenges....my interview is just hours away.
Post #52 (http://www.codeguru.com/forum/showpost.php?p=1738783&postcount=52) in this thread. ;)
Zaccheus
July 9th, 2008, 11:14 AM
My comments were based on the assumption that the operation was possible, or that you didn't dereference the "this" pointer and were just casting away the uniqueness of everything to make it a legal operation.
Oh, when you said that you were thinking about:
this = &x;
That sentence makes a bit more sense now.
If that were the case, then the same comments would be in effect, namely memory leak for sure, and possibly a program crash on destruction.
I'm fairly sure the effect of just overwriting the "this" pointer with the address of x would been the same as doing nothing at all in that function.
The "this" pointer is normally just implemented as a hidden parameter which is passed into the function and is effectively a local variable.
So overwriting the "this" pointer, if it were possible, would be like writing the following in C:
yes, like i said, much confusion going on, mostly on my part...that's what I get for "not cheating."
> I'm fairly sure the effect of just overwriting the "this" pointer with the
> address of x would been the same as doing nothing at all in that function.
True, in the case you gave, but the value of this is still the same for the class being referred to...so I was thinking something along the lines of modifying the "this" pointer directly (i.e. taking it's address). After cheating on that, though, the compiler tells me you can't take the address of this (not an l-value), so one would need a fancy schmancy way of creating the equivalent to "this = &x" ....in order to create a memory leak...good for us that C++ makes that difficult to do.
Ok, so in regard to Ejaz' problem....again with no cheating...I will have to say, um, make the constructor private, and overload the new operator. Seems like there is more than meets the eye there, but I could be wrong. If so, there could be a need to make CSomeClass a derived class, but I can't think of why at the moment.
Plasmator
July 9th, 2008, 04:29 PM
Ok, so in regard to Ejaz' problem....again with no cheating...I will have to say, um, make the constructor private, and overload the new operator. Seems like there is more than meets the eye there, but I could be wrong. If so, there could be a need to make CSomeClass a derived class, but I can't think of why at the moment.Assuming no other code, making the destructor private should suffice.
TheCPUWizard
July 9th, 2008, 04:59 PM
How should this not be allowed to compile?
CSomeClass obj; // Not allowed
CSomeClass * pObj = new CSomeClass; // Ok
Along the same lines, but more general...
Is it possible to either:
1) Prevent an object from being created on the stack but allow creation on the "heap?"
2) Prevent an object from being created on the "heap" but allow it to be created on the stack?
If you answer yes for either or both, please provide an example....
[please dont let this degenerate to "heap" vs. "freestore", I admit to being loose with the terms...]
Zaccheus
July 9th, 2008, 05:21 PM
True, in the case you gave, but the value of this is still the same for the class being referred to...so I was thinking something along the lines of modifying the "this" pointer directly (i.e. taking it's address). After cheating on that, though, the compiler tells me you can't take the address of this (not an l-value), so one would need a fancy schmancy way of creating the equivalent to "this = &x" ....in order to create a memory leak...good for us that C++ makes that difficult to do.
I don't understand what you mean by "modifying the 'this' pointer directly (i.e. taking it's address)". In a typical C++ implementation the only "this" pointer is the hidden parameter passed into the function call, which only exists for the duration of the function call. How on earth would you create a memory leak just by modifying the value of that hidden parameter?
Ejaz
July 9th, 2008, 10:26 PM
Assuming no other code, making the destructor private should suffice.
Correct :) However, for real world applications, a separate destruction mechanism would be required to deallocate the object.
Arjay
July 9th, 2008, 10:40 PM
How did the interview go?
andersod2
July 10th, 2008, 02:38 AM
> Is it possible to either:
> 1) Prevent an object from being created on the stack but allow creation
> on the "heap?"
> 2) Prevent an object from being created on the "heap" but allow it to be
> created on the stack?
Ok, for the sake of time I'm going to say yes on both counts, but I don't have time to give examples (not good ones anyway). Basically, your local variables are going to be on the stack, and your dynamic memory will be on the heap. So, as Plasmator revealed, I think the answer to Ejaz' problem fulfills the first one (though nobody said, whether my solution was wrong or right). For the second one, I will just say doing everything normal, except overload new and make it private. Kind of rushing through this, so I haven't given it enough thought.
> How on earth would you create a memory leak just by modifying the
> value of that hidden parameter?
I suppose that would depend on where the parameter came from originally. How is "this" obtained for purposes of pushing the parameter onto the stack? Is it just a matter of taking the address of the object in question? (I don't know) If not, I could imagine a scenario where usurping the address of "this" (in some strange manner) was capable of causing us to lose the address of the object altogether....maybe if "this" was in fact a global variable behind the scenes??... and was just pushed onto the stack as-is, under the assumption that programmers can't access it's address anyway (without trying hard). At any rate, it's not a very enlightening issue since I think we are agreed that it's a good thing that you can't modify "this" very easily. At the very least you've lost your object for the rest of the function call...doh!
Zaccheus
July 10th, 2008, 03:46 AM
Assuming no other code, making the destructor private should suffice.
Wouldn't you need a free() member function which deletes the object?
TheCPUWizard
July 10th, 2008, 06:04 AM
> Ok, for the sake of time I'm going to say yes on both counts, but I don't have time to give examples (not good ones anyway). Basically, your local variables are going to be on the stack, and your dynamic memory will be on the heap. So, as Plasmator revealed, I think the answer to Ejaz' problem fulfills the first one (though nobody said, whether my solution was wrong or right). For the second one, I will just say doing everything normal, except overload new and make it private. Kind of rushing through this, so I haven't given it enough thought.
Anybody else have comments before I reveal the answers (which were published in one of the most often referenced C++ books about 15 years ago...)??
JohnW@Wessex
July 10th, 2008, 06:35 AM
A quick guess would be if you wanted to create on the heap but not on the stack would be to make the constructors private and supply some sort of 'Create' function?
JohnW@Wessex
July 10th, 2008, 06:40 AM
For the the stack but not the heap, maybe declare an overloaded 'new' operator for the class but not define it?
TheCPUWizard
July 10th, 2008, 07:08 AM
Me bad :blush: :blush: :o :o :mad:
I left out the most important qualification (Doh!)...
"...without impacting the ability to create (in the other location) in any way".
My humble apologies..
Joeman
July 10th, 2008, 07:23 AM
I am going to take a rough guess and say the stl heap?
Ejaz
July 10th, 2008, 07:45 AM
1) Prevent an object from being created on the stack but allow creation on the "heap?"
TheCPUWizard, did I get this right:
http://www.codeguru.com/forum/showpost.php?p=1738689&postcount=51
If not, what the *&%! is the answer. :D
JohnW@Wessex
July 10th, 2008, 10:04 AM
How aboutv[i] = ++i;
TheCPUWizard
July 10th, 2008, 10:05 AM
Hmm, in that case I will have an educated guess and go back to my C roots, where all global data was, by default, initialised to zero. I would therefore think that C++ memsets such global data to zero before any user defined initialisation code is run. Therefore I would guess that X will already be zero when it is returned by f().
That is PART of the answer. The second part of the answer has to deal with how the computer detects if statics are initialized. I dont have the spec with my, but it is (as one would assume) a "one-shot" deal.
Therefore the first time the static is referenced (or at some prior point) the assignment (initialization, or construction) must happen once. However if the static is referred to during this process it does NOT happen again.
Many people give the wrong answer that it is a recursive call that will stack crash....
TheCPUWizard
July 10th, 2008, 10:05 AM
I replied there... http://www.codeguru.com/forum/showthread.php?p=1739503&posted=1#post1739503
Sorry for leaving you hanging...
exterminator
July 10th, 2008, 10:39 AM
Hmm, confirmed. CPUWiz is correct. Looked into the standards today just to confirm/refresh my memory. [basic.start.init - 3.6.2/1] :(
The value would be 0 and is well defined. Here the variable with static storage duration is initialized twice. Static initialization happens either to be via zero initialization or constant initialization (if a constant is used). Anything else is dynamic initialization and static initialization happens before any dynamic initialization. There is no constant expression involved, so zero initialization for the static variable happens and it gets the value 0. The function call then forms the dynamic initialization part, wherein, X is dynamically initialized with the statically initialized value of X i.e. 0. And hence, X is 0.
Pasting the code below for reference:static int X = f(); //dynamic initialization via the function call.
//static initialization/zero initialization already has happened before the function call
int f() { return X; } //effectively returns 0.
int main()
{
cout << X << end;
}
Zaccheus
July 10th, 2008, 10:47 AM
So ...
#include <iostream>
int f();
static int X = f();
int f() { return X + 1; }
int main()
{
cout << X << end;
}
and
#include <iostream>
int f();
static int X = f();
int f() { return X++; }
int main()
{
cout << X << end;
}
would be even more fun!
Ejaz
July 10th, 2008, 10:30 PM
v[i] = i++;
is equivelent to
v[i] = i;
i++;
f(v[i],i++);
Undefined.
Well, they both are undefined (http://http://www.research.att.com/~bs/bs_faq2.html#evaluation-order). Actually, if you read a variable twice in an expression where you also write it, the result is undefined.
Btw, I wonder what happened about the interview. :rolleyes:
JohnW@Wessex
July 11th, 2008, 02:48 AM
Well, they both are undefined (http://http://www.research.att.com/%7Ebs/bs_faq2.html#evaluation-order). Your link doesn't work.
For some reason I had thought that the right hand side of the assignment is always evaluated before the left hand side of the assignment, in which case the result would have been well defined.
JohnW@Wessex
July 11th, 2008, 03:39 AM
Well, they both are undefined (http://http://www.research.att.com/%7Ebs/bs_faq2.html#evaluation-order).Yes, when I thought about it some more it makes sense.
Interestingly Visual Studio implements
v[i] = i++;
and
v[i] = ++i;
exactly as my initial gut reaction assumed they did.
Deliberate choice by Microsoft? Who knows.
I did read a comment on another forum that in C# these are well defined.
Zaccheus
July 11th, 2008, 03:44 AM
I did read a comment on another forum that in C# these are well defined.
Does that mean that in C# the right hand side is always evaluated before the left hand side (or is it perhaps the other way around)?
Wanders off to the C# forum ...
JohnW@Wessex
July 11th, 2008, 03:56 AM
Wanders off to the C# forum ...It wasn't on this site.
Joeman
July 11th, 2008, 03:57 AM
You sure? I think he posted this in the C# forum
http://www.codeguru.com/forum/showthread.php?t=457023
Zaccheus
July 11th, 2008, 04:13 AM
It wasn't on this site.
I didn't assume it was, I just went there to ask the question on this site. :)
JohnW@Wessex
July 11th, 2008, 04:26 AM
It was in one of the comments here (http://weblogs.asp.net/kennykerr/archive/2006/03/29/Increment-differences-in-C_2B002B00_-and-C_2300_.aspx).
Not having ever coded in C# I have no knowledge on this aspect.
aljodav
July 11th, 2008, 01:18 PM
Below are two possibilities to instantiate and initialize a vector:
int pint[1234567] = {0};
and
int pint[1234567];
pint[0] = 0;
Why are those two possibilities equivalent? Or they are not equivalent?
Hermit
July 11th, 2008, 02:24 PM
Below are two possibilities to instantiate and initialize a vector:
Not quite. In the first example you are initializing an array (vector is the name of an STL container, so you shouldn't use that word to refer to arrays). In the second example you are declaring an array and assigning, not initializing, its first element to 0.
andersod2
July 11th, 2008, 02:44 PM
> How did the interview go?
Thanks for asking arjay. I think it went well. We shall see soon enough I guess...I have another one coming up soon as well.
aljodav
July 11th, 2008, 05:18 PM
This question is about XOR operation.
Suppose you have two functions:
bool FooA(/*some parameter(s)*/)
{
//
// some code, including vectors (sorry Hermit, I mean array)
//
return some_boolean_value;
}
bool FooB(/*some parameter(s)*/)
{
//
// some code, including vectors (OMG, sorry again Hermit, I mean array)
//
return some_boolean_value;
}
And you want to compute :
bool b = FooA(/**/) ^ FooB(/**/);
The problem is that your computer keyboard IS NOT keying in the '^' symbol and, for some real reason, you cannot use any other keyboard. Which of the defines below would you use to implement the logical xor:
Ok, without cheating...an xor is the same as an inverter (electronics), or a half-adder without a carry....
So I would put:
bool x = FooA();
bool y = FooB();
bool b = (x || y) && (!(x && y));
...perhaps optimizable, but clear enough....fun! hey, some of those xor's look awefully equivalent at first glance....but a first glance is all they'll get :P
TheCPUWizard
July 11th, 2008, 05:55 PM
heh, this is a funny one:
Ok, without cheating...an xor is the same as an inverter (electronics)
Actually "!" (not) is an inverter (eg 7404 TTL chip). You can implement an inverter by using an XOR, and fixing one input at "1"/"true"/"high".
( I actually did this on a design in 1978-1979. Inverters came 6 to a chip, and XOR's 4 to a chip. I needs (6n+1) inverters, and (4n-1) XOR gates. By using the above technique I was able to eliminate one chip, which reduced the space requirements by approx 0.3 sq inches and also reduced the power draw.
[Sometimes I still miss the old days of TTL design. I remember well when it was "new technology" and was just making inroads against RTL and DTL]...
aljodav
July 12th, 2008, 12:14 AM
bool x = FooA();
bool y = FooB();
bool b = (x || y) && (!(x && y));
Yes! You got it right but, try this in a real program:
#define xor2 !=0==!
bool b2 = FooA(/**/) xor2 FooB(/**/);
and tell me which one is simpler. This one I got some time ago in Code Project's forum. :)
aljodav
July 12th, 2008, 02:49 AM
All the following functions will compile successfully... or not?
int FooA(/**/)
{
int n;
// n is used here...
float& f = (float)n;
// f and n are used here...
return n;
}
int FooB(/**/)
{
int n;
// n is used here...
float& f = n;
// f and n are used here...
return n;
}
int FooC(/**/)
{
int n;
// n is used here...
float& f = *(new float(n));
// f and n are used here...
delete &f;
return n;
}
int FooD(/**/)
{
int n;
// n is used here...
float& f = float(n);
// f and n are used here...
return n;
}
Zaccheus
July 12th, 2008, 06:05 AM
tell me which one is simpler.
As you are returning booleans, why not use the logical boolean xor operator which C++ provides:
if(fooA() != fooB())
{
}
:)
Plasmator
July 12th, 2008, 10:20 AM
The problem is that your computer keyboard IS NOT keying in the '^' symbol and, for some real reason, you cannot use any other keyboard. I wonder what direction the interview would take if you answered with either of these:
1. Use a trigraph sequence: ??'
2. ALT + 094
Do you think you'd get the job for being a smart ***? :rolleyes:
aljodav
July 12th, 2008, 02:03 PM
A question about templates is a valid one? If so, please, consider the following declaration:
template <typename T, int depth> class FunnyOne // depth > 0, always.
{
FunnyOne<T, depth - 1> vec;
T val;
public:
FunnyOne() : val(T(10)) {}
T Get() {
return vec.Get();
}
void Set(T val) {
vec.Set(this->val + val);
}
};
template <typename T> class FunnyOne<T, 1>
{
T val;
public:
FunnyOne() : val(T(10)) {}
T Get() {
return this->val;
}
void Set(T val) {
this->val += val;
}
};
So simple that I'm speechless, but before that, let me say that I intend that one of the defines be used. ;)
aljodav
July 12th, 2008, 06:48 PM
Do you think you'd get the job for being a smart ***?
My deer :rolleyes: , I'd rather let such a job be taken by a smart *** as you :rolleyes: . Maybe, you don't like me but **** **** ;) .
aljodav
July 12th, 2008, 07:06 PM
Change that to:
template <int n>
int GetFunnyValue()
{
FunnyOne<int, n> funny;
funny.Set(0);
return funny.Get();
}
Correct! But you may notice that I haven't used any vector (OMG! sorry again, I mean array!)
... and I guess the answer would be 100.
You've just got a job! ;)
Ejaz
July 13th, 2008, 11:05 PM
Maybe, you don't like me but ... ;) .
Stay in line.
aljodav
July 16th, 2008, 01:10 AM
Stay in line.
I'm sorry, I should know ***********************? OMG, I'm definitely OFF THIS LINE!
Ejaz
July 16th, 2008, 03:14 AM
I'm sorry, I should know ************************** OMG, I'm definitely OFF THIS LINE!
No need to be sorry, I'm putting you to the place where you belong.
Zaccheus
July 16th, 2008, 03:26 AM
How do handle agression in interviews! Great derail! Discuss!! :D :thumb:
TheCPUWizard
July 29th, 2008, 09:21 AM
How do handle agression in interviews! Great derail! Discuss!! :D :thumb:
Actually, handling agression can definately be a part of a job interview. Especially if you are going to be in a client (internal or external) facing role.
I have been on both sides (interviewee and interviewer) where "role playing" involved dealing with a very angry "customer".
Interviewing tech people for their "soft" skills is becoming more and more common.
HighCommander4
July 29th, 2008, 06:58 PM
Mitsukai, I was talking about the following technique:
MyClass& MyClass::operator=(const MyClass& other)
{
// use copy constructor to create a copy.
// If an exception happens, then 'this will remain unchanged.
MyClass temp(other);
// Use a member function called swap to exchange the internal representations of 'this' and temp, just like std::vector<T>::swap does.
swap(temp); // Cannot throw exceptions!
return *this;
}
Ok, I can't resist the temptation to enliven the discussion about this technique for implementing the assignment operator.
Exception safe though it may be, IMHO it can be brutally inefficient. Not because of the call to swap() (that can usually be implemented very efficiently), but because creating the temporary object can force memory allocation that would in many cases be unnecessary for the assignment.
Just think, for example, if std::vector or std::string's assignment operators were implemented in this way. Normally, so long as the size of the RHS vector/string does not exceed the capacity of the LHS vector/string, no reallocation is necessary for the assignment. If implemented using this technique, however, then a new piece of memory would be allocated on every assignment, and an old one thrown away: that's an unnecessary call to each of new[] and delete[] on every assignment. Considering how expensive memory allocation can be, I'd advise caution when choosing to use this technique.
Feel free to disagree, this is just my two cents. :)
TheCPUWizard
July 29th, 2008, 08:02 PM
Feel free to disagree, this is just my two cents. :)
Glad to. :D :D
For the mjority of objects, the guarantee that the state will always be 100% consistant overrides the memory concerns and even the time concerns.
If the object happens to be large, then implementing the "bulk" as an independant object eliminates this situation.
About the only place I would agree with your concerns is on (very small) embedded systems.
HighCommander4
July 29th, 2008, 08:16 PM
If the object happens to be large, then implementing the "bulk" as an independant object eliminates this situation.
But, assuming that all Swap() does is swap pointers, then this is now a shallow copy...
Am I missing something?
EDIT: Wait a minute, I misread your code. As it stands now, no copying is done at all! temp's m_Bulk will be uninitialized, and when Swap() is called, the LHS object will end up with an uninitialized m_Bulk member!
TheCPUWizard
July 29th, 2008, 09:41 PM
:eek:
But, assuming that all Swap() does is swap pointers, then this is now a shallow copy...
Am I missing something?
The code is actually missing quite a bit, and was merely intended to illustrate a means where the "cost" of creation of the temporary has been measured to cause a significant application level overhead.
In the 10+ years that Ihave used exception safe assignment, I have had to implement a solution based on the above concept exactly twice. And in one of those cases the code was re-factored within weeks (during the next sprint) to eliminate the need by refining the object model.
I'm afraid this over-simplification makes the point unclear. If s_SuppressBulk is true, then the copy constructor is basically a no-op and m_Bulk is left uinitialized. If s_SuppressBulk is false then the copy constructor performs a shallow copy. Are there other key statements being omitted from your implementation of the copy constructor and assignment operator? Can you elaborate on them? Otherwise I cannot imagine any definition for BigData and Swap that would make this code function correctly.
I would also also create a separate, private constructor rather than use a static flag to alter the behavior of the regular copy constructor. The static variable thing is a bit ugly and makes thread-safety more difficult than it has to be.
PABLO_ALCH
July 30th, 2008, 12:04 AM
"Is it at all possible to create a local function inside a function in C++? If so, how?"
Yes. You create a local struct/class, and override operator().
Why would you want to do this:
'Local' gives you encapsulation. If you modify the 'local function', you only have to re-test the encapsulating function.
Just my two bits,
Pablo.
Zaccheus
July 30th, 2008, 03:05 AM
Just think, for example, if std::vector or std::string's assignment operators were implemented in this way. Normally, so long as the size of the RHS vector/string does not exceed the capacity of the LHS vector/string, no reallocation is necessary for the assignment. If implemented using this technique, however, then a new piece of memory would be allocated on every assignment, and an old one thrown away: that's an unnecessary call to each of new[] and delete[] on every assignment. Considering how expensive memory allocation can be, I'd advise caution when choosing to use this technique.
That's a very good point, one of many special cases where the copy/swap technique can be improved upon. For the bulk of classes, though, I think the copy/swap technique is a good default option.
muse1987
July 30th, 2008, 04:47 AM
Lots of thing to learn here! A very good thread ( except aljodav's posts :P )
Well, how about this one?
Suppose we have a template class
template <unsigned long N>
class Something
{
typedef ...... unsigned_type_according_to_n;
};
The question is : Make "unsigned_type_according_to_n" to be
- unsigned char, if 0 <= N < 256
- unsigned short, if 256 <= N < 65536
- unsigned long, if 65536 <= N
Sorry if too easy ^_^
Zaccheus
July 30th, 2008, 07:35 AM
LOL, muse1987, that's great.
I have no idea, honestly.
Though I suspect the use of template specialisation, I can't think of any way to do it without declaring over 65000 specialised classes. :sick:
TheCPUWizard
July 30th, 2008, 07:53 AM
I'm afraid this over-simplification makes the point unclear. If s_SuppressBulk is true, then the copy constructor is basically a no-op and m_Bulk is left uinitialized. If s_SuppressBulk is false then the copy constructor performs a shallow copy. Are there other key statements being omitted from your implementation of the copy constructor and assignment operator? Can you elaborate on them? Otherwise I cannot imagine any definition for BigData and Swap that would make this code function correctly.
I would also also create a separate, private constructor rather than use a static flag to alter the behavior of the regular copy constructor. The static variable thing is a bit ugly and makes thread-safety more difficult than it has to be.
I am afraid I forgot the axiom:
Make things as simple as possible, but no simpler
:o :o :o
Rather than code, let me just use words....
A conventional (full) implementation would do a deep copy for post the "temp" and within the "swap". For many cases this is not an issue.
The approach can be optimized so that the number of deep copies is reduced to one (leaving the "swap" to be just a shallow copy [the example code implemented the reverse :blush: ]
However, when there is significant overhead to making a deep copy, assignments (regardless of implementation) become expensive regardless of implementation. At best this type of optimization can only provide a <50% improvement (half the time). Refactoring can eliminate the expense entirely (or at least defer it by techniques such as COW).
If the assignment (and/or copy construction) is going to be so expensive that it will impact application performance, then it is usually a good idea to prevent these from being implicitly invoked in any case.
The choice to "munge" the copy constuctor (vs. a specialized constructor) is another issue. If the practice of using initializer lists for all (possible) construction activities is followed, you end up with code duplication at every constructor. This will cause an increase in maintenance costs and lower reliability. But I agree that it is not an "ideal" solution.
JohnW@Wessex
July 30th, 2008, 07:55 AM
LOL, muse1987, that's great.
I have no idea, honestly.
Though I suspect the use of template specialisation, I can't think of any way to do it without declaring over 65000 specialised classes. :sick:Maybe some form of template meta-programming?
TheCPUWizard
July 30th, 2008, 07:57 AM
Yes. You create a local struct/class, and override operator().
Why would you want to do this:
'Local' gives you encapsulation. If you modify the 'local function', you only have to re-test the encapsulating function.
Just my two bits,
Pablo.
That would work, but is probably too obtuse for any real use.
The testing benefit you mention is not real. If you modify a file in any way, you MUST do a 100% retest of the code that is contained in that file. (You would be willing to risk $1,000,000 that you never had a case of "happy fingers" and made a completely unintentional edit at some other point in the file?).
HighCommander4
July 30th, 2008, 10:38 AM
A conventional (full) implementation would do a deep copy for post the "temp" and within the "swap". For many cases this is not an issue.
The approach can be optimized so that the number of deep copies is reduced to one (leaving the "swap" to be just a shallow copy [the example code implemented the reverse :blush: ]
My concern was not having to make two deep copies. That would be simply ridiculous. swap() can almost always be implemented to simply swap the internal representations without doing any actual work.
My concern was the extra memory allocation/deallocation that your method would require. I'm not even talking about particularly large objects. On the contrary, the scenario where this inefficiency would be most pronounced would be the assignment of a large number of small objects. For each assignment, when the temporary object is constructed, a new block of memory will be allocated, and when the temporary object is destructed, the old block of memory that the LHS object used to have (that was swapped into the temporary object) will be freed.
If, as I said, we have a large number of assignments, we have a large number of allocations that would, in a "plain", copy-the-data, implementation, be unnecessary. Given that allocating a block of memory can take significantly longer than copying a small amount of data, this makes the method very inefficient.
If the assignment (and/or copy construction) is going to be so expensive that it will impact application performance, then it is usually a good idea to prevent these from being implicitly invoked in any case.
Agreed. But sometimes you need to be explicitly making copies of objects. In this case, and especially if you have a large number of objects, it pays off for the assignment operation to be as efficient as possible.
The choice to "munge" the copy constuctor (vs. a specialized constructor) is another issue. If the practice of using initializer lists for all (possible) construction activities is followed, you end up with code duplication at every constructor. This will cause an increase in maintenance costs and lower reliability. But I agree that it is not an "ideal" solution.
I'm not sure I understand what you mean here... what do you mean by "munging" the copy constructor?
Plasmator
July 30th, 2008, 10:42 AM
Suppose we have a template class
template <unsigned long N>
class Something
{
typedef ...... unsigned_type_according_to_n;
};
The question is : Make "unsigned_type_according_to_n" to be
- unsigned char, if 0 <= N < 256
- unsigned short, if 256 <= N < 65536
- unsigned long, if 65536 <= N
I've only given this a minute of thought, so it's likely broken (not tested):
template<unsigned long N> class Foo
{
enum
{
VALUE_TYPE_INDEX = (N >= 0 && N < 256) ? TYPE_UCHAR :
(N >= 256 && N < 65536) ? TYPE_USHRT : TYPE_ULONG
};
//Expose this to the user
public: typedef typename XTypeResolver<VALUE_TYPE_INDEX>::value_type value_type;
};
What's your solution?
TheCPUWizard
July 30th, 2008, 03:30 PM
My concern was not having to make two deep copies. That would be simply ridiculous. swap() can almost always be implemented to simply swap the internal representations without doing any actual work.
My concern was the extra memory allocation/deallocation that your method would require. I'm not even talking about particularly large objects. On the contrary, the scenario where this inefficiency would be most pronounced would be the assignment of a large number of small objects. For each assignment, when the temporary object is constructed, a new block of memory will be allocated, and when the temporary object is destructed, the old block of memory that the LHS object used to have (that was swapped into the temporary object) will be freed.
If, as I said, we have a large number of assignments, we have a large number of allocations that would, in a "plain", copy-the-data, implementation, be unnecessary. Given that allocating a block of memory can take significantly longer than copying a small amount of data, this makes the method very inefficient.
I am sure you are aware (but for the other readers), an object which has MANY member objects (not pointers or references) is allocated in one block. so your point may not apply to many instances.
Also local variable allocation typically takes place upon unter to a method, so if there is ANY allocation it is usually done for the maxiumum amount of local memory needed.
Therefore (if not optimized by ther compiler), the following two samples require the exact same amount of allocation overhead.
void f()
{
int i;
}
class X
{
}
class Y
{
X data[1000]
}
void f()
{
X biggie[1000];
}
[/quote]
Both examples will "increment" the stack pointer ONCE and decrement it ONCE upon entry/exit. Hardly an "Expensive" operation.
Agreed. But sometimes you need to be explicitly making copies of objects. In this case, and especially if you have a large number of objects, it pays off for the assignment operation to be as efficient as possible.
Again, if you are going to be copying a large object (into an EXISTING instance!) you really have to look at the design.
Not sure I understand what you mean here... what do you mean by "munging" the copy constructor?
Using an external condition to influence how the copy constructor behaves.
In conclusion (at least for this reply), nobody would accept a database which did not adhere to ACID, yet so many people readily accept this in a program without even thinking about the conseuqences.
When you implement an architecture that 100% guarentees that the state of the entire system will be consistant across every statement boundary (ie the statement will have completed sucessfully, or had no visible impact) the number of test cases required is drastically reduced.
Consider the following (bad code) example:
class X
{
X(int v) {}
X(X const & src)
{
if (m_V == something) {throw....}
}
}
class Y
{
X data[1000];
}
void f(X &x2)
{
X x1;
// code
x2 = x1;
}
There are now at least 999 additional test cases required since the result of calling f(...) has 1002 possible execution paths rather than exactly two.
HighCommander4
July 30th, 2008, 07:53 PM
I am sure you are aware (but for the other readers), an object which has MANY member objects (not pointers or references) is allocated in one block. so your point may not apply to many instances.
Also local variable allocation typically takes place upon unter to a method, so if there is ANY allocation it is usually done for the maxiumum amount of local memory needed.
Therefore (if not optimized by ther compiler), the following two samples require the exact same amount of allocation overhead.
void f()
{
int i;
}
class X
{
}
class Y
{
X data[1000]
}
void f()
{
X biggie[1000];
}
Both examples will "increment" the stack pointer ONCE and decrement it ONCE upon entry/exit. Hardly an "Expensive" operation.
I'm sorry if I wasn't clear enough. I was speaking of an object that handles a piece of dynamic (heap) memory, such as your m_Bulk example.
In a case like that, the constructor of the temporary would allocate a piece of heap memory for the temporary, swap() would simply swap the m_Bulk pointers of the temporary and the LHS object, and the destructor of the temporary would now deallocate the original piece of memory that the LHS object used to hold.
In a conventional implementation (i.e. straight copying, no copy constructor call/temporaries), assuming that the size of the memory block handled by the RHS object does not exceed that handled by the LHS object, no allocation/reallocation would be necesary. Hence the significant overhead, as allocating/deallocating a piece of heap memory can take significantly longer than copying a small chunk of data.
Again, if you are going to be copying a large object (into an EXISTING instance!) you really have to look at the design.
Point taken. Looking back at the examples in my own code that I was thinking of, it was in fact copy construction that was taking place, and not assignment.
muse1987
July 30th, 2008, 10:29 PM
I've only given this a minute of thought, so it's likely broken (not tested): ......
What's your solution?
Exactly.
Zaccheus
July 31st, 2008, 03:24 AM
Exactly.
Very nice indeed!
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.