operator overloading = modifies original object - c++

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; 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

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; calls the compiler-generated copy constructor, which does not deep copy the pointer member.
If you were to use a std::vector for items then you could leave the constructors and assignment operator to the compiler. (You don't have any delete[] calls in your code which means that your class will leak memory like a colander leaks water.)

Related

Error Within Overloaded Assignment Operator Function and Copy Constructor

I am currently writing a program that creates a dynamically allocated circular array. For it, I have created a copy constructor and an assignment operator.
I am getting an error called "munmap_chunk(): invalid pointer", when I try to call my assignment operator for a second time. If I call it once, the error does not show. Am I writing my copy constructor and assignment operator properly? If there is any information needed I am happy to provide, thank you.
CircularDynamicArray(const CircularDynamicArray& source){
cout << "copy constructor called" << endl;
m_capacity = source.m_capacity;
m_size = source.m_size;
m_front = source.m_front;
m_rear = source.m_rear;
arr = new elmtype[source.m_capacity];
for(int i = 0; i < source.m_capacity; i++) {
arr[i] = source.arr[i];
}
}
//overloaded assignment operator
CircularDynamicArray &operator = (const CircularDynamicArray& source) {
cout << "Overloaded Assignment called" << endl;
//check for self assignment
if (this == &source) {
return *this;
}
m_capacity = source.m_capacity;
m_size = source.m_size;
m_front = source.m_front;
m_rear = source.m_rear;
delete[]arr;
for(int i = 0; i < source.m_capacity; i++) {
arr[i] = source.arr[i];
}
return *this;
}
Am I writing my copy constructor and assignment operator properly?
I will say that you are making more work for yourself than necessary when it comes to writing an assignment operator.
If you have written a copy constructor (which you have), and a destructor (which you didn't show, but let's assume you did), and both of these functions have no bugs, then the assignment operator can be implemented trivially using copy / swap.
In general, you should strive to write the copy constructor and destructor before writing the assignment operator, so that this "trick" of writing the assignment operator can be utilized. Here is an example:
#include <algorithm>
//...
CircularDynamicArray &operator=(const CircularDynamicArray& source)
{
if (this != &source)
{
CircularDynamicArray temp(source); // Create a copy of what we want
// get the temp's innards, and give temp our stuff
std::swap(temp.m_capacity, m_capacity);
std::swap(temp.m_size, m_size);
std::swap(temp.m_front, m_front);
std::swap(temp.m_rear, m_rear);
std::swap(temp.arr, arr);
} // goodbye temp
return *this;
}
No allocating memory, no delete[] calls, you don't even need a check for self-assignment (but done anyway, for efficiency purposes). The above is exception-safe also. Everything you need for an assignment operator to work, basically flawlessly.
Note that you need to swap all of the member variables -- don't forget any, as that will cause this to not work correctly.
All that is being done is that you're making a copy of the passed-in object, and swapping out the guts of the current object this with the copy. Then the copy dies off with your old information. This is why you need a working copy constructor (for the initial copying of the source to work), and a working destructor (so that the destruction of the temp works).

Heap Corruption error with Copy constructor and overloaded assignment operator

I am a student so I apologize up front for not using the correct forum protocols. I have searched for hours on this problem, none of my classmates can help. My assignment is to create a copy constructor, overloaded assignment operator(=) and a destructor (the 'big three') in C++ to manage an array on the heap. What I wrote below in VS13 produces the correct output but I get a debug error: HEAP CORRUPTION DETECTED:c++ crt detected that the application wrote to memory after end of heap buffer
Can anyone give me some guidance on this, I don't even know where to look. Thanks!!
//copy constructor
myList::myList(const myList& source){
cout << "Invoking copy constructor." << endl;
array_capacity = source.array_capacity; //shallow copy
elements = source.elements; //shallow copy
delete[] arrayPointer;
arrayPointer = new double(source.array_capacity); //deep copy
for (int i = 0; i < array_capacity; i++) //copy array contents
{
arrayPointer[i] = source.arrayPointer[i];
}
}
//overloaded assignment operator
myList& myList::operator=(const myList& source){
cout << "Invoking overloaded assignment." << endl;
if (this != &source){
array_capacity = source.array_capacity; //shallow copy
elements = source.elements; //shallow copy
delete[] arrayPointer; //delete original array from heap
arrayPointer = new double(array_capacity); //deep copy
for (int i = 0; i < source.array_capacity; i++) {//copy array contents
arrayPointer[i] = source.arrayPointer[i];
}
}
return *this;
}
//destructor
myList::~myList(){
cout << "Destructor invoked."<< endl;
delete[] arrayPointer; // When done, free memory pointed to by myPointer.
arrayPointer = NULL; // Clear myPointer to prevent using invalid memory reference.
}
There are a couple of problems with your code. First you are invoking delete on arrayPointer but it hasn't been initialized to anything. This could in fact end up deleting memory you have already allocated or result in an excecption or asset in the implementation of delete. Second when you do initialize it you are allocating a single double initialized to the value of source.array_capacity. Notice the parenthesis used in the line below.
arrayPointer = new double(source.array_capacity);
This will certainly result in undefined behavior during the copy as you end up accessing elements outside the bounds of the array. The above line is present in both your copy constructor and copy-assignment operator and should be using square brackets instead like so:
arrayPointer = new double[source.array_capacity];
You also never check to see if there are any elements stored in the source instance of myList. In this case you should likely be assigning nullptr (or NULL in C++03) to arrayPointer.
As a side note you do not really need to assign NULL to arrayPointer in your destructor. Once the object is destroyed it's gone and any attempt to access it after the fact will result in undefined behavior anyway.
Captain Obvlious pointed out the problem in your copy-constructor already.
You will have noticed that the copy-constructor and the assignment-operator contain a lot of the same code but with a subtle difference. In fact this is probably how you ended up with the error (the assignment operator needs to delete[] the old value, but the copy-constructor doesn't.
Code duplication is bad because it leads to subtle errors like this creeping in. A good pattern you can use here is what's called the copy and swap idiom.
The gist is that you define the copy-constructor, and you also define swap. Then you get assignment for free. This works because swap is easier to implement correctly than the assignment operator, and another major benefit is that nothing can go wrong (you don't have to worry about out-of-memory errors and so on).
In your code; keeping your fixed copy-constructor, you could add inside the class definition:
friend void swap( myList &a, myList &b )
{
std::swap( a.array_capacity, b.array_capacity );
std::swap( a.arrayPointer, b.arrayPointer );
std::swap( a.elements, b.elements );
}
and now the assignment operator is very simple:
myList& myList::operator=(const myList &source)
{
myList temp(source);
swap(*this, temp);
return *this;
}
The old resources from the current object are deleted by the destructor of temp. This version doesn't even need to check for self-assignment because std::swap(x, x) is well-defined.
This can even be optimized further by taking source by value instead of by reference, as explained on the linked page.

C++ Object out of scope

When does the object Second go out of scope? If I leave the third cout statement I get a compiler error. Even when I surround the initialization of Second and the cout statements.
#include <iostream>
using namespace std;
class MyArray{
public:
int Size;
int* data;
MyArray(int s) : Size(s), data(new int[s]){}
~MyArray(){
delete[](this->data);
}
};
int main()
{
MyArray First(20);
First.data[0] = 25;
MyArray Second = First;
cout << First.data[0] << endl;
cout << Second.data[0] << endl;
cout << Second.data[0] << endl;
system("PAUSE");
return 0;
}
The runtime error:
Here is what's going on: MyArray objects get a pointer to their own array of integers called data upon initialization. However, this line changes things for the Second object:
MyArray Second = First;
Now both First.data and Second.data point to the same int[] array, because the default copy constructor and assignment operator perform a shallow copy.
This is bad, because the compiler has freedom to destroy First after its last use in your code. Hence, accessing Second.data[0] may be invalid already.
Moreover, once First and Second go out of scope at the end of main(), they both will try deleting data. Only the first one would succeed; the second one will trigger undefined behavior.
To avoid such errors in the future, use the rule of three. It says that if you define a destructor, a copy constructor, or an assignment operator, you probably need all three of them.
Your code has only the destructor. Adding a copy constructor and an assignment operator will fix this problem, and prevent memory leaks on assigning MyArray objects.
The scope of both names ends at the }. (Scope is a compile-time notion.)
The lifetime of Second ends when execution reaches the end of the block, after which the lifetime of First ends. But your program has undefined behaviour because you delete the array twice, so anything can happen.
You need to do the deep copy using copy constructor
MyArray(const MyArray &t)
{
t.size = size;
if(size not_eq 0)
{
t.data = new int(size);
for(int i=0 ; i<size ; i++)
t.data[i] = data[i];
}
}
after this MyArray Second = First; will work fine
if you want to assign then you have to write assignment operator as well

My Stats class prints garbage

Consider the following class:
class Stats
{
private:
int arraySize; // size of array
int * data; // pointer to data, an array of integers
// default size of array in default constructor
static const int DEFAULT_SIZE = 10;
public:
Stats() // default constructor
{
arraySize = DEFAULT_SIZE; // element count set to 10
data = new int[DEFAULT_SIZE]; // array of integers
srand((unsigned int) time(NULL)); // seeds rand() function
// initializes integer array data with random values
for (int i = 0; i < DEFAULT_SIZE; i++)
{
// data filled with value between 0 and 10
data[i] = rand() % (DEFAULT_SIZE + 1);
}
}
~Stats() // destructor that deletes data memory allocation
{
delete [] data;
}
void displaySampleSet(int numbersPerLine)
{
cout << "Array contents:" << endl; // user legibility
// iterates through array and prints values in array data
for (int i = 0; i < arraySize; i++)
{
cout << data[i];
/* nested if statements that either prints a comma between
values or skips to next line depending on value of numbersPerLine */
if (i + 1 < arraySize)
{
if ((i + 1) % numbersPerLine != 0)
cout << ", ";
else
cout << endl;
}
}
}
}
For some reason, when I create a Stats object in the following way:
Stats statObject = Stats();
and then call the displaySampleSet() on it, the numbers display fine. However, the function prints garbage when the Stats object is created in the following way:
Stats statObject;
statObject = Stats();
I have no idea why it does this and have a feeling it has to do with the integer pointer 'data' and/or with the way the object is being created but I'm not sure what... Any and all help is fully appreciated! Thank you so much in advance.
Update: Destructor added
Both the code statements produce undefined behavior. It is lucky that the first one works while second doesn't.
Stats statObject = Stats();
Is copy initialization. It copies the created Stats object by calling the default constructor and then copies it in to statObject by calling the copy constructor. Note your class does not provide an copy constructor so the implicitly generated one gets used. This creates a shallow copy of the dynamically allocated members. The object from which this copy gets created is eventually destroyed and then whats left in your class is dangling pointers which do not point to anything valid. The copy constructor needs to do a deep copy.
Same is the case with:
Stats statObject;
statObject = Stats();
Wherein the compiler generated copy assignment operator is called. Resulting in similar problems as in case 1.
You need to follow the Rule of Three and provide your copy constructor and copy assignment operators for the class.
You code is using synthesized copy constructor , assignment operator defined by compiler.
Also you are not freeing the memory allocated dynamically for default constructor.
you have to define your own copy construcor, overload assignment operator( follow rule of three).
Both the cases are undefined behaviour
Stats statObject;
statObject = Stats(); //using compiler defined assignment operator.
Stats statObject = Stats(); //using compiler defined copy constructor.
Here temp object generated is is using assignment operator overloaded by compiler and is doing shallow copy instead of deep copy.
overload assignment operator:
Stats& operator=(const Stats& rhs);
define copy constructor:
Stats(const Stats& rhs);

overloading operator =

I have to create a function which overloads the = operator so that when you put object1 = object2; it'll copy over all the values inside object2 into object1.
My class looks like:
class foo {
private:
int max;
int *val;
size_t *listofVal;
}
And my overload function is declared as:
foo& foo::operator=(const foo& matrix);
foo::foo(std::istream& s); //constructor
how would I do this?
The best way to do this is by using Copy and Swap Idiom.
Also, note that if you feel the need of overloading the copy assignment operator then you also probably need to overload copy constructor as well as the destructor for your class.
Do have a look at Rule of Three.
Simply copying all the values is the default behaviour of the compiler supplied copy contructor. This is known as a "shallow-copy".
More elaborate behaviour is achieved by implementing your own constructor so that the objects (here values) pointed to by your reference are created anew in the copy.
foo::foo(std::istream& s) {
max = s.max;
val = new int;
*val = s->val;
listofVal = new size_t;
*listofVal = s->listofVal;
}
would be one way of achieving that which is known as a "deep copy".
But as one of your members is called listofVal I rather feel you are doing something other than storing a single value at the memory address it points, in which case you should be holing a counter to the number of elements contained therein, which I will henceforth assume to be the field you call max. To copy the whole list, your copy constructor would then need to be:
foo::foo(std::istream& s) {
max = s.max;
val = new int;
*val = s->val;
listofVal = new size_t[max];
for (int i = 0; i < max; ++i)
listofVal[i] = s->listofVal[i];
}
Ravi, yes, the copy constructor is a proof of construct and despite breaking the "Rule Of Three" can be implemented before the other two. Here is the assignment operator.
foo& foo::operator=(const foo& matrix) {
if (this != matrix) {
max = matrix.max;
val = new int;
*val = matrix->val;
listofVal = new size_t[max];
for (int i = 0; i < max; ++i)
listofVal[i] = matrix->listofVal[i];
}
}
would be suitabe for object1 = object2; assignment. I tend towards the copy constructor approach.
The methods need to be members to access the private data so your class should be like
class foo {
///...///As before
foo &operator=(const foo& matrix);
};
Of course it needs a destructor but as it was not explicitly requested I didn't want to answer what wasn't asked.
Following on from the link to the Copy and Swap idiom, when the LHS may already contain data, for robust assignment you might consider:
foo& foo::operator=(const foo& matrix) {
if (this != matrix) {
val = new int;
*val = matrix->val;
size_t* newArray = new size_t[max];
int newMax = matrix.max;
std::copy(matrix.listofVal, matrix.listofVal + max, newArray);
if (listofVal) delete listofVal;
listofVal = newArray;
max = newMax;
}
}
I would add that assigning local objects on the heap can cause memory leaks (if the method breaks before they are assigned to an object responsible for their deletion), but that is just a whole nother layer of parannoia when we have enough to do preserving class integrity.
Googling for "C++ Assignment Operator" gives you this useful site ;-)