Click to See Complete Forum and Search --> : [RESOLVED] Copy constructor vs copy assignment operator


sszd
June 24th, 2008, 04:10 PM
Take the following code...

Base b1;
Base b2 = b1;

Is it merely a compiler optimization that causes the copy constructor to get called vice the copy assignment operator? Or, is this a hard and fast rule? Or, is it merely the tact that compiler writers have taken when such a construct is written? And, could this be different with different compilers?

Graham
June 24th, 2008, 04:37 PM
Nope. Your second line shows copy construction, not copy assignment. Don't be fooled by the equals sign, this is not an assignment.

Graham
June 24th, 2008, 04:44 PM
In fact, to go a bit further, the following:

class bar
{
public:
bar();
bar(int);
};

int main()
{
bar b = 1;
}

also shows copy construction, but slightly more subtly: first a temporary bar object is created using the bar(int) constructor, then that temporary is used to copy construct b. Note the difference between this and

bar b(1);

which is direct construction of b using the bar(int) constructor.

In the first example above, compilers are allowed to optimise away the copy construction, and plant code equivalent to the second example, but only if the copy constructor is actually accessible. So, if bar had a non-public copy constructor, the second example would compile, but the first would fail, even though - in practice - the copy constructor wouldn't be called. It has to act as if the copy constructor was called.

sszd
June 24th, 2008, 04:49 PM
Nope. Your second line shows copy construction, not copy assignment. Don't be fooled by the equals sign, this is not an assignment.
So, what you're saying is there is no compiler writer in their right mind that would take this construct and call the default constructor followed by the copy assignment operator... or some other non-sense?

Is it written in the specs anywhere that this construct must absolutely call the copy constructor, and only the copy constructor?

sszd
June 24th, 2008, 05:14 PM
...compilers are allowed to optimise away the copy construction, and plant code equivalent to the second example, but only if the copy constructor is actually accessible.
Ok, this makes perfect sense. And it seems reasonable that this would be the best way to implement such a construct. Thank you for the astute explanation.

I am still curious whether or not the spec mentions anything about this.

TheCPUWizard
June 24th, 2008, 06:27 PM
So, what you're saying is there is no compiler writer in their right mind that would take this construct and call the default constructor followed by the copy assignment operator... or some other non-sense?

Is it written in the specs anywhere that this construct must absolutely call the copy constructor, and only the copy constructor?

YES. If a compiler writer did what you suggest above, he would be creating some other language, for it surely is not C++.

Graham
June 25th, 2008, 03:53 AM
It's covered iin section 8.5 of the standard. There's too much there to quote in full, but this section gives a flavour of what it says:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that
can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function
is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize,
according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

Paul McKenzie
June 25th, 2008, 08:28 AM
Basically, what CPUWizard and Graham are stating is that the copy constructor is called whenever the compiler feels neccessary, (necessary meaning the compiler cannot or does not have the ability to optimize the copy construction away).

Regardless, the coder must always write their user-defined copy constructor if the language requires it (i.e. the class has reference members, and a copy is invoked, even if the compiler can optimize the copy away).

Moreover, many times on interviews, the interview question is "how many times is the copy constructor called in this example?". This is a trick question, since the answer is "it depends on the compiler", and never a concrete number of times. If the answer the interviewer gives is "it is called twice" or "it's called three times", or some set number of times, the interviewer should be the one looking for a job, as that answer is incorrect.

Regards,

Paul McKenzie

TheCPUWizard
June 25th, 2008, 09:00 AM
Moreover, many times on interviews, the interview question is "how many times is the copy constructor called in this example?". This is a trick question, since the answer is "it depends on the compiler", and never a concrete number of times. If the answer the interviewer gives is "it is called twice" or "it's called three times", or some set number of times, the interviewer should be the one looking for a job, as that answer is incorrect.

Regards,

Paul McKenzie

Giving away an Interview "secret". One sample I ask developers to walk through involves a copy constructor which increments a static variable.

The question is what is the MAXIMUM value after running the sample.

