Reputation: 1733
Hey this is a really basic question and but I got confused about it. Say I created an object
MyObject a
.
It comes with a copy constructor, so I know I can do this:
MyObject b(a);
But can I do this?
MyObject& b(a);
And if I do this:
MyObject b = a;
what is in b
? Apology if this question is too fundamental to be bothered posting.
Upvotes: 2
Views: 131
Reputation: 110778
Doing MyObject& b(a)
has nothing to do with the copy constructor. It just creates b
which is a reference to the object a
. Nothing is copied. Think of b
as an alias for the object a
. You can use b
and a
equivalently from then on to refer to the same object.
MyObject b = a;
will use the copy constructor, just as MyObject b(a);
would.
There are two forms of initialisation: T x = a;
is known as copy-initialization; T x(a)
and T x{a}
are known as direct-initialization.
When T
is a reference type, it doesn't matter which type of initialisation is used. Both have the same effect.
When T
is a class type, we have two possibilities:
If the initialisation is direct-initialization (MyClass b(a);
), or, if it is copy-initialization with a
being derived from or the same type as T
(MyClass b = a;
): an applicable constructor of T
is chosen to construct the object.
As you can see, both of your examples fall in this category of class type initialisers.
If the initialisation is any other form of copy-initialization, any user-defined conversion sequence will be considered followed by a direct-initialization. A user-defined conversion sequence is basically any sequence of standard conversions with a single conversion constructor thrown in there.
If c
were of Foo
class type and there was a conversion constructor from Foo
to MyClass
, then MyClass b = c;
would be equivalent to MyClass b(MyClass(c));
.
So basically, if the source and destination types are the same, both forms of initialisation are equivalent. If a conversion is required, they are not. A simple example to show this is:
#include <iostream>
struct Bar { };
struct Foo
{
Foo(const Foo& f) { std::cout << "Copy" << std::endl; }
Foo(const Bar& b) { std::cout << "Convert" << std::endl; }
};
int main(int argc, const char* argv[])
{
Bar b;
Foo f1(b);
std::cout << "----" << std::endl;
Foo f2 = b;
return 0;
}
The output for this program (with copy elision disabled) is:
Convert
----
Convert
Copy
Of course, there are lots of other types of initialisations too (list initialisation, character arrays, aggregates, etc.).
Upvotes: 7
Reputation: 6612
Here is my view:
References are tied to someones else's storage, whenever u access a reference, you’re accessing that storage. References cannot be assigned to null because of the fact that they are just an aliases. A reference must be initialized when it is created. (Pointers can be initialized at any time.)
so when you say
MyObject& b(a);
compiler allocates a piece of storage b, and ties the reference to a.
when you say
MyObject b = a;
you pass a reference of a to the copy constructor and creates new b from it. Note that its a deep copy only if you have a copy constructor written for it. Otherwise it calls the default copy constructor which results in a shallow copy.
and when you say
a = b; // after creating these objects
it translates as Object::operator=(const Object&), thus A.operator=(B) is called (invoke simple copy, not copy constructor!)
Upvotes: 0