6
votes

I have implemented a function where the identity is given to me and out of my control. It returns std::shared_ptr<const void>. In the function i allocate an arbitrary amount of memory, and return access to it though the shared_ptr.

My memory allocation is done with new unsigned char[123]. The problem is that valgrind detects a mismatch between the usage of new and delete variants. While i use new[](unsigned) to allocate memory, the shared_ptr destructor uses delete(void*) to deallocate it, and valgrind warns whenever you are using the "incorrect" deallocator for an allocation.

In more practical terms, i wrote this test case to show what i mean:

TEST(Example, Test1)
{
  unsigned char* mem = new unsigned char[123];
  std::shared_ptr<const void> ptr(mem);
}

The valgrind report says

==45794== Mismatched free() / delete / delete []
==45794==    at 0x4C2A64B: operator delete(void*) (vg_replace_malloc.c:576)
==45794==    by 0x40B7B5: _M_release (shared_ptr_base.h:150)
==45794==    by 0x40B7B5: ~__shared_count (shared_ptr_base.h:659)
==45794==    by 0x40B7B5: ~__shared_ptr (shared_ptr_base.h:925)
==45794==    by 0x40B7B5: ~shared_ptr (shared_ptr.h:93)
==45794==    by 0x40B7B5: Example_Test1_Test::TestBody() (test.cc:108)
==45794==  Address 0x5cb6290 is 0 bytes inside a block of size 123 alloc'd
==45794==    at 0x4C29CAF: operator new[](unsigned long) (vg_replace_malloc.c:423)
==45794==    by 0x40B72E: Example_Test1_Test::TestBody() (test.cc:107)

I want to avoid valgrind filters if possible.

What is the correct way to allocate an arbitrary amount of data and return as a std::shared_ptr<const void>?

1
What about allocation with malloc? - Yury
If you use malloc instead, you get a similar warning because you have not matching free. - Martin G

1 Answers

17
votes

If you give shared_ptr<T> a T pointer, it assumes that you created a single object and implies the deleter delete p;. If your allocation was actually performed with array-new, you need to pass an appropriate deleter that performs delete[] p;. You can reuse std::default_delete if you like:

return static_pointer_cast<const void>(
    std::shared_ptr<unsigned char>(
        new unsigned char[N],
        std::default_delete<unsigned char[]>()));

(You don't even need the outer cast, since the conversion is implied.)

In C++17, shared_ptr supports arrays, so you can say

shared_ptr<unsigned char[]>(new unsigned char[N])

there and get the correct deleter (and then convert to void).