C++ operator destroys input variables - c++

This is probably just me being stupid somehow or the other, but I am relatively new to C++, so forgive me the idiocy. I'm trying to teach myself how to use operators. I've defined a very basic operator as follows:
Matrix::Matrix operator+(Matrix a1, Matrix a2) {
if(a1.rows != a2.rows || a1.cols != a2.cols) {
std::cout << "ERROR: Attempting to add matrices of non-equal size." << std::endl;
exit(6);
}
int i, j;
Matrix c(a1.rows, a1.cols);
for(i = 0; i < a1.rows; i++)
for(j = 0; j < a1.cols; j++)
c.val[i][j] = a1.val[i][j] + a2.val[i][j];
return c;
}
The class Matrix represents a matrix, and has a constructor that takes two ints as input (the number of rows and columns in the matrix, respectively), and creates a 2D array of doubles of the appropriate size (named val). This function works as supposed to in that the value for c is correct, but it also appears to destruct a1 and a2. That is, if I write
Matrix c = a + b;
It gives the right result, but a and b are no longer usable, and I get a glibc error at the end of the code claiming I am trying to destruct a and b when they have already been destructed. Why is this?

Your signature is wrong:
Matrix operator+(Matrix a1, Matrix a2)
it should be
Matrix operator+(const Matrix& a1, const Matrix& a2)
The reason it appears to destroy a1 and a2 is because, well, it is, since those are temporary copies created in the method scope.
If the original values are destroyed, you're probably violating the rule of three. When a1 and a2 are destroyed, the destructor gets called, and you're probably deleting pointers in the destructor. But since the default copy constructor does only a shallow copy, the copied a2 and a1 will delete the original memory.
Edit: Since there are split opinions about this, I'll extend my answer:
Assume:
struct A
{
int* x;
A() { x = new int; *x = 1; }
~A() { delete x; }
};
//option 1:
A operator + (A a1, A a2)
{
A a; return a; //whatever, we don't care about the return value
}
//option 2:
A operator + (const A& a1, const A& a2)
{
A a; return a; //again, we don't really care about the return value
}
In this first example, the copy constructor is not implemented.
A copy constructor is generated by the compiler. This copy constructor copies x into the new instance. So if you have:
A a;
A b = a;
assert( a.x == b.x );
Important note that the pointers are the same.
Calling option 1 will create copies inside operator +, because the values are passed by value:
A a;
A b;
a + b;
//will call:
A operator + (A a1, A a2)
// a1.x == a.x
// a2.x == n.x
When operator + exits, it will call delete x on objects a1 and a2, which will delete the memory that is also pointed to by a.x and b.x. That is why you get the memory corruption.
Calling option 2 however, since no new objects are created because you pass by reference, the memory will not be deleted upon function return.
However, this isn't the cleanest way to solve the issue. It solves this issue, but the underlying one is much more important, as Konrad Pointed out, and I have in my original answer (although haven't given it enough importance, I admit).
Now, the correct way of solving this is properly following the rule of three. That is, have an implementation for destructor, copy constructor and assignment operator:
struct A
{
int* x;
A() { x = new int; *x = 1; }
A(const A& other) //copy constructor
{
x = new int; // this.x now points to a different memory location than other.x
*x = other.(*x); //copy the value though
}
A& operator = (const A& other) //assignment operator
{
delete x; //clear the previous memory
x = new int;
*x = other.(*x); //same as before
}
~A() { delete x; }
};
With this new code, let's re-run the problematic option 1 from before:
A a;
A b;
a + b;
//will call:
A operator + (A a1, A a2)
// a1.x != a.x
// a2.x != n.x
Because the copies a1 and a2 now point to different memory locations, when they are destroyed, the original memory is intact and the original objects - a and b remain valid.
Phew! Hope this clears things up.

