Comment appeler l'opérateur new
avec alignement?
auto foo = new(std::align_val_t(32)) Foo; //?
et ensuite, comment le supprimer correctement?
delete(std::align_val_t(32), foo); //?
Si c'est la bonne forme d'utilisation de ces surcharges, pourquoi valgring se plaindre des incompatibles free ()/delete/delete []?
il existe un principe de base - la routine sans mémoire doit toujours correspondre pour allouer la routine. si nous utilisons une non-concordance, l'allocation et le comportement d'exécution libre peuvent être quelconques: ils peuvent tous être aléatoires, ou planter par exécution, ou par une fuite de mémoire ou une corruption de tas.
si nous allouons de la mémoire avec la version alignée de l'opérateur new
void* operator new ( std::size_t count, std::align_val_t al);
nous devons utiliser la version alignée correspondante de l'opérateur delete
void operator delete ( void* ptr, std::align_val_t al );
appeler void operator delete ( void* ptr );
ici doit toujours conduire à une erreur d'exécution. laissez simplement tester
std::align_val_t al = (std::align_val_t)256;
if (void* pv = operator new(8, al))
{
operator delete(pv, al);
//operator delete(pv); this line crash, or silently corrupt heap
}
pourquoi la version alignée et non alignée de operator delete
est-elle toujours incompatible? réfléchissons - comment est-il possible d’allouer align sur une mémoire de valeur? Au début, nous allouons toujours un bloc de mémoire. pour que le pointeur return align soit utilisé - nous devons ajuster le pointeur de la mémoire allouée sur plusieurs alignements. D'accord. Cela est possible en allouant plus de mémoire que demandé et en ajustant le pointeur. mais maintenant question - comment libre ce bloc? en général, l'utilisateur n'a pas pointé le début de la mémoire allouée - comment ce pointeur utilisateur revient-il au début du bloc alloué? sans information supplémentaire c'est impossible. nous avons besoin de stocker le pointeur sur la mémoire allouée avant que l'utilisateur ne renvoie le pointeur. peut-être que cela sera plus visible dans l'implémentation typique du code pour les variables new
et delete
alignées, utilisez _aligned_malloc
et _aligned_free
void* operator new(size_t size, std::align_val_t al)
{
return _aligned_malloc(size, static_cast<size_t>(al));
}
void operator delete (void * p, std::align_val_t al)
{
_aligned_free(p);
}
lorsque non aligné new
et delete
, utilisez malloc
et free
void* operator new(size_t size)
{
return malloc(size);
}
void operator delete (void * p)
{
free(p);
}
regardons maintenant l'implémentation interne de _aligned_malloc
et _aligned_free
void* __cdecl _aligned_malloc(size_t size, size_t alignment)
{
if (!alignment || ((alignment - 1) & alignment))
{
// alignment is not a power of 2 or is zero
return 0;
}
union {
void* pv;
void** ppv;
uintptr_t up;
};
if (void* buf = malloc(size + sizeof(void*) + --alignment))
{
pv = buf;
up = (up + sizeof(void*) + alignment) & ~alignment;
ppv[-1] = buf;
return pv;
}
return 0;
}
void __cdecl _aligned_free(void * pv)
{
if (pv)
{
free(((void**)pv)[-1]);
}
}
dans les mots généraux _aligned_malloc
allouer size + sizeof(void*) + alignment - 1
à la place demandé par l'appelant size
. ajustez le pointeur alloué pour qu’il corresponde à l’alignement et store à la mémoire allouée à l’origine avant que le pointeur ne soit renvoyé à l’appelant.
et _aligned_free(pv)
n'appelle pas free(pv)
mais free(((void**)pv)[-1]);
- pour toujours un autre pointeur. parce que cet effet de _aligned_free(pv)
toujours une autre comparaison free(pv)
. et operator delete(pv, al);
toujours incompatible avec operator delete(pv);
si, par exemple, delete []
a le même effet que delete
mais que align vs ne pas aligner toujours une durée d’exécution différente.