Assignment of objects in C++

42,195

Solution 1

This is a little complicated.

Consider this simple class:

class Thing1
{
public:
  int n;
}

Now we try the first experiment:

Thing1 A;
A.n = 5;

Thing1 B = A;
B.n = 7;

cout << A.n << " " << B.n << endl;

The result is "5 7". A and B are two separate, independent objects. Changing one doesn't change the other.

Second experiment:

Thing1 *p = &A;
p->n = 9;

cout << A.n << " " << p->n << endl;

The result is "9 9"; p is a pointer to A, so A.n and p->n are the same thing.

Now things get complicated:

class Thing2
{
public:
  int *p;
};

...
Thing2 A;
A.p = new int(2);

Thing2 B = A;
*(B.p) = 4;

cout << *(A.p) << " " << *(B.p) << endl;

Now the result is "4 4". The assignment B = A copied the pointer, so although A and B are two different objects, their pointers point to the same int. This is a shallow copy. In general, if you want to make a deep copy (that is, each Thing points to an int of its own) you must either do it by hand or give the class an assignment operator which will handle it. Since your Matrix class doesn't have an explicit assignment operator, the compiler gives it the default-- which is a shallow copy. That's why, in your first snippet, both matrices appear to be changed.

EDIT: Thanks to @AlisherKassymov, for pointing out that declarations of the form Thing A=B; use the copy constructor, not the assignment operator. So for the solution to work in the above code, the copy constructor must make a deep copy. (Note that if the copy constructor does it, you almost certainly want the assignment operator to do it too (see the Rule of Three)). Also note that if these functions get complicated, it makes sense to simply have the copy constructor invoke the assignment operator.)

Solution 2

These two are not equal.

The first snippet

Matrix test, with is full content is COPIED into Matrix ptr. When you work with ptr later, you only change the copy, not the original Matrix test.

The second snippet

The address of Matrix test is put into the pointer Matrix *ptr. The pointer now holds the address of test. When you write (*ptr), you dereference the pointer and work with the value of the original test.

In Java

In java, all objects are kindof pointers (primitives like int are not). When you assign one object to another there, the default is to only overwrite the pointer value. Works like your second example.

Solution 3

The first copies that value of test into ptr. The second sets ptr to be a pointer to the address of test

These two actions are not the same. In the first case, ptr will have the same value as test, but they are in them selves two distinct copies of that data, so that your assignment of ptr(0,0) = 95; will not set test(0, 0).

In the second instance however, ptr points to the address of test, so that the dereference of ptr is test. Thus, when you set the value here, you are actually setting the value of test(0, 0) as well.

This is trivially verifyable with a program such as this:

#include <iostream>
class Test{
public:
    int i;
};

int main(){
    Test c;
    c.i = 1;

    Test d = c;
    d.i = 2;
    std::cout << "Value: " << c.i << "\n";

    Test* e = &c;
    (*e).i = 2;
    std::cout << "Pointer: " << c.i << "\n";
}

Of course, if you dynamically allocate your Matrix (new) then when you copy the value as per your first example, the pointer to the data is also copied, so when you set the new data, it appears to be equal to the second example.

Solution 4

The equivalent of Java behavior in this case is expressed using C++ references

Matrix test(4,4);
Matrix &ptr = test;
ptr(0,0) = 95;

This code indeed does the same thing as the pointer version, i.e. it modifies the original test object.

Your first code sample formally creates a copy of the original object and then modifies that copy. However, your Matrix class appears to be a multi-level object that owns some lower-level memory (matrix pointer). If your Matrix class copy constructor implements shallow-copying logic (i.e. it shares the lower-level matrix data with the original object instead of deep-copying it), then modifying the copy will appear to modify the original object as well. Whether this behavior is correct or incorrect depends on your intent.

In your comments you mentioned that the first code also appears to modify the original object. This immediately means that your class in fact implements shallow-copying logic. And it looks like it is not an intended part of your design. Apparently you forgot to follow the Rule of Three when you were implementing your Matrix class.

Share:
42,195
user1493813
Author by

user1493813

Updated on July 24, 2022

Comments

  • user1493813
    user1493813 almost 2 years

    To contextualize my question, I'm using a Matrix class with the following definitions:

    Matrix(unsigned int, unsigned int); // matrix of the given dimension full of zeroes
    Matrix(Matrix*); // creates a new matrix from another one
    int &operator()(int, int);  // used to access the matrix
    int **matrix; // the matrix
    

    Now take these two code snippets:

    First:

    Matrix test(4,4);
    Matrix ptr = test;
    ptr(0,0) = 95;
    

    Second:

    Matrix test(4,4);
    Matrix *ptr = &test;
    (*ptr)(0,0) = 95;
    

    Both codes have the same effect, the element in the (0,0) position receives 95 (The first snippet is very similar to Java, the reason which led me to ask this question). The question is, are both ways correctly making the assignment of the object?