I’m assuming that you are allocating dynamic memory inside the Matrix class using new and didn’t implement a custom copy constructor, thus violating the rule of three.
The cause of the error would then be that the local copy of the Matrix instances reuses the dynamic memory of the argument instances, and their destructors free it at the end of the method.
The solution is very simple: Don’t use pointers. Instead, you could use a nested std::vector to store the data.
Furthermore, the head of the operator is mangled. Depending on where you declare the function, it must either look like this, if you define the operator inside the class:
ReturnType operator +(Arg1)
Or, if you define it outside the class, it needs to look like this:
ReturnType operator +(Arg1, Arg2)
Yours is a wild mix of both. I’m assuming that your operator definition should look as follows:
Matrix operator +(Matrix a, Matrix b) { … }
If your code compiles then this is either an error in the compiler or you are using a very weird class structure indeed. Furthermore, as Luchian pointed out, it’s more efficient to pass the arguments as const& rather than by value. However, you should first make your code run correctly.

As Luchian Grigore pointed out, your signature is wrong and should be:
Matrix operator+(const Matrix& a1, const Matrix& a2)
But even this signature should not itself spoil a and b. But because you are copying arguments, I have another suspicion.
Have you defined your own copy constructor? I think you may be deleting the old variables when you are copying their values to the operator's arguments.
Please share your copy constructor (and preferably also operator= and the two-argument constructor that you use in this operator)

Since the size of the matrix is given at runtime, I suppose that the Matrix class hold a pointer that is initialized with a dynamically allocated array that is deleted by the destructor.
In that case, the copy constructor and the assignment operator must also be definedin order to allocate the copies of what the a matrix holds.
Not doing that, the default copy and assignment will just reassign the pointers, letting you with two or more Matrix holding a same array. And when one is destroyed (thus deleting the array) the others remain dangling.

Related

Overloading the assignment operator or use the default one?

If I have
Class A {
int x;
};
Class B {
vector<A> y;
//...
};
And I want to overload the assignment operator = in B. Will using the default = operator be enough? Will it just use the = operator of vector on y member?
EDIT: say I wanted to implement something like that myself, so that b = k will work if both b and k are of type B. would I need to explicitly call the vector destructor to free the y member of b, inside the implementation?
How will it look like?
B& B::operator=(const B& b) {
if (this == &b) {
return *this;
}
y = b.y;
return *this;
}
Will the original vector this->y destructed here? why?
Will using the default = operator be enough?
Yes, it's sufficient, unless you have any resources that need special treatment or parameters.
Will it just use the = operator of vector on y member?
Yes, the generated default assignment operator/copy constructor will call any assignment operators/copy constructors available for member variables automatically.
B& B::operator=(const B& b) {
if (this == &b) {
return *this;
}
y = b.y;
return *this;
}
Will the original vector this->y destructed here? why?
Yes, it will be "destructed", since the operator=() definition of vector<A> implies to do so.
It's not really the destructor function is called, but the implementation of the assignment operator does imply the same behaviour as there would be a new instance constructed, and all of the contained member destructors will be called when the vector is cleared.
If you don't have special cases (like owning pointers, or unique_ptr), you can just not-define it and use the default one. In your case it should be fine.
In case of owning pointers, you would suddenly have two objects pointing to the same data in memory, which might not be the behaviour you want (same goes for shared_ptr.
unique_ptr can't be copied, so the default assignment operator won't work in this case and you'd have to write an overload.

Should the assignment operator allocate new memory for data members or reuse existing memory?

I have a question regarding the assignment operator (apologies if this has already been answered in a different post).
As I understand the assignment operator, it is suppose to assign the value of one object to another, e.g.
class A {
public:
A();
A& operator=(const A& rhs)
{
b = rhs.b;
return *this;
}
private:
B b;
};
Now, if the class contains pointers, it seems to be common practice to allocate new memory in the assignment operator. This makes the assignment operator more complicated since one needs to be more careful, e.g.
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
if (this != &rhs) {
B* b1 = 0;
try {
b1 = new B(*rhs.b);
}
catch {
delete b1;
throw;
}
delete b;
b = b1;
}
return *this;
}
private:
B* b;
};
So my question is: Why not assign the value of the existing object pointed to, i.e. reuse the memory by calling B's assignment operator as one would do if b was not a pointer (see my first example)? Then the assignment would look like this
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
*b = *rhs.b;
return *this;
}
private:
B* b;
};
This is much more simple and does exactly what I would expect from the assignment operator.
Moreover, I can think of at least one example where the more complicated assignment operator will get me into trouble: If class A has a member function which returns b
const B* A::GetB() const
{
return b;
}
then the following code will leave a dangling pointer
A a1, a2;
const B* my_b = a1.GetB();
a1 = a2; // this leaves my_b dangling!
Your comments are much appreciated. Thanks!
You should always reuse existing memory whenever possible. In you case, you should reuse the existing memory only if you are dead certain that b points to valid memory.
It depends on the exact scenario at hand really. Usually, it is a good practice to reuse the memory, but really, when you are trying to assign a differently sized internal array, etc, you might be better off with reallocating or delete/new.
Note that, the own-cope check at the beginning would still be desired to avoid A = A alike situation, however.
I can't think of any reason that you shouldn't try to do that. The examples that I have found where the memory is deleted is due to the fact that the type itself wasn't copyable. If you have a class attribute and it is a pointer to a copyable type then your suggestion should work conceptually (meaning that you only posted a partial snip so I am not saying that what is in your example is perfect). Some older examples show how it is done with char* and int* or other types of dynamic arrays. However, modern C++ programmers should be trying to use std::string and container classes. Now we also have smart pointers so that you can have shared pointers without having to worry about dangling references or worrying about which object is really the owner of it. Below is an example of what I mean. The example does use a template but copies as though the T is a c-array (not a container class) so deep copying has to be done as in your first example.
http://www.cplusplus.com/articles/y8hv0pDG/

