C++ copy constructor using pointers
Asked Answered
B

3

10

Can anyone explain the meaning of *p=*q in this C++ code? Is this a copy constructor concept?

class A{
  //any code
}

int main(){
  A *p=new A();
  A *q=new A();
  *p=*q;
  return 0;
}
Belga answered 4/9, 2016 at 13:6 Comment(3)
You're diving in far too deep. Be sure to understand the basics first. The situation is just like in int a; int *p = &a, *q = &a; *p = *q;Tarsal
@KerrekSB But in the OP's code new A() & new A() produce distinct instances, whereas your analogy uses a sole int a instance. I think a better parallel would be: int a1 = 5, a2 = 10; int *p = &a1; int *q = &a2; *p = *q;Cinch
For any pointer tp that is of type T*, the expression *tp will dereference that pointer, evaluating to a value of type T, that is pointed to by tp. So, you can read that as the value pointed to by "p" = the value pointed to by "q";Colocynth
Y
13

Is this a copy constructor concept?

No, what you are referring to is a copy assignment concept. Consider this:

int* p = new int{9};
int* q = new int{10};
*p = *q;

As you can see above, only the value of the variable q which is pointed to is copied. This is the equivalent of the copy assignment for objects. If you were to do this:

p = q;

Then this would not be a copy assignment, because both int's point to the same address and value, meaning any change to p or q would be reflected on the other variable. To give a more concrete and validated example, here is some code:

int main() 
{
    int* p = new int{9};
    int* q = new int{10};
    *p = *q;
    //p = 10, q = 10
    *p = 11;
    //p = 11, q = 10
    delete p;
    delete q;
}

And here is a supplementary counter-example

int main() 
{
    int* p = new int{9};
    int* q = new int{10};
    p = q;
    //p = 10, q = 10
    *p = 11;
    //p = 11, q = 11
    delete p;
    //delete q; Not needed because p and q point to same int
}

As you can see, the changes are reflected on both variables for p=q

Side Note You mentioned copy-construction, but you were unclear about the concept. Here is what copy-construction would have looked like:

int* p = new int{9};
int* q = new int{*p}; //q=9

Copy construction differs from copy assignment in the sense that with copy construction, the variable doesn't already have a value, and for an object, the constructor has yet to be called. Mixing up the 2 terms is common, but the fundamental differences make the two concepts, well, different.

Yankeeism answered 4/9, 2016 at 13:15 Comment(1)
You forgot semicolons before the end of both mainsFreeman
D
10

It looks like, you're unclear about the copy constructor and copy assignment. Let's first have a look at both concepts individually, and then I'll come to your question. The answer is a little long, so be patient :)

Copy Constructor

Here, I'm not going to explain how to write a copy constructor, but when the copy constructor is called and when it's not. (If you want to know, how to write a copy constructor, see this)

