I understand we can use Class-specific overloads of operator new[]
/delete[]
to specify our own memory allocation strategy.
So I write the following code. I barely want to use the held
field to record how much memory of a type X<id>
is allocated on heap.
However, I meet the problem that in operator new[]
, I can get the count
, while I can't in operator delete[]
. So in operator delete[]
, I don't know how much memory is actually released.
#include <cstdio>
#include <string>
#include <vector>
template <int Id>
struct X {
static inline int held = 0;
static void* operator new(std::size_t count)
{
held += count;
return ::operator new(count);
}
static void* operator new[](std::size_t count)
{
held += count;
return ::operator new[](count);
}
static void operator delete(void* ptr)
{
held -= sizeof(X<Id>);
::operator delete(ptr);
}
static void operator delete[] (void* ptr)
{
// This is not correct.
// However, I don't know the count.
held -= sizeof(X<Id>);
::operator delete [] (ptr);
}
std::string s1;
std::vector<int> v1;
};
int main() {
auto * dx2 = new X<1>[100]();
printf("x1d2 %d\n", X<1>::held);
delete [] dx2;
printf("x1d2 released %d\n", X<1>::held); // exptected 0
}
I understand one way is that I can allocate an extra bytes to store the count, it introduces extra overhead though.
Moreover, the compiler will actually store the count
somewhere when compiling operator delete []
, however, it is not exposed to users. I think I am now allowed to access the count
.
Since C++14, there are flavors of operator delete
(number (5)) and operator delete[]
(number (6)), that you can override and have an additional parameter for the allocated size:
void operator delete ( void* ptr, std::size_t sz ) noexcept; (5)
void operator delete[] ( void* ptr, std::size_t sz ) noexcept; (6)
These operators have priority over the ones without the size (at least for classes with non-trivial destructor like yours):
Called instead of (1,2) if a user-defined replacement is provided, except that it's unspecified whether (1,2) or (5,6) is called when deleting objects of incomplete type and arrays of non-class and trivially-destructible class types.
In your case you can replace your operator delete
and operator delete[]
with:
static void operator delete(void* ptr, std::size_t sz)
{
held -= sz;
::operator delete(ptr);
}
static void operator delete[](void* ptr, std::size_t sz)
{
held -= sz;
::operator delete[](ptr);
}
Output on MSVC (last value is 0 as expected):
x1d2 7208
x1d2 released 0
Some notes:
held
should better be a size_t
to match the type in the new
and delete
operators.std::cout
instead of the less-recommnded C style printf
s.held
has different values after the allocation in different compilers because the size of classes is implementation dependant (but it is always 0 after deallocation).operator delete[]
may cause memory increase for classes with a trivial destructor, because it will force the compiler to store the size (that otherwise it would not have to).