Objects modified when they are passed to a function as arguments in C++

I have the following simple vector class, where I can define vectors of different sizes and sum them; I have overloaded the + operator and include a simple data display method desplegar(); the class is defined as:
class Cvector{
private:
int dim;
float * C;
public:
Cvector(){};
Cvector(int n){dim=n; C = new float[dim];};
~Cvector(){delete [] C;};
int getdim(){return dim;};
float getC(int i){
if(i<dim && i>=0)
return C[i];
else
return 0;
};
void setC(int i, float x){
if(i<dim && i>=0)
C[i]=x;
}
void desplegar(){
cout<<"[ ";
for(int i=0;i<dim;i++)
cout<<getC(i)<<" ";
cout<<" ]"<<endl;
}
Cvector operator + (Cvector);
};
Cvector Cvector::operator + (Cvector A){
int n;
if(A.getdim()>dim)
n=A.getdim();
else
n=dim;
Cvector temp(n);
for(int i=0;i<n;i++)
temp.setC(i,getC(i)+A.getC(i));
return temp;
}
When I run the following code, everything works fine:
Cvector X(2);
Cvector Y(3);
defines the objects of dimension 2 and 3
X.setC(0,1);
X.setC(1,4);
Y.setC(0,0);
Y.setC(1,6);
Y.setC(2,9);
adds the corresponding values to each dimension (2 for X and 3 for Y)
X.desplegar();
Y.desplegar();
Cvector T=Y+X;
T.desplegar();
Everything is displayed correctly up til here, T displays the sum of vector Y and vector X, which is what I want. But when I run
X.desplegar();
it only displays unrelated data from memory, it seems that object X is modified when it is passed as an argument to operator +, why is this so? If I instead use pointers to the objects this does not happen, but it does not seem intuitive for this to happen.
can anyone comment?
When you overload the operator + with argument pass-by-value, the copy constructor is invoked to instantiate a temporary object. By default, the copy constructor is a bitwise copy of the original object. And just before the method returns, the destructor, which is user-defined, is invoked on the temp object, where the underlying storage is deallocated.
To correct the code, please define the copy constructor yourself or pass the argument by reference.
Please always remember the rule-of-three, that is when you've defined any of destructor, copy constructor, and assignment operator, please consider also define the others among these three.
The problem is in your operator+.
Cvector Cvector::operator + (Cvector A)
Here, you are passing the second operand (which, in your case is X) by making a copy of it. This copy (A) will have it's C pointer pointing to the same memory as your X vector:
Cvector T=Y+X;
In the end of the operator+, the copy will get destroyed, the destructor will be called, and the memory will be freed. Since this memory is shared by the copy of X, and X itself, you will have garbage. Change your operator+ so it takes a reference and everything will work fine:
Cvector Cvector::operator + ( const Cvector & A )
Note that it is also a good idea to implement a copy constructor.
You copy vectors incorrectly. temp has its destructor automatically invoked at the end of operator+, causing T to have a dangling pointer. You might be doing something similar to X, it's hard to tell without looking at the complete program.
Cvector doesn't have a copy constructor, so the default one will be generated:
Cvector::Cvector(Cvector const & original)
: dim(original.dim), C(original.C)
{}
this does a shallow copy, that is, you now have two objects with the same C pointer.
So, when you copy X as the argument to any function, this happens:
Cvector x;
// populate X
// now call this:
void foo(Cvector y) { // y is created as a copy of x
} // y is destroyed
when the copied argument is destroyed, it deletes C, which is shared with the original.
These changes should be made:
write proper a copy constructor and assignment operator, that perform a deep copy (this will stop copies from breaking the original object when destroyed)
pass arguments by const reference when you're not changing them