A copy constructor is a special constructor for creating a new object as a copy of an existing object. (It is called whenever there's a need for making a copy of an existing object)

These are the scenarios when the copy constructor will be called to make the copy of an existing object:


  • Initializing an object with some previously created object:

    SomeClass obj;
    // ...
    SomeClass anotherObj = obj; // here copy constructor will be called.
    

    See, SomeClass obj; statement is simply creating an object (here, default constructor will be called to create the object). The second statement SomeClass anotherObj = obj; is instantiating an object, initialized with the values of obj (an existing object), so copy constructor will be called here. You can also initialize an object with an existing object this way: SomeClass anotherObj(obj); (This statement is equivalent to SomeClass anotherObj = obj;)

    Except:
    If you initialize with some rvalue expression. e.g.

    SomeClass someObject = aObject + anotherObject;
    

    In this case, move constructor will be called. See, What are move semantics?


  • Passing an object by value to some function (see Passing arguments by value):

    See, the following code snippet, here the function doSomething is accepting an object as parameter by value:

    void doSomething(SomeClass someObject)
    {
        // ...
    }
    

    There are some cases, when there will be a need to make the copy of the passed argument in the parameter object someObject, I've listed when there will be a need to make the copy and when there'll not be a need.

    Have a look at the following code snippet:

    SomeClass someObject;
    // ...
    doSomething(someObject); // here copy constructor will be called.
    

    The statement SomeClass someObject; is just instantiating someObject by calling the default constructor.

    The second statement doSomething(someObject); is calling the function doSomething previously shown, by passing someObject as argument. This is the case, when there's a need to make a copy of someObject to pass to the function.

    Except:
    Similiary, If we call doSomething with some rvalue expression, it will call move constructor instead of copy constructor.


  • Returning an object from a function by value:

    Let's have a look at the following definition of doSomething

    SomeClass doSomehing()
    {
        SomeClass someObject;
        // ...
        return someObject;
    }
    

    In the above function doSomething, an object of SomeClass is being created and after doing some task, the object is being returned by the function, in this case, a copy of the someObject will be created and returned.

    Except:
    Similiary, If doSomething returns some rvalue expression, it will call move constructor instead of copy constructor.


Copy Assignment

Copy Assignment is usually confused with the copy construction, let's have a look at how it is different than the copy construction:

SomeClass someObject;
// ...
SomeClass anotherObject;
// ...
anotherObject = someObject; // here copy assignment operator will be called.

The first two statements are just creating someObject and anotherObject, you see, the third statement is actually calling the copy assignment operator and not the copy constructor.

Constructors are only called when some new object is being created. And in the case of anotherObject = someObject;, both the objects are already created, so there won't be any call to the copy constructor. Instead a copy assignment operator will be called (to see, how to overload a copy assignment operator, see this)


Now, let's have a look at your code snippet:

A *p=new A();
A *q=new A();
*p=*q;

In the first statement A *p=new A();, default constructor will be called to create an object (in this case, new object will be created on heap) and p will be initialized with the address of the newly created object (as p is a pointer)

Similar is the case with second statement A *q=new A(); (It is creating another object and q will be initialized with newly created object)

Now, the third statement: *p = *q; (here * is Dereference operator)

To understand, what the third statement is doing, let's have a look at some pointers and de-referencing them to get the actual object, which they're pointing to.

int someVariable = 5;
int *somePointer = &someVariable;
// ...
*somePointer = 7;

Let's try to understand the above code snippet: someVariable is created and initialized with a value of 5, then somePointer is created and initialized with the address of someVariable.

Now, the last statement *somePointer = 7;, it is actually de-referencing the somePointer and by de-referencing, it'll get the the variable which it is pointing to. (so it will get someVariable) and then it is assigning 7 to it. So after this statement, someVariable's value will become 7

Let's have another example:

int* somePointer = new int;
int* anotherPointer = new int;
// ...
*somePointer = 5;
*anotherPointer = 7;
// ...
*somePointer = *anotherPointer;

First, somePointer will be created and initialized with the address of dynamically allocated int variable (will be allocated in heap, see Dynamic Allocation in c++), similarly, anotherPointer will be initialized with the address of another dynamically allocated variable.

Then, it is assigning 5 to the first variable (which is being pointed by somePointer) and 7 to the second variable (which is being pointed by anotherPointer)

Now, the last statement will be of your interest, *somePointer = *anotherPointer;, in this statement *somePointer is de-referencing and getting the first variable (whose value was 5) and *anotherPointer is de-referencing and getting the second variable (whose value is 7), and it is assigning the second variable to the first variable, resulting in changing the first variable's value to 7.

Now, let's have a look at your *p=*q; statement, (p was pointing to the first object of A, and q was pointing to the second object of A, both dynamically allocated), *p will dereference p and get the first object, *q will dereference q and get the second object, and then the second object will be copied in the first object, and if you see, no new object is being created in *p=*q;, and only the value of second object is being created in the first object, so copy assignment operator will be called here and no the copy constructor.

Another thing

You should de-allocate the dynamically allocated memory which you borrowed using new operator:

delete p;
delete q;

You should add these two lines at the end of your program, so to avoid Memory Leak.

Discommon answered 4/9, 2016 at 13:50 Comment(0)
R
5

As stated in the comments, one need to understand the basics first:

  • With A *p=new A(); one gets a pointer to a memory region on the heap in which an object of type A is constructed.

  • With *p one dereferences the pointer, i.e. one retrieves the object.

  • Now *p = *q uses the (possibly implicitly declared) assignment operator of class A in order to give *p -- the object pointed to by p -- the value of *q. This operation could equivalently be written as p->operator=(*q).

The last step, the assignment, is identical to what you would obtain with objects instead of pointers (which is usually a better way to go in C++ and often called RAII):

A r;
A s;
r=s;
Reverent answered 4/9, 2016 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.