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.
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 ;)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With