1

As far as I know you call the copy constructor in the following cases:
1 When instantiating one object and initializing it with values from another object
2 When passing an object by value.
3 When an object is returned from a function by value.
I decided to put this to the test and I made this small program testing this (with messages each time a constructor is called. It seems to work for the first two cases, but not for the third one. I want to find out my mistake. Ideas are welcomed.

#include <iostream> using namespace std; class Circle{ private: double* data; public: Circle(); Circle(double* set); Circle(const Circle& tt1); ~Circle(); Circle& operator=(const Circle& tt1); }; Circle :: Circle() { cout << "Default constructor called" << endl; data = NULL; } Circle :: Circle(double* set) { cout << "Set up constructor called" << endl; data = new double[3]; copy(set, set+3, data); } Circle :: Circle(const Circle& tt1) { cout << "Copy constructor called" << endl; data = new double[3]; copy(tt1.data, tt1.data+3, this->data); } Circle :: ~Circle() { cout << "Destructor called!" << endl; delete[] data; } Circle& Circle :: operator=(const Circle& tt1) { cout << "Overloaded = called" << endl; if(this != &tt1) { delete[] this->data; this->data = new double[3]; copy(tt1.data, tt1.data+3, this->data); } return *this; } void test2(Circle a) { } Circle test3() { double arr [] = { 3, 5, 8, 2}; Circle asd(arr); cout<< "end of test 3 function" << endl; return asd; } int main() { cout <<"-------------Test for initialization" << endl; double arr [] = { 16, 2, 7}; Circle z(arr); Circle y = z; cout << "-------------Test for pass by value" << endl; test2(z); cout <<"------------- Test for return value-------"<<endl; Circle work = test3(); cout<< "-----------Relese allocated data" << endl; return 0; } 
3
  • 4
    You did no mistake. It was simply optimized away. Commented May 30, 2013 at 19:16
  • 1
    Wow. I spent literally a couple of hours thinking about this. Well at least I will not forget it now! Commented May 30, 2013 at 19:18
  • 1
    @Bloodcount the important thing is that you learned it and won't forget it. Those are a couple of hours well-spent if you ask me. Commented May 30, 2013 at 20:02

2 Answers 2

8

Because of Return Value Optimization the 3rd test case's copy constructor call is optimized away by the compiler.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! Noted and Learned!
3

In virtually all cases, the compiler is not allowed to change the meaning of your code. It can (and will when optimising) change your code dramatically from what you wrote into something more optimal whilst never changing the observable behaviour of your code.

This is why when debugging you'll see many confusing things as the generated code does something totally different from what you wrote whilst keeping the observable state intact. This actually makes writing debuggers hard - if for instance you want to examine the value of a variable whilst debugging, the compiler may've decided that the variable didn't even need to exist and so in that case what should the debugger show?

In a very small number of cases the compiler is allowed to change the meaning of your code. The RVO and NRVO [same link as above] are two examples of these - the compiler is allowed to elide the copy constructor in these limited cases - which is what you are seeing. The compiler should still check that your copy constructor exists and is accessible - for instance it isn't private, deleted or impossible to generate - but it can then not use it.

For this reason Copy Constructors [and by inference destructors] should just do the normal thing - as if they're elided you'll get observably different behaviour.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.