Search code examples
c++pointersdynamic-allocation

Thinking I should be allowed to `delete` a pointer but an error is thrown `block in use`


Consider this function:

bool RemoveElement(const T2& e2)
{
    bool match = false;

    for (int i = 0; i < _trenutno; i++)
    {
        if (*_elementi2[i] == e2){

            match = true;

            for (int j = i; j < _trenutno - 1; j++)
            {
                _elementi1[j] = _elementi1[j + 1];
                _elementi2[j] = _elementi2[j + 1];

                // SOLUTION: The two lines above are not correct.
                // Instead of assigning addresses, I should have assigned
                // values, ie. dereference them like below:

                // *_elementi1[j] = *_elementi1[j + 1];
                // *_elementi2[j] = *_elementi2[j + 1];

            }
        }
    }


    if (match)
    {
        _trenutno--;

        //delete _elementi1[_trenutno];
        _elementi1[_trenutno] = nullptr;

        //delete _elementi2[_trenutno];
        _elementi2[_trenutno] = nullptr;
    }

    return match;
}

FYI, the pointers themselves (see below). I'm trying in the above function delete values behind this particular pair of pointers (last element) using delete because otherwise I believe memory leak would occur?

template<class T1, class T2, int max>
class Kolekcija
{
    T1* _elementi1[max];
    T2* _elementi2[max];
    int _trenutno;
public:
    Kolekcija()
    {
        for (int i = 0; i < max; i++)
        {
            _elementi1[i] = nullptr;
            _elementi2[i] = nullptr;
        }
        _trenutno = 0;
    }
    //  ...

    ~Kolekcija()
    {
        for (int i = 0; i < _trenutno; i++){
            delete _elementi1[i]; _elementi1[i] = nullptr;
            delete _elementi2[i]; _elementi2[i] = nullptr;
        }
    }
    // ....

Why is this happening, I'd like to learn more and better understand pointers.


Solution

  • If your class is the data owner and you want to remove some element:

    1. Find index of element (_elementi1[nFound] == searchValue)
    2. Delete this element from heap delete _elementi1[nFound];
    3. Set element's value to nullptr _elementi1[nFound] = nullptr;
    4. Only then move trailing elements in place of removed item.

    This sequence will protect you against memory leaks in Remove method.

    Destructor will clean other heap allocated values (assuming _trenutno is actual count):

        for (int i = 0; i < _trenutno; i++)
        {
            delete _elementi1[i]; _elementi1[i] = nullptr;
            delete _elementi2[i]; _elementi2[i] = nullptr;
        }
    

    Deleting nullptr is safe.

    So my version of Remove():

    bool RemoveElement(const T2& e2)
    {
        for (int i = 0; i < _trenutno; i++)
        {
            if (*_elementi2[i] == e2) 
            {
                delete _elementi1[i];
                _elementi1[i] = nullptr;
                delete _elementi2[i];
                _elementi2[i] = nullptr;
    
                for (int j = i; j < _trenutno - 1; j++)
                {
                    _elementi1[j] = _elementi1[j + 1];
                    _elementi2[j] = _elementi2[j + 1];
                }
                _trenutno--;
                return true; //found
            }
        }
        return false; //not found
    }
    

    I'm assuming that your collection is the data owner. So you have to delete all pointers that was passed to it. Simple example (we'r adding 3 values):

    int* elements[max] = {0};
    elements[0] = new int(4); //0x00FF1F
    elements[1] = new int(5); //0x00FF2F
    elements[2] = new int(6); //0x00FF3F
    _trenutno = 3;
    

    We have to delete all 3 int*

    If no Remove was called destructor will handle it (delete from 0 to 2).

    If we call Remove(5):

    1. Found index of 5 value i == 1
    2. Call delete elements[1] that means releaze memory at address 0x00FF2F
    3. Make shift: elements[1] = elements[2]

    Now our array is:

    elements[0]; //0x00FF1F
    elements[1]; //0x00FF3F
    elements[2]; //0x00FF3F
    

    And _trenutno = 2;

    So destructor will remove pointers from '0' to '1'.

    That is all 3 pointers where deleted!