C++ Destructor being called in overloaded arithmetic operators

I have a custom-made Matrix library for a neural network program and overloaded arithmetic operators.
Here's the class declarations:
class Matrix{
public:
int m;
int n;
double **mat;
Matrix(int,int);
Matrix(int);
Matrix(const Matrix& that):mat(that.mat),m(that.m),n(that.n)
{
mat = new double*[m];
for(int i = 0;i<m;i++)mat[i] = new double[n];
};
~Matrix();
friend istream& operator>>(istream &in, Matrix &c);
friend ostream& operator<<(ostream &out, Matrix &c);
Matrix operator+(const Matrix& other);
};
This is the function definition for + operation:
Matrix Matrix::operator+(const Matrix& other)
{
Matrix c(m,n);
for(int i=0;i<m;i++)
{
for(int j = 0; j<n;j++)
c.mat[i][j] = mat[i][j] + other.mat[i][j];
}
return c;
}
I have tried to implement it in all ways and error is same...here's an instance
Matrix x(m,n); //m and n are known
x = a+b; // a and b are also m by n matrices
I have debugged the code using breakpoints and here's the error...
The local matrix 'c' in operator function is destroyed before it is returned and hence what is assigned to x is a garbage pointer..
Please suggest me something...
You need to define a copy constructor for your class. The copy constructor will need to allocate memory for mat and make a copy of the data.
Without this, when you return c, a new object is constructed that has the same value of mat as c. When c subsequently goes out of scope, it deletes c.mat. As a result, the copy of c is left with a dangling pointer.
Having done this, you should also implement an assignment operator.
The value you returned is used to initialize a temporary, and this temporary is then copied into the result after the value you returned has been destroyed. This is normal behavior (unless the call is elided because of NRVO).
However, since your class has no explicitly defined copy constructor, the implicitly generated one will be invoked, and that will just copy a pointer (mat) to stuff that has been deallocated by the returned object's destructor.
This is a violation of the so-called Rule of Three, a programming best-practice saying that whenever your class explicitly defines a copy-constructor, an assignment operator, or a destructor, then it should define all of them. The rationale is that a class that defines one of them most likely does so because it is managing some resource, and for correctly handling resource releasing/acquiring logic, all of those three special member functions are needed.
Notice that in C++11 you can also have a move constructor that will be allowed to perform the transfer of the Matrix's content just by assigning pointers and invalidating the object you moved from.
Matrix(Matrix&& m)
{
mat = m.mat;
m.mat = nullptr;
}
Of course, if you introduce a move constructor, you will have to modify your class destructor accordingly to check if you really have to release the allocated memory:
~Matrix()
{
if (m.mat == nullptr)
{
return;
}
...
}
Your Matrix class has a raw pointer member and presumably allocates memory in its constructors, yet you have no copy constructor or copy assignment operator.
Also, you have a destructor, yet you have no copy constructor or copy assignment operator. This is a violation of the Rule of Three.
Your Matrix c is a local variable. So it is destroyed when that method where it was created ends. In C++ this unwanted situation is usually solved by copying objects. You can define copy constructor and assignment operator = with the same functionality. The problem of copying is that it is slow, so if you want it to be faster, you should use a different approach withotu copying. For example, you can add a parameter to the method where caller would pass a reference to the existing matrix object where to store the result.
You need a copy constructor and an assignment operator for your class that make a deep copy of the object as the compiler generated functions will not.
The compiler generated copy constructors and assignment operators will simply copy the objects contained in the class. In your case these are PODs, so the automatically generated functions will simply do a bitwise copy. In the case of the double**, this will result in the copy of the pointer value, not the pointed to values. As a result, you end up with two Matrix objects pointing to the same underlying data, just before the destructor pulls the rug out from underneath you.
You should change your code to return a Matrix *, rather than a Matrix object. This way you can ensure that the Matrix object lives after the function. (Your current code makes the Matrix object a function variable, thus it will be removed after the function has ended).
Your code could look like this:
Matrix *Matrix::operator+(const Matrix& other)
{
Matrix *c = new Matrix(m,n);
for(int i=0;i<m;i++)
{
for(int j = 0; j<n;j++)
c->mat[i][j] = mat[i][j] + other.mat[i][j];
}
return c;
}
EDIT: Apparently this is bad practice, guess I also learned something today :)

