operator overloading memory leak

2019-09-02 10:18发布

问题:

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.

回答1:

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?



回答2:

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.


回答3:

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.