This is common trick to reduce the number of memory allocations and improve locality of references by allocating containers of a dynamic size at the end of an object (with a fixed size).
In C++ though, using that extra member[1]
causes a bit of trouble - that member is automatically initialized, while the rest of the elements are not. It is better to avoid declaring that member at all and rather provide accessors/iterators for the elements. And then initialize and destroy all the members manually. E.g.:
struct address {
int x;
int y;
};
struct details {
int count;
int size;
address* addr_begin() { return reinterpret_cast<address*>(this + 1); }
address* addr_end() { return addr_begin() + count; }
static void* operator new(size_t sizeof_details, int count) {
return ::operator new(sizeof_details + count * sizeof(address));
}
static void operator delete(void* p) {
::operator delete(p);
}
static std::unique_ptr<details> create(int count, int size) {
return std::unique_ptr<details>(new(count) details(count, size));
}
~details() {
std::for_each(addr_begin(), addr_end(), [](address& a) { a.~address(); });
}
private:
details(int count, int size)
: count(count)
, size(size)
{
std::uninitialized_fill(addr_begin(), addr_end(), address{});
}
};
int main() {
auto det = details::create(10, 10);
}
If you cannot change the structures, then:
#include <new>
#include <algorithm>
struct address {
int x;
int y;
};
struct details {
int count;
int size;
address addre[1];
};
details* create_details(int count, int size) {
void* mem = ::operator new(sizeof(details) + (count - 1) * sizeof(address));
auto* p = new (mem) details{count, size};
std::uninitialized_fill(p->addre + 1, p->addre + count, address{});
return p;
}
void destroy_details(details* p) {
std::for_each(p->addre + 1, p->addre + p->count, [](address& a) { a.~address(); });
p->~details();
::operator delete(p);
}
int main() {
auto* p = create_details(10, 10);
destroy_details(p);
}