copy_constructor doesnt get called ... why ?

M

Mike

The copy_constructor is called when a function returns an
object (copys it out) right?
But in this program the operator+ function doesnt call it.

Can someone tell me why please ?
Thanks!! :)

----8<----8<----8<----8<----8<----

#include <iostream>
using namespace std;

class Complex {
protected:
float re, im;
public:
Complex(float re, float im) {
cout << "constructor\n";
this->re = re;
this->im = im;
}

Complex(const Complex& x) {
cout << "copy_constructor\n";
re = x.re;
im = x.im;
}

Complex& operator=(const Complex& x) {
if(this != &x) {
cout << "operator=\n";
re = x.re;
im = x.im;
}
return *this;
}

~Complex() {
cout << "destructor\n";
}

void Print(void) {
cout << "re: " << re << " im: " << im << endl;
}

friend Complex operator+ (const Complex& a, const Complex& b);
};

Complex operator+ (const Complex& a, const Complex& b) {
cout << "operator+\n";
Complex tempc(a.re + b.re, a.im + b.im);
return tempc; // <----- !!! THIS SHOULD CALL THE copy_constructor !!!
}

Complex test(Complex c) {
c.Print();
return c;
}

int main(void)
{
Complex c1(1.1, 2.2), c2(11.11, 22.22);

c1.Print();
c2.Print();

cout << "** start c1 = c1 + c2;\n";
c1 = c1 + c2;
cout << "** end c1 = c1 + c2;\n";

c1.Print();
c2.Print();

return 0;
}
/*
this is the program output:

constructor
constructor
re: 1.1 im: 2.2
re: 11.11 im: 22.22
** start c1 = c1 + c2; <-----+
operator+ |
constructor | the copy_constructor should
operator= | be called somewhere here ...
destructor |
** end c1 = c1 + c2; <-----+
re: 12.21 im: 24.42
re: 11.11 im: 22.22
destructor
destructor
*/

----8<----8<----8<----8<----8<----
 
M

Mike

I am using "gcc version 3.3 20030226 (prerelease) (SuSE Linux)":

mike@zeus:~> c++ -v
Reading specs from /usr/lib/gcc-lib/i486-suse-linux/3.3/specs
Configured with: ../configure --enable-threads=posix --prefix=/usr
--with-local-prefix=/usr/local --infodir=/usr/share/info
--mandir=/usr/share/man --libdir=/usr/lib
--enable-languages=c,c++,f77,objc,java,ada --disable-checking
--enable-libgcj --with-gxx-include-dir=/usr/include/g++
--with-slibdir=/lib --with-system-zlib --enable-shared
--enable-__cxa_atexit i486-suse-linux
Thread model: posix
gcc version 3.3 20030226 (prerelease) (SuSE Linux)
 
S

Suzanne Vogel

Hmm. The copy constructor gets called using Visual C++ 7.0.

constructor
constructor
re: 1.1 im: 2.2
re: 11.11 im: 22.22
** start c1 = c1 + c2;
operator+
constructor
copy_constructor <--+ note this output
destructor <--+
operator=
destructor
** end c1 = c1 + c2;
re: 12.21 im: 24.42
re: 11.11 im: 22.22
destructor
destructor
 
R

Rolf Magnus

Mike said:
The copy_constructor is called when a function returns an
object (copys it out) right?
But in this program the operator+ function doesnt call it.

It's a compiler optimization that is explicitly allowed by the C++
standard.

Complex operator+ (const Complex& a, const Complex& b) {
   cout << "operator+\n";
   Complex tempc(a.re + b.re, a.im + b.im);
   return tempc; // <----- !!! THIS SHOULD CALL THE copy_constructor !!!
}

Without that optimizaion, the operator+ locally creates the space for
temprc and constructs it into that space. Then it has to copy it into
the (caller provided) return value using the copy constructor, since
the operator+'s local variables will be destroyed on return. With the
optimization, the operator+ directly constructs tempc into space
provided by the calling function, and so the additional copy can be
elided. The C++ standard explicitly says that this is allowed, even if
that copy constructor has side effects (like e.g. your
cout << "copy_constructor\n";).
 
S

Shane Beasley

Mike said:
The copy_constructor is called when a function returns an
object (copys it out) right?
But in this program the operator+ function doesnt call it.

Can someone tell me why please ?

12.8/15:
"Whenever a temporary class object is copied using a copy constructor,
and this object and the copy have the same cv-unqualified type, an
implementation is permitted to treat the original and the copy as two
different ways of referring to the same object and not perform a copy
at all, even if the class copy constructor or destructor have side
effects."

If a function returns a temporary, and it can be arranged so that the
caller can use it directly, there's no meaningful reason to copy it to
a second temporary, just to destroy both shortly thereafter. C++
simply allows compilers to make the appropriate arrangements.
Complex operator+ (const Complex& a, const Complex& b) {
cout << "operator+\n";
Complex tempc(a.re + b.re, a.im + b.im);
return tempc; // <----- !!! THIS SHOULD CALL THE copy_constructor !!!
}

Some compilers perform what is called "named return value
optimization" (NRVO) wherein the copy constructor is "elided" (not
called) where unnecessary.

NB: Even more compilers perform unnamed return value optimization
(simply RVO), which is like NRVO except it only works when you return
an unnamed object:

Complex operator+ (const Complex& a, const Complex& b) {
cout << "operator+\n";
return Complex(a.re + b.re, a.im + b.im); // no name; may use RVO
}

For best results, prefer this syntax where applicable.
int main(void)
{
Complex c1(1.1, 2.2), c2(11.11, 22.22);

c1.Print();
c2.Print();

cout << "** start c1 = c1 + c2;\n";
c1 = c1 + c2;

c1's operator= expects a Complex, and (c1 + c2) returns a Complex, so
the compiler is free to use the returned value directly in the
assignment without extraneous copying.
cout << "** end c1 = c1 + c2;\n";

c1.Print();
c2.Print();

return 0;
}
/*
this is the program output:

constructor
constructor
re: 1.1 im: 2.2
re: 11.11 im: 22.22
** start c1 = c1 + c2; <-----+
operator+ |
constructor | the copy_constructor should
operator= | be called somewhere here ...
destructor |
** end c1 = c1 + c2; <-----+
re: 12.21 im: 24.42
re: 11.11 im: 22.22
destructor
destructor
*/

g++ 2.95.4 *does* call the copy constructor in that region, but g++
3.2 does not; the latter version performs NRVO. (Note: The former
performs RVO and would not have called the copy constructor if you had
returned an unnamed object.)

- Shane
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,128
Latest member
ElwoodPhil
Top