user1861088
user1861088

Reputation: 1733

C++ reference to a copy-constructed object

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

Answers (2)

Joseph Mansfield
Joseph Mansfield

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:

  1. 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.

  2. 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

DotNetUser
DotNetUser

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

Related Questions