About constructors and assign operators in C++ - c++

I simply created a class like this:
class GreatClass
{
public:
GreatClass(){cout<<"Default Constructor Called!\n";}
GreatClass(GreatClass &gc){cout<<"Copy Constructor Called!\n";}
GreatClass(const GreatClass &gc){cout<<"Copy Constructor (CONST) Called!\n";}
~GreatClass(){cout<<"Destructor Called.\n";}
GreatClass& operator=(GreatClass& gc){cout<<"Assign Operator Called!";return gc;}
const GreatClass& operator=(const GreatClass& gc){cout<<"Assign Operator (CONST) Called!";return gc;}
};
GreatClass f(GreatClass gc)
{
return gc;
}
and in main() function, there are two versions:
version #1:
int main()
{
GreatClass g1;
GreatClass G = f(g1);
}
version #2:
int main()
{
GreatClass g1;
f(g1);
}
They all generates the SAME output:
Default Constructor Called!
Copy Constructor Called!
Copy Constructor Called!
Destructor Called.
Destructor Called.
Destructor Called.
I do not understand why there is nothing happening when I'm assigning f(g1) to G. What constructor or operator is called at this point?
Thanks.

Compiler implementations are allowed to elide/remove copy constructor calls in certain cases, the example you specify is a good example use case of such a scenario. Instead of creating a temporary object and then copying it to destination object the object is created directly in the destination object and the copy constructor call is removed out.
This optimization is known as Copy elision through Return value optimization.
Also, with C++11 move semantics through rvalue references might kick in instead of the Copy semantics. Even with move semantics the compilers are still free to apply RVO.

Related

When is the compiler allowed to optimize out the copy-constructor [duplicate]

This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 4 years ago.
Today I encountered something I don't really understand about copy constructor.
Consider the next code:
#include <iostream>
using namespace std;
class some_class {
public:
some_class() {
}
some_class(const some_class&) {
cout << "copy!" << endl;
}
some_class call() {
cout << "is called" << endl;
return *this; // <-- should call the copy constructor
}
};
some_class create() {
return some_class();
}
static some_class origin;
static some_class copy = origin; // <-- should call the copy constructor
int main(void)
{
return 0;
}
then the copy constructor is called when assigning origin to copy, which makes sense. However, if I change the declaration of copy into
static some_class copy = some_class();
it isn't called. Even when I am using the create() function, it does not call the copy constructor.
However, when changing it to
static some_class copy = some_class().call();
it does call the copy constructor.
Some research explained that the compiler is allowed to optimize the copy-constructor out, which sound like a good thing. Until the copy-constructor is non-default, as then it might, or might not do something obvious, right?
So when is it allowed for the compiler to optimize out the copy-constructor?
Since C++17, this kind of copy elision is guaranteed, the compiler is not just allowed, but required to omit the copy (or move) construction, even if the copy (or move) constructor has the observable side-effects.
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:
In the initialization of a variable, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the
variable type:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:
T f() {
return T();
}
f(); // only one call to default constructor of T
Your code snippet matches these two cases exactly.
Before C++17, the compiler is not required, but allowed to omit the copy (or move) construction, even if the copy/move constructor has observable side-effects. Note that it's an optimization, even copy elision takes place the copy (or move) constructor still must be present and accessible.
On the other hand, call() doesn't match any conditions of copy elision; it's returning *this, which is neither a prvalue nor a object with automatic storage duration, copy construction is required and can't be omitted.

copy constructor VS constructor with a class pointer as parameter

