C++ Object Instantiation vs Assignment - c++

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.

Related

trivial assignment

In trying to understand this answer, it seems that new can be classified as a "copy constructor" and delete sometimes as a "trivial destructor".
I can find next to nothing (that I can quickly grasp) on "trivial assignment" except that it is "trivial if it is implicitly declared, if its class has no virtual member functions or virtual base classes, and if its direct base classes and embedded objects have a trivial assignment operator".
I found a question on yahoo about implicit declaration, but to my surprise, it did not answer.
My brain hurts after reading about virtual member functions.
Besides, I'm a monkey-see-monkey-do programmer, so the only way I'm going to get this is to see it in action. Please explain with respect to the above definition of "trivial" by using the example provided in the first answer, that std::string has a trivial assignment operator by using new or delete or for a less apparent reason.
New can use a copy constructor, and delete uses a destructor. The copy constructor or destructor may be trivial.
That being said, there is a STRONG chance that you will not need to worry about whether a constructor/destructor is trivial for a long time.
new calls a constructor to construct the object. If the one argument to the constructor of type T is an instance of T that is a copy constructor: you are trying to construct an instance of one object from another
class Foo
{
public:
Foo(int x) // not a copy constructor
: mX(x)
{ }
Foo(const Foo& inOther) // copy constructor
: mX(inOther.mX)
{ }
private:
int mX;
};
class Bar
{
public:
Bar(int x)
: mX(x)
{ }
// no copy constructor specified.. C++ will build an implicit one for you
private:
int mX;
}
};
Foo a(1); // uses the first constructor (not a copy)
Foo b(a); // uses a copy constructor
Foo c = a; // copy constructor
Foo* d = new Foo(1); // construct a new instance of Foo (not a copy)
Foo* e = new Foo(a); // copy
Bar f(1); // normal constructor
Bar g(f); // IMPLICIT copy constructor
If your class does not have a copy constructor, like Bar, C++ usually provides you one (always provides you one unless you have an explicit constructor or delete the copy constructor with a C++11 keyword). This copy constructor is very straightforward: it copies each member of your class.
A trivial copy constructor is special. A trivial copy constructor can only be created when the copy constructor is implicitly created for you by the compiler and:
All members of your class are trivially copyable
You do not have any virtual methods or virtual base classes
All of your base classes are trivially copyable.
If you specify a constructor in your class, it is not trivial, by definition. Foo does not have a trivial copy constructor because it is user defined. Bar has an implicit copy constructor because it is not user defined. The implicit copy constructor IS trivial, because copying mX is trivial (copying ints is trivial).
Similar rules go for destructors. A trivial destructor follows the same rules, and delete
WHat does it do for you? The spec lists a few key behaviors about trivial constructors/destructors. In particular, there's a list of things you can do if you have a trivial constructor and destructor that are illegal otherwise. However, they are all very nuanced, and unimportant to 99.9% of C++ code development. They all deal with situations where you can get away with not constructing or destructing an object.
For example, if I have a union:
union MyUnion {
int x;
ClassA y;
ClassB z;
}
If y and z have trivial constructors and destructors, C+ will write a copy constructor for that union for me. If one of them has a non-trivial constructor/destructor, I have to write the copy constructor for the union myself.
Another thing you can do is fast destruction of an array. Usually, when you delete an array, you have to make sure to call the destructor on every item. If you can prove that the destructor of each element is trivial, then you are allowed to skip destroying the elements, and just free the memory. std::vector does this under the hood (so you don't have to)

About constructors and assign operators in 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.

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.

"MyClass mc = MyClass()" or "MyClass mc"?

What is the difference between
MyClass mc = MyClass();
and
MyClass mc;
in C++?
The first invokes copy constructor, with temporary object as parameter - MyClass() creates temporary.
The second invokes default constructor.
In reality, they are, in most cases, optimized out to the same code, but that's the semantical difference.
As Negal mentioned, the case is a bit different with POD types; when "MyClass" is POD, the second snippet will not value-initialize mc, whereas first will.
The first one is copy initialization and the 2nd one is default initialization.
For example, the following code will not compile:
class MyC
{
public:
MyC(){}
private:
MyC(const MyC&) {}
};
int main()
{
MyC myc = MyC();
return 0;
}
Custom copy constructor and default constructor.
First create temp-object via c-tor without arguments and then calls copy-ctor for object (no take in account any optimisations). Second calls c-tor without arguments, without copy. With compilers optimization both cases are equal.
Differences are in fundamental-types, so
// initialized to something
int c;
// initialized to int() that is 0 by standard.
int c = int();
no difference. default ctor call. syntax sugar )
no copy ctor!!!!
class PPP{
public:
PPP(PPP&){
std::cout<<"PPP1"<<std::endl;
}
PPP(const PPP&){
std::cout<<"PPP2"<<std::endl;
}
PPP(){
std::cout<<"PPP3"<<std::endl;
}
};
PPP ppp = PPP();
and you find only PPP in the console.

