Recently I got a task to do in C++, implement a Set class with union, intersection etc. as overloaded operators. I've got a problem with overloading an operator+(). I decide to use vectors and get the advantage of some algorithm's library functions. The problem is I HAD TO pass to constructor an array pointer and array size. This complicated this task a bit... I can compile it but during the "z=a+b" operation I encounter somekind of memory leak. Can anyone explain me what am I doing wrong?
class Set {
int number; // array size (can't be changed)
int *elems; // array pointer (same)
public:
Set();
Set(int, int*); // (can't be changed)
~Set();
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
};
Set::Set(){};
Set::Set(int n, int* array){
number=n;
elems = array = new int[number];
for(int i=0; i<number; i++) // creating Set
std::cin >> elems[i];
std::sort(elems, elems + number);
}
Set::~Set(){
delete[] elems;
}
int main(){
int* pointer;
Set z;
Set a = Set(5, pointer);
Set b = Set(2, pointer);
z=a+b;
}
I added copy constructor and copy assingment, changed the operator+() as NathanOliver advised and now I am passing to constructor static array. Still have memory leak and strange thing is that I got this memory leak even when in main there is only class variable initialization, doesn't matter if with parameters or not... Any suggestions? I think cunstructor is valid.
Set::Set(int n, int* array){
number = n;
elems = array;
std::sort(elems, elems + number);
}
Set::Set(const Set& s){
number=s.number;
elems=s.elems;
}
Set& operator=(const Set& X){
if(this==&X)
return *this;
delete [] elems;
elems=X.elems;
number=X.number;
return *this;
I use gcc (tdm64-2) 4.8.1 compiler.
In
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
You create a vector, modify it and then set the elems to point to what the vector contains. The issue with that is that when the vector is destroyed at the end of the function the memory that the vector held is released. So you now have a pointer pointing to memory you no longer own. Trying to do anything with it is undefined behavior. What you could do is create a new array, copy the elements of the vector into the array and then assign the new array to `elems
Set Z;
Z.number= v.size();
Z.elems= new int[z.number];
for (int i = 0; i < Z.number; i++)
Z.elems[i] = v[i];
return Z;
Secondly you need to define a copy constructor and assignment operator for you class. To do that reference: What is The Rule of Three?
The best solution (but I can't tell if you're allowed to do this specifically) is to use vector internally in your Set and assign it from the passed in pointer and length using the two-iterator constructor.
Now if that's not possible you need to properly manage the memory of your class:
You need a copy constructor and copy assignment operator.
In your operator+ you can't create a local vector and then take the address of its memory, that memory will disappear as soon as the operator returns.
Possibly other things I didn't catch.
When you have z=a+b, the assignment operator for the Set class is used. You didn't define a custom version of this operator, so the default compiler-generated one is used. This compiler-generated assignment operator=() simply does a member-wise copy.
Since you have raw owning pointers in your Set class, this doesn't work correctly: the default compiler-generated operator=() shallow-copies pointers, instead you should deep-copy the data.
An option to fix this is to define your own version of operator=(), paying attention to do proper deep-copy of source data.
Note that in this case you also should define a copy constructor.
But a better option would be to get rid of the owning raw pointer data members, and instead use a RAII building block class, like std::vector.
So, for example, instead of these data members:
int number; // array size (can't be changed)
int *elems; // array pointer (same)
you could just have a single:
std::vector<int> elems;
If you do that, the default compiler-generated operator=() will work just fine, since it will copy the std::vector data member (not the raw owning pointers), and std::vector knows how to properly copy its content without leaking resources.
Related
In Stroustrups The C++ Programming Language Fourth Edition, on page 76, there is example of move constructor use.
The class is defined like this:
class Vector {
private:
double∗ elem; // elem points to an array of sz doubles
int sz;
public:
Vector(int s) :elem{new double[s]}, sz{s}
{ for (int i=0; i!=s; ++i) elem[i]=0; // initialize elements }
~Vector() { delete[] elem; } // destructor: release resources
Vector(const Vector& a); // copy constructor
Vector& operator=(const Vector& a); // copy assignment
Vector(Vector&& a); // move constructor
Vector& operator=(Vector&& a); // move assignment
double& operator[](int i);
const double& operator[](int i) const;
int size() const;
};
The move constructor is defined:
Vector::Vector(Vector&& a) :elem{a.elem}, // "grab the elements" from a
sz{a.sz}
{
a.elem = nullptr; // now a has no elements
a.sz = 0;
}
Example execution which, I think, will cause memory leak:
Vector f()
{
Vector x(1000);
Vector y(1000);
Vector z(1000);
// ...
z=x; //we get a copy
y = std::move(x); // we get a move
// ...
return z; //we get a move
};
It seems that such move operation will cause memory leak, because y had been allocated 1000 elements in
Vector y(1000);
and simple pointer re-assignment at line y = std::move(x); would leave these initial 1000 ints pointed by y left on its own. I assume, the move constructor has to have extra line of code to de-allocate pointer 'elem' before moving.
Assuming the move constructor is implemented properly, no. The line you are refering to invokes move assignment, not constructor, because z already exists.
Is there a memory leak in this C++ move constructor?
No.
Example execution which, I think, will cause memory leak:
The shown execution calls move assignment, not move constructor.
I assume, the move constructor has to have extra line of code to de-allocate pointer 'elem' before moving.
It does not need to, because the pointer of the newly constructed object can not have pointed to any allocated memory before it was initialized with the value of the pointer from the move argument.
Yes, agree, this is move assignment: y = std::move(x);, not the constructor.
In the book they did not show it, just mentioned that move assignment is defined similarly.
But, in the move assignment definition, as I understand, we would need to deallocate elem, right? Something like this.
Vector& Vector::operator=(Vector&& a)
{
delete[] elem;
elem = a.elem;
sz = a.sz;
a.elem = nullptr; // now a has no elements
a.sz = 0;
}
and simple pointer re-assignment at line y = std::move(x); would leave
these initial 1000 ints pointed by y left on its own.
The address of the pointer to double is copied over to the new object (y) and the old object (x) has it's pointer set to nullptr. (It's happening in the move assignment method not shown)
I assume, the move constructor has to have extra line of code to
de-allocate pointer 'elem' before moving.
No deallocation needed here. The pointer is just 'moved' during assignment (it would also be moved during move construction). Cleanup of the double pointer should be handled by the destructor. In the case of x, the destructor would eventually fire on the nullptr. delete[] on a nullptr is a noop. see http://www.cplusplus.com/reference/new/operator%20delete[]/
The doubles allocated in y would however need to be cleaned out during the move assignment as pointed out.
This question already has answers here:
How is vector implemented in C++
(9 answers)
Closed 8 years ago.
Not so far I discovered, that I completely have no clue about nature of std::vector.
Let me explain:
Vector is growable, right? That means, inside it must somehow allocate/reallocate memory dynamically. Something like this:
class vector {
private:
int *data;
};
Okay. But such definition implies the fact that if we pass std::vector to another function by reference or by value -- there will be no difference between this two types of parameter passing, and both function will be able to modify data (unless vector is passed as const).
BUT! I tried the following and my idea failed:
void try_to_modify(vector<int> v) {
v[2] = 53;
}
int main() {
vector<int> v(3);
v[2] = 142;
try_to_modify(v);
cout << v[2] << '\n'; // output is: 142
return 0;
}
So where's the truth? What std::vector really is?
Thank you.
std::vector is a container, which internally manages its memory and provides a custom copy constructor. In this copy constructor, new memory is allocated and the existing data is copied over, which makes it a expensive operation. If you want to pass a vector without copying the contained data, you can pass by const reference, for example, const std::vector<int>&.
Let's take a look how to implement a basic container like std::vector.
template <typename T>
class MyVector
{
public:
MyVector (int size)
: data_ (new T[size])
, size_ (size)
{}
~MyVector ()
{
delete [] data_;
}
private:
T* data_ = nullptr;
int size_ = 0;
};
If we copy such an object, we'll have two problems. First, as you noticed, the memory will point to the same location. Second, we will have two destructors that will destruct the same memory, resulting in a double-free. So, let's add a copy constructor, which will be invoked whenever a copy is made.
MyVector (const MyVector& other)
: size_ (other.size_)
{
data_ = new T[size_];
std::copy (other.data_, other.data_ + size_, data_);
}
MyVector& operator= (const MyVector& other)
{
// allocate and copy here to allow for self-assignment
auto newData = new T[other.size_];
std::copy (other.data_, other.data_ + size_, newData);
delete [] data_;
size_ = other.size_;
data_ = newData;
return *this;
}
And that's how std::vector works internally.
This isn't so much a problem with vector as it is with pass-by-value and pass-by-reference arguments.
void try_to_modify (vector<int> vec) will send a copy of the original vector to the function and the function operates on a copy. The vector will copy the data when a copy is made. i.e. new pointer to new data.
However, if you define your function as: void try_to_modify(vector<int> & vec) then it will send the exact vector to the function and your function will operate on it.
Passing by reference is much faster for objects and is usually preferable, unless you have a specific need for a copy.
I'm a noob in c++. I have a problem that I cannot solve anyway. I wrote a code to understand better classes and overload operator:
#include <iostream>
#include <stdlib.h>
#include <stdarg.h>
using namespace std;
class vectmy {
public:
int size;
int *a;
vectmy(int n,int val);
~vectmy (){delete[] a; //IF I DELETE THIS PROGRAM WORKS
}
vectmy & operator = (const vectmy &);
};
vectmy::vectmy(int n,int val){
size=n;
a = new int[ n+1 ];
for (int i=0;i<n;++i){
*(a+i)=val;
}
}
vectmy& vectmy::operator= (const vectmy& param)
{
for (int i=0;i<3;++i) a[i]=param.a[i];
return *this;
}
vectmy operator+( vectmy left, vectmy right)
{
vectmy result = left;
for (int i=0;i<3;++i) result.a[i]=result.a[i]+right.a[i];
return result;
}
int main() {
int b1[3]={1,2,4};
vectmy d(3,2),b(3,4),c(3,0);
c=(b+d);
for (int j=0; j<3; ++j)cout<<c.a[j]<<' '<<endl;
return 0;
}
When I run it crashes. If I remove the destructor it works. Why it happens?
When I run it crashes. If I remove the destructor it works. Why it happens?
Your operator + creates a copy of left here:
vectmy result = left;
Since you haven't defined a copy constructor explicitly, the compiler will generate one implicitly that performs member-wise copying.
A dull copy of the a data member means that the a pointer will eventually point to the same location for two different instances of vectmy (result and left), both of which will delete[] it upon destruction.
Such double deletion gives your program undefined behavior, which in your case manifests itself as a crash.
This is the point of the Rule of Three: every time you have a user-defined copy-constructor, assignment operator, or destructor, you should probably define all of them.
The reason is that you normally define one of those functions because you are managing some resource (memory, in your case), and you usually want to perform proper actions when copying, destructing, or assigning objects that manage a resource.
In this particular case, a proper copy constructor is missing. This is how you could define it:
vectmy::vectmy(vectmy const& v)
{
size=v.size;
a = new int[size];
*this = v;
}
Also, I would suggest you to avoid manual memory management through raw pointers, new, and delete (or their array counterparts) whenever you can, and consider using std::vector instead.
UPDATE:
Also notice, that your operator + is accepting its parameters by value, which means that each argument will be copied (i.e., the copy constructor will be invoked).
Since a copy is not really necessary here, you may want to take your parameters by reference (to const):
vectmy operator + ( vectmy const& left, vectmy const& right)
// ^^^^^^ ^^^^^^
In your operator+ you do
vectmy result = left;
This will call the default constructor and copy constructor, of which you have none. So the compilers variants will be used, which doesn't allocate memory for the a member. For the automatically generated copy constructor the pointer will simply be copied, making two object use the same pointer. When one deletes it, the other pointer becomes invalid.
You should read about the rule of three.
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 :)
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).