Q1: Does the "self class typed pointer constructor" have a decent/official name?
Q2: Why is copy constructor much more famous than "self class typed pointer constructor"?
Or what are the cases that we must use copy constructor rather then "self class typed pointer constructor"?
class MyClass
{
public:
int i;
MyClass()
{
i = 20;
}
//Copy constructor
MyClass(MyClass const & arg)
{
i = arg.i;
}
//What kind of Constructor is this one?
MyClass(MyClass* pArg)
{
i = pArg->i;
}
};
int main() {
MyClass obj;
MyClass* p = new MyClass();
//call copy constructor
MyClass newObj(obj); //call copy constructor directly
MyClass newObj2(*p); //dereference pointer and then call copy constructor
MyClass* pNew = new MyClass(*p); //dereference pointer and then call copy constructor
//Call THE constructor
MyClass theObj(&obj); //get the address, call THE constructor
MyClass theObj2(p); //call pointer constructor directly
MyClass* ptheNew = new MyClass(p); //call pointer constructor directly
}
It has no special name, because there is nothing special about it.
The copy constructor is "more famous", because it is special. It is special because it is a fundamental part of the way the language works. If you don't declare a copy constructor, in most classes, one will be implicitly defined for you. It is involved in many basic operations, such as these:
void foo(MyClass obj) // the copy ctor is (potentially) called to create this parameter
{
...
}
MyClass bar() // the copy ctor is (potentially) called when this function returns, and when it result is used
{
...
}
MyClass a;
MyClass b(a); // This calls the copy constructor
MyClass c = b; // So does this
Note that, in many cases, the copy is optimized away. See Copy Elision. Also, in C++11, the move constructor is called in many places where the copy constructor used to be called. But the move constructor too can be optimized away in the same places that copy elision could happen.
I can't think of many reasons you would ever use "THE constructor", as you call it.
On a side note, a copy constructor should almost always have this signature:
MyClass(MyClass const &)
Not this one:
MyClass(MyClass &)
To an object in C++, we usually use reference rather than pointer. We can use reference to get the address of the object, and than you can use '.' to access its method or data, it is more simple and direct than '->'.
I think there is no need to use 'THE constructor'.

constructing a new object and assignment in the same step

const ClassA& curShot = vec_shots[curShotIndx];
In the above code the curShot object is constructed and assigned at the same step. My question is which constructor is used in the above statement. I thought it will be the default constructor followed by the assignment operator, but it seems to call the copy constructor instead. Why is that the case?
What happens is that
vec_shots[curShotIndx];
returns a reference which you assign to const ClassA& curShot. There is no object creation involved in this step. Therefore no constructor is invoked (neither default nor copy constructor).
The assignment operator is not invoked either since you are not assigning one object instance to another, but only a reference. You are not handling more than one (existing) object instance in this code. So, no construction or assignment is invoked.
Since you wrote "it seems to call copy constructor", I assume the ampersand in your question is a typo.
In that case, if you would do
const ClassA curShot = vec_shots[curShotIndx];
it is evaluated as copy construction. It is just the same as the ugly const ClassA curShot( vec_shots[curShotIndx] ).
However, if you write
ClassA curShot; // I skipped the "const", because otherwise the example would be invalid.
curShot = vec_shots[curShotIndx];
then a default constructor gets called and an opearator= is called on the second line.
Moreover, "=" so much can mean calling NEITHER copy constructor NOR operator=, that you can have this:
const ClassA f(){ return ClassA(); }
//...
const ClassA curShot = f(); // we would expect here a copy constructor call
Here -- if the compiler uses return value optimization and usually it does -- only a default constructor gets called for curShot.
No constructor is used, curShot is a reference, an alias to an already existing object, not a stand-alone object by itself.
Also, initialization and assignment cannot be done at the same step. For example, say you had
ClassA original;
ClassA copy = original;
Here, copy is not assigned original, it's initalized using original. This is called copy initialization.
If you did
ClassA copy2(original);
this would be called direct initialization.
The copy constructor would be called in both instances. (copy elision can occur, so it might not be called, but it must be available)
Assignment is when you use operator = on an already existing object:
ClassA x;
ClassA y;
x = y; //assignment
This statement just define curShot as a reference, it's not a new object.

C++ Object Instantiation vs Assignment