The candidate has access to a computer with a full dev environment, and the source is already in the project. However the compiler used will optimize away 3 of the possible 5 copy constructors...so if they run it, instead of analyzing it, they will get it wrong..

disclaimer: My interviews are designed to be hard. A highly qualified candidate will typically get only 66-75% of the questions correct. It is what I learn by discussing their answers that really matters.....

Mike Pliam
June 25th, 2008, 02:45 PM
I have always found this to be a very confusing subject.

Some have suggested that if the class coder doesnt supply an assignment operator and a copy constructor, the compiler will automatically provide one.

Others have suggested that, when writing class code, the coder should always provide an assignment operator and a copy constructor.

Paul's comments suggests that only a copy constructor is required under specific circumstances:

Regardless, the coder must always write their user-defined copy constructor if the language requires it (i.e. the class has reference members, and a copy is invoked, even if the compiler can optimize the copy away).


I have written an array container class that has both a copy constructor and an assignment operator, without which B = A; results in an empty object B (no array values), so I presume that one or both are necessary. As it stands, B = A copies all the values of A into B in both release and debug modes, when B is an empty object.

But, when I instantiate a third class object, C, with container values, and try to code a second copy to B, i.e., B = C; THE CODE WILL COMPILE AND RUN TO COMPLETION WITHOUT ERRORS IN DEBUG MODE, BUT A RUNTIME ERROR IN RELEASE MODE.

After struggling for hours, I thought perhaps members of this thread might have some ideas. The code is rather complex and extensive and cannot be readily posted. Also, it uses boost::multi_array class.

:confused:

Graham
June 25th, 2008, 03:17 PM
The compiler will provide certain member functions for you if

You do something that needs them and
You don't provide them yourself
these functions are:

copy constructor;
copy assignment operator;
destructor;
two (const and non-const) address-of operators

In addition, if you do not define any constructor yourself, the compiler will provide a default constructor.

Whether the compiler-generated copy constructor and copy assign are sufficient depends entirely on the details of the class. As a rule of thumb, if you need to do any tidying up in your destructor (freeing a resource), then you probably need to write (or disable) the copy ctor and copy assign operator. This is because the default copy is memberwise: that is, the default copy constructor initialises each member of the class using that member's own copy constructor (basic types such as int are deemed to have copy constructors for the purposes of this definition). Similarly, the copy assign assigns each member using the appropriate assignment operator.

If your class contains only members for which simple copying is sufficient, then you can rely on the compiler-generated functions. If, however, your class contains members which it would be dangerous to copy (pointers to allocated memory, for example), then you should either implement copying to do deep copy, or you should disable copying by making the copy ctor and copy assign private.

TheCPUWizard
June 25th, 2008, 04:24 PM
THE CODE WILL COMPILE AND RUN TO COMPLETION WITHOUT ERRORS IN DEBUG MODE, BUT A RUNTIME ERROR IN RELEASE MODE.

:confused:

You are probably confiused because there is no such thing as Release or Debug "Mode"

There are build configurations. Each configuration consists of approx 100 different switch settings. The IDE creates two configurations by default. These can be modified (but NEVER should be), you can create many unique configurations of your own.

Some of the switches impact the initialization of memory (zero before use and/or usage of guard bytes, etc). If you have a poorly written program, then these changes may impact you. If you have a properly written program, these changes have no impact.

So many people keep forgetting this simple fact.. :sick: :sick:

Paul McKenzie
June 25th, 2008, 05:23 PM
Paul's comments suggests that only a copy constructor is required under specific circumstances:You are mixing up two different things

1) What is required by your program to behave correctly
2) What is required by the C++ language to compile your code without errors.

I am refering specifically to 2), not 1).

There was no requirement in your case to provide a copy constructor for your code to compile. You could have left it out -- the problem is that your program won't be stable when making copies.

You must code a copy constructor if your class has reference members, and you try to do a copy. There are other scenarios where you must provide a copy constructor due to compilation issues, not runtime issues.

Regards,

Paul McKenzie