Will this addition class cause a memory leak?

Here is an example of a class that is made available for the + operation.
class A
{
public:
int *array;
A()
{
array = new int[10];
}
~A()
{
delete[] array;
}
A operator+ (const A &b)
{
A c;
for(int i=0; i<10; i++)
c.array[i] += array[i] + b.array[i];
return c;
}
};
int main()
{
A a,b,c,d;
/* puts some random numbers into the arrays of b,c and d */
a = b+c+d;
}
Will a run the destructor before copying the result of b+c+d or not? If not, how do I make sure no memory is leaked?
The + operator overload is designed this way such that no operand is modified.
You need to add an equals operator to A. Also, you will likely want to create a copy constructor.
When a becomes the return from b+c+d, the array pointer in a gets over written without delete[] ever being called on it. You need to make an operator= that deletes the array.
An example of an operator= is below:
A& operator=(A const& a)
{
if (&a != this) {
int* tmp = this->array;
this->array = new int[10];
//copy a.array to this->array
delete[] tmp;
}
return *this;
}
There's a lot of subtleties in this if you're new to operator=.
In particular, the check whether or not a is equal to this is necessary because it's perfectly valid to write:
A a;
a = a;
This would cause a pointless copy, and in most cases of operator= will cause bugs.
The other subtlety is less of a requirement than that one and more of a coding style (though a very wide spread standard). When copying something that is dynamically allocated, you always want to allocate and copy before you release. That way, if new throws an exception (or something else fails), the object is still in a stable state, though with it's old data instead of the new expected dated.
Will this cause memory leak?
Yes, it will. You forgot to add copy constructor and assignment operator. See Rule of Three
You could also use std::vector<int> for A::array instead of int*. In this case you wouldn't need to worry about copy constructor/assignment operator (as long as you don't add something else that must be handled in destrcutor).