Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

operator overloading = modifies original object

struct List {
    int size;
    int* items;
    List& operator=(const List& l);
};

List& List::operator=(const List& l)
{
    delete[] items;
    size = l.size;
    items = new int[20];

    for (int i = 0; i < size; i++)
        items[i] = l.items[i];

    return *this;
}

ostream& operator<<(ostream& out, const List& l)
{
    out << "Size: " << l.size << "\t {";
    for (int i = 0; i < l.size; i++)
        out << l.items[i] << " ";

    return (out << "}");
}

int main()
{
    int size = 6;
    List l1 = { size, new int[size]{ 0, 1, 2, 3, 4, 5 } };
    List l2 = l1;

    l2.items[1] = 50;

    cout << l1 << endl;
    cout << l2 << endl;

    return 0;
}

I'm confused since when I assign l2 = l1 by using an overloaded operator, why does the contents of l1 change, when altering l2 later on? Especially since l1 is passed as a const. They somehow point to the same object in memory instead of being a copy.

like image 942
snzm Avatar asked Dec 04 '22 19:12

snzm


2 Answers

List l2 = l1;

Despite the =, because this is a declaration you're performing copy construction (formally "copy-initialisation"), which has nothing to do with the assignment operator.

You didn't define a copy constructor (which should look much like your copy assignment operator), so the pointers are indeed shared.

The results would have been what you expected, had you written:

List l2{};
l2 = l1;

By the way, I'd give size and items defaults (0 and nullptr respectively), if I were you. Otherwise, when you forget that {}, the members have unspecified values and all hell breaks loose. This could be done with a nice default constructor, or by forgetting this whole enterprise and using a std::vector instead ;)

like image 38
Lightness Races in Orbit Avatar answered Dec 08 '22 00:12

Lightness Races in Orbit


List l2 = l1; does not call the copy assignment operator (operator=(const List& l)). Since the "assignment" happens during a variable declaration you are instead initializing l2 via copy initialization which is calling the compiler generated default copy constructor. Since it does a shallow copy both objects will point to the same data.

If you are going to be writing your own classes that manage memory/resources you need to at least provide your own copy constructor, copy assignment operator and destrcutor. This is known as the rule of three. If you want to include move semantics then you need to provide a move constructor and move assignment operator, which is known as the rule of 5. There is also the rule of zero, where use use types that already "do the right thing" (like using a std::vector) allowing the compiler generated defaults to work for you and you can read about all it at The rule of three/five/zero

like image 51
NathanOliver Avatar answered Dec 07 '22 23:12

NathanOliver