What is the difference between this:
TestClass t;
And this:
TestClass t = TestClass();
I expected that the second might call the constructor twice and then operator=, but instead it calls the constructor exactly once, just like the first.
TestClass t;
calls the default constructor.
TestClass t = TestClass();
is a copy initialization. It will call the default constructor for TestClass() and then the copy constructor (theoretically, copying is subject to copy elision). No assignment takes place here.
There's also the notion of direct initialization:
TestClass t(TestClass());
If you want to use the assignment operator:
TestClass t;
TestClass s;
t = s;
The first case is quite simple - constructs an instance using the default constructor.
The second class is Constructing an anonymous object and then calling the copy constructor. Notice that here the = is not assignment, it's similar to (but not identical) writing:
TestClass t(TestClass());
We can verify that this needs the copy constructor to be available by making it unavailable, e.g.:
#include <iostream>
struct TestClass {
TestClass() { std::cout << "Ctor" << std::endl; }
TestClass(const TestClass&) = delete;
};
int main() {
TestClass t = TestClass();
}
Which fails to compile because of the deleted copy constructor. (In C++03 you can use private: instead).
What's actually happening most likely though is that your compiler is doing Return value optimisation, whereby it's allowed to ommit the call to the copy constructor entirely provided a suitable one exists and would be accessible.
In the first one, you are calling the default constructor implicitly. And in the second one you're calling it explicitly.
The latter one could call copy constructor and thus requires one to be public.
Edit: I certainly drew far too big conclusions from the type name you used. The sentence above only applies for class-types (i.e. not POD). For POD types, the former leaves the variable uninitialized, while the latter initializes it with so-called "default" value.

C++ copy constructor invocation

As far as i know, a copy constructor is invoked in the following scenarios :
1) Pass by value
2) Return by value
3) When you create and initialize a new object with an existing object
Here's the program :
#include <iostream>
using namespace std;
class Example
{
public:
Example()
{
cout << "Default constructor called.\n";
}
Example(const Example &ob1)
{
cout << "Copy constructor called.\n";
}
Example& operator=(const Example &ob1)
{
cout << "Assignment operator called.\n";
return *this;
}
~Example()
{
cout<<"\nDtor invoked"<<endl;
}
int aa;
};
Example funct()
{
Example ob2;
ob2.aa=100;
return ob2;
}
int main()
{
Example x;
cout << "Calling funct..\n";
x = funct();
return 0;
}
The output is:
Default constructor called.
Calling funct..
Default constructor called.
Assignment operator called.
Dtor invoked
Dtor invoked
Please correct me, IIRC the following sequence of calls should occur :
1) Constructor of x is called
2) Constructor of ob2 is called
3) The function returns and so copy constructor is invoked (to copy ob2 to unnamed temporary variable i.e funct() )
4) Destructor of ob2 called
5) Assign the unnamed temporary variable to x
6) Destroy temporary variable i.e invoke its destructor
7) Destroy x i.e invoke x's destructor
But then why copy constructor is not invoked and also only 2 calls to dtors are there whereas i expect 3.
I know compiler can do optimizations, however, is my understanding correct ?
Thanks a lot :)
Regards
lali
A copy constructor might not be invoked when you return by value. Some compilers use return value optimization feature.
Read about "Return Value Optimization"
The part of the standard which tells you when compilers may elide copies is 12.8/15. It's always up to the compiler whether to do actually perform the elision. There are two legal situations, plus any combination of them:
"in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type"
"when a temporary class object that has not been bound to a reference would be copied to a class object with the same cv-unqualified type".
The former is usually referred to as the "named return value optimization", and it's what permits the output you're seeing in your example. The latter in effect turns copy-initialization into direct initialization, and could occur for instance if your code did Example x = Example();.
Other copy elisions are not permitted, except of course that the usual "as-if" rules apply. So if the copy constructor has tracing in, then the following code must call it:
Example x;
Example y = x;
But if x were otherwise unused, and the cctor had no side-effects, then I think it could be optimized away, just like any other code that does nothing.
When doing x = funct(); the compiler notices that it will be directly returned and thus avoids a useless construction. That's also why you will only get two destructor calls.
This is a example why sometimes working with "copy" isn't necessarily a lost of performances.
In your exampe the structure is small enough therefore it is passed through a register. The generated code is similar to Return value optimization. Construct a more complicated example, and you'll see the behavior expected.
g++ v4.4.1 has an option to suppress "elide" optimizations:
tst#u32-karmic$ g++ -fno-elide-constructors Example.cpp -o Example
tst#u32-karmic$ ./Example
Default constructor called.
Calling funct..
Default constructor called.
Copy constructor called.
Dtor invoked
Assignment operator called.
Dtor invoked
Dtor invoked
As you can see the copy constructor is now called!