0

Again with templates. Now I'm trying to write a Deleter function class so that it works similar to the language one delete. Here is my attempt:

class Deleter
{
public:
    template <typename T>
    void operator()(T*) const;
};

template <typename T>
void Deleter::operator()(T* ptr) const
{
    std::cout << "freeing memory...\n";
    delete ptr;
}


int main()
{

    int* pi = new int(7);
    char* cp = new char[100];
    strcpy(cp, "hi there!");

    Deleter del;
    del(pi); // 1- argument deduced is int*
    del(cp); // 2- argument deduced is char*

    std::cout << "\ndone\n";
}

I've overloaded the call operator as a template thus at compile time it knows the static type of argument. (I could made it non-template replacing T with void*).

It works fine but I think surely that statement 2 will cause a Memory leak because I am passing a pointer to a dynamic array into delete which works on pointers to dynamic object not array and as I've already learned it is undefined behavior to call delete on an object that had not been allocated with the corresponding new operator.

So Is there a workaround in my code so that I can call del[]cp;? Thank you!

11
  • Why do you need to do any of this instead of using standard library containers which manage the memory on their own? Commented Nov 6, 2020 at 20:56
  • @UnholySheep: Just for practice. After all I prefer using smart pointers as much as possible. Commented Nov 6, 2020 at 20:57
  • 1
    Practicing what exactly? You cannot deduce from a pointer in what way it has been allocated, that information is not available there Commented Nov 6, 2020 at 20:57
  • 1
    I don't see an easy solution. The information on whether it was allocated via new or new[] is not available. Commented Nov 6, 2020 at 20:58
  • 1
    You could create a second class called ArrayDeleter and have the delete in operator() use [] like delete [] ptr; Commented Nov 6, 2020 at 21:21

1 Answer 1

3

You cannot use delete with memory allocated with new[], and vice versa using delete[] with memory allocated with new. This is exactly why std::unique_ptr and std::shared_ptr have specializations to decide when to use delete[] vs delete for array and non-array memory, respectively (via the std::default_delete template).

You are just going to have to specialize your Deleter in a similar manner, too. Only the user of your Deleter, not the Deleter itself, can decide which specialization to use, since only the user has information about how the memory was allocated to begin with, and thus how it needs to be freed.

For example:

template <typename T>
class Deleter
{
public:
    void operator()(T* ptr) const {
        std::cout << "freeing memory using 'delete'...\n";
        delete ptr;
    }
};

template <typename T>
class Deleter<T[]>
{
public:
    template <typename U>
    void operator()(U* ptr) const {
        std::cout << "freeing memory using 'delete[]'...\n";
        delete[] ptr;
    }
};

int main()
{
    int* pi = new int(7);
    char* cp = new char[100];
    strcpy(cp, "hi there!");

    Deleter<int> del1;
    del1(pi); // uses 'delete'...

    Deleter<char[]> del2;
    del2(cp); // uses 'delete[]'...

    std::cout << "\ndone\n";
}

Live Demo

Sign up to request clarification or add additional context in comments.

3 Comments

so can I call it with an r-value this way: Deleter<char[]>()(cp);?
@Maestro Yes. Though consider using {} instead of () when constructing the Deleter object, eg: Deleter<char[]>{}(cp); But both ways will work.
Ah yes to avoid the vexing parser?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.