web-dev-qa-db-fra.com

Comment invoquer correctement nouveau/supprimer?

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 []?

4
kreuzerkrieg

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.

1
RbMm