I was writing an aligned operator for vector and I realize that I don't know the difference between operator aligned new/delete vs operator aligned new[]/delete[].
Both seem only de/allocate memory, not call de/constructor, both take the size in byte. So what the point having both ?
What I am missing ?
Demo code : https://wandbox.org/permlink/ErBIKvtt3t7wczpm
#include <iostream>
#include <iterator>
#include <vector>
#include <new>
#include <type_traits>
#include <memory>
template <class T>
T* allocate_bracket(std::size_t count)
{
return static_cast<T*> ( ::operator new[](count * sizeof(T) , std::align_val_t{64}));
}
template <class T>
void deallocate_bracket(T* p, size_t count ) noexcept {
::operator delete[](p, count, std::align_val_t{64} );
}
template <class T>
T* allocate_basic(std::size_t count)
{
return static_cast<T*> ( ::operator new(count * sizeof(T) , std::align_val_t{64}));
}
template <class T>
void deallocate_basic(T* p, size_t count ) noexcept {
::operator delete(p, count, std::align_val_t{64} );
}
struct Noisy
{
Noisy() {
std::cout << "ctr" << std::endl;
}
~Noisy() {
std::cout << "~" << std::endl;
}
};
int main()
{
const size_t nb = 2;
std::cout << "Only alocate bracket \n";
{
auto ptr = allocate_bracket<Noisy>(nb);
deallocate_bracket<Noisy>(ptr, nb);
}
std::cout << "Only alocate basic \n";
{
auto ptr = allocate_basic<Noisy>(nb);
deallocate_basic<Noisy>(ptr, nb);
}
std::cout << "alocate/create bracket \n";
{
auto ptr = allocate_bracket<Noisy>(nb);
new (ptr) Noisy[nb];
ptr[nb-1] = Noisy();
for (size_t i= 0 ; i < nb; i ) (ptr i)->~Noisy();
deallocate_bracket<Noisy>(ptr, nb);
}
std::cout << "alocate/create Basic \n";
{
auto ptr = allocate_basic<Noisy>(nb);
new (ptr) Noisy[nb]; // dunno if there is an other syntaxe
ptr[nb-1] = Noisy();
for (size_t i= 0 ; i < nb; i ) (ptr i)->~Noisy();
deallocate_basic<Noisy>(ptr, nb);
}
}
Valgrind is clean (no miss match new/new[]/free)
==3200== Memcheck, a memory error detector
==3200== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3200== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==3200== Command: ./op_new.out
==3200==
Only alocate bracket
Only alocate basic
alocate/create bracket
ctr
ctr
ctr
~
~
~
alocate/create Basic
ctr
ctr
ctr
~
~
~
==3200==
==3200== HEAP SUMMARY:
==3200== in use at exit: 0 bytes in 0 blocks
==3200== total heap usage: 6 allocs, 6 frees, 73,736 bytes allocated
==3200==
==3200== All heap blocks were freed -- no leaks are possible
==3200==
==3200== For lists of detected and suppressed errors, rerun with: -s
==3200== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
CodePudding user response:
You assumption that no de/constructors are called is wrong, although it happens at a level above that call in the new-expression and delete-expression.
The new[]
operators allocate a bit more and store the size of the array size so the delete-expression can call the destructors:
If expression is not a null pointer and the deallocation function is not a destroying delete (since C 20), the delete expression invokes the destructor (if any) for the object that's being destroyed, or for every element of the array being destroyed (proceeding from the last element to the first element of the array).
CodePudding user response:
Both seem only de/allocate memory, not call de/constructor, both take the size in byte. So what the point having both ?
They do the same thing because you called the default operators, which do the same thing. In fact the default operator new[]
just calls operator new
.
The reason for having two versions is that they could do different things, and a user might overload them separately.