Why is the compiler bugging me on this one?

(using Visual C++ 2010, compiling in debug with optimizations turned off)
I have the following very simple class:
class exampleClass
{
public:
exampleClass()
{
cout << "in the default ctor" << endl;
}
private:
exampleClass (const exampleClass& e)
{
cout << "in the copy ctor" << endl;
}
};
When I try to compile it with the following main:
#include <iostream>
using namespace std;
int main()
{
exampleClass e1=exampleClass();
return 0;
}
I get the compilation error:
'exampleClass::exampleClass' : cannot access private
member declared in class 'exampleClass'
When I remove the access modifier "private" from the copy ctor, the program compiles and prints only:
in the default ctor
Why is this happening? If the compiler will not invoke the copy ctor anyway, why is it bugging me?
Since some people missed the first line (at least before some edits) i will repeat it:
I compiled in debug with optimizations turned off.
This type of initialization is called copy-initialization. I believe the following clause from the C++11 standard applies here (paragraph 8.5.16, page 204):
If the initialization is direct-initialization, or if it is
copy-initialization where the cv-unqualified version of the source
type is the same class as, or a derived class of, the class of the
destination, constructors are considered. The applicable constructors
are enumerated (13.3.1.3), and the best one is chosen through overload
resolution (13.3). The constructor so selected is called to initialize
the object, with the initializer expression or expression-list as its
argument(s). If no constructor applies, or the overload resolution is
ambiguous, the initialization is ill-formed.
In this case, the best applicable constructor is the copy ctor, which is private, hence the error message.
To further answer your question, when the copy ctor is private, your program is simply not allowed to pass the complier check because of the rules imposed by the standard. When you make the copy ctor public, the program becomes valid, but the call to the copy ctor is optimized away.
EDIT:
Okay, to elaborate on previous paragraph.You're dealing here with the so-called copy elision. While the copy elision is possible in this case, the standard requires you to provide an accessible copy ctor for your class.
exampleClass e1=exampleClass();
This will first create a temporary exampleClass using the default constructor and then copy that into e1 using the copy constructor. This will invoke the private copy constructor and thus give you the error. The corrent way to instantiate an instance of a class with the default constructor is this:
exampleClass e1;
The compiler is required to bug you there. While the copy can be elided, the standard requires that the copy constructor is accessible for that type of construction. Of course, you can simplify the code and avoid the copy construction altogether:
exampleClass e1; // Will call exampleClass::exampleClass()
exampleClass e1=exampleClass();
is the same as:
exampleClass e1(exampleClass());
i.e it invokes the (private) copy constructor.
This is because at compile time, the compiler checks if the function the user is trying to access is really accessible. So when you use exampleClass e1=exampleClass();, it first checks if the copy-constructor is accessible. It spits out an error because the copy-constructor is not private. Remember that at this point the compiler hasn't gone onto the optimization stage where it does the clever stuff as to skip the copy-constructor.
When you make the copy-constructor public, the compiler successfully goes through the stage of parsing the code and making sure that everything is accessible and is in order (there's actually more than that going on) and then at the optimization stage, which usually is on in 'Release' mode it does the clever stuff and by-passes the use of copy-constructor. However if you tried the same code in 'Debug' mode you'd see that the copy-constructor does get called.
Everyone explains how you should instantiate an object and #Grigory Javadyan makes a good point on copy elision. It looks like, MSVC does this optimization (so called return value optimization) even in debug mode.
exampleClass e1=exampleClass();
is the same as
exampleClass giveExample()
{
return exampleClass();
}
exampleClass e1 = giveExample();
You will see that copy ctor will not be called.
But here :
exampleClass giveExample()
{
exampleClass example;
return example;
}
exampleClass e1 = giveExample();
you will see another output line :
in the copy ctor
Because you are forcing the compiler to first generate an object and then return it.
Here, here and here some questions I can find, similar to yours.
PS. Link#2 is from another Q&A site. I hope this is not a problem.
That's not how you do instantiate an object in C++. If you want it allocated on the stack, you write:
exampleClass e1;
and you're done, sincee exampleClass' constructor accepts no parameters.
Otherwise, if you want it allocated on the heap, you write:
exampleClass e1 = new exampleClass();
The way you wrote it actually creates a temporary object and invokates the copy constructor on that temporary object to create e1. Problem is that your copy-ctor is private, so the compiler's error message.
when you write
exampleClass e1 = exampleClass()
it is the same as writing
exampleClass e1( exampleClass() );
which invokes the copy ctor.
Its because the copy constructor is private..
your code is
creating a temporary exampleClass and invoking the default constructor exampleClass()
attempting to assign the resulting temporary object to e1 using the private copy constructor