2010-12-11 45 views
12

Je suis sûr que ce code devrait être illégal, car il ne fonctionnera certainement pas, mais il semble être autorisé par le FCD C++ 0x. Peut-être que l'un de vos juristes de langue peut vous expliquer comment la norme l'interdit.Légalité de l'utilisation de l'opérateur delete sur un pointeur obtenu à partir du placement new

Il y a aussi une forme de tableau:

class X { /* ... */}; 
void* raw = malloc(sizeof (X)); 
X* p = new (raw) X[1]; // according to the standard, the RHS is a placement-new expression 
::operator delete[](p); // definitely wrong, per litb's answer 
delete [] p; // legal? I hope not 

This is the closest question j'ai pu trouver.

EDIT: Je suis tout simplement pas acheter l'argument selon lequel les arguments restrictifs de la langue de la norme de fonctionner void ::operator delete(void*) appliquent de manière significative à l'opérande de delete dans un supprimer l'expression. Au mieux, la connexion entre les deux est extrêmement ténue, et un certain nombre d'expressions sont autorisés comme opérandes à delete qui ne sont pas valides pour passer à void ::operator delete(void*). Par exemple:

struct A 
{ 
    virtual ~A() {} 
}; 

struct B1 : virtual A {}; 

struct B2 : virtual A {}; 

struct B3 : virtual A {}; 

struct D : virtual B1, virtual B2, virtual B3 {}; 

struct E : virtual B3, virtual D {}; 

int main(void) 
{ 
    B3* p = new E(); 
    void* raw = malloc(sizeof (D)); 
    B3* p2 = new (raw) D(); 

    ::operator delete(p); // definitely UB 
    delete p; // definitely legal 

    ::operator delete(p2); // definitely UB 
    delete p2; // ??? 

    return 0; 
} 

J'espère que cela montre que si un pointeur peut être transmis à void operator delete(void*) n'a pas d'incidence si ce même pointeur peut être utilisé comme opérateur de delete.

+1

FYI: Le FCD (N3092) n'est plus le dernier brouillon. Le dernier projet est N3225. J'ai gardé la [page wiki du tag C++ - 0x] (http://stackoverflow.com/tags/c%2b%2b0x/info) mise à jour avec un lien vers le dernier brouillon PDF. –

+1

Notez que 5.3.5/2, qui couvre cela, a été modifié dans le dernier projet. Il est maintenant dit que le pointeur peut être "un pointeur vers un objet non-array créé par une nouvelle expression", et une nouvelle expression inclut en effet de nouvelles expressions. Je ne pense pas que ce soit voulu. –

+0

@James: Merci beaucoup pour le nouveau brouillon. Et 5.3.5 est exactement la section que je pense devrait interdire cela, mais ne le fait pas. Pourriez-vous s'il vous plaît regarder ma réponse (je me prépare à tirer le moindre changement de langage de la nouvelle version) et laissez-moi savoir si vous pensez que cela a une incidence sur cette question? –

Répondre

2

Le compilateur ne se soucie pas vraiment que p provient d'un appel new de placement, de sorte qu'il ne vous empêchera pas d'émettre delete sur l'objet. De cette façon, votre exemple peut être considéré comme "légal".

Cela ne fonctionnera pas, car les opérateurs "placement delete" ne peuvent pas être appelés explicitement. Ils ne sont appelés implicitement que si le constructeur se lance, de sorte que le destructeur peut s'exécuter.

2

Je suppose que vous pourriez sortir avec elle (sur un compilateur particulier) si

  1. new/delete sont mises en œuvre en termes de malloc/free et
  2. le placement new utilise en fait le même mécanisme de maintien piste du destructeur associé à des allocations comme le standard new, de sorte que l'appel à delete pourrait trouver le bon destructeur.

Mais il n'est pas possible que ce soit portable ou sûr.

+1

Ce n'est certainement pas sûr, car cette expression de suppression est nécessaire pour appeler une fonction de désallocation, en passant un pointeur qui ne provient pas de la fonction d'allocation correspondante. –

2

J'ai trouvé dans la section de la bibliothèque de la norme, qui est à peu près aussi contre-intuitif un emplacement (OMI) possible: section

C++ 0x FCD (et le projet de N3225) 18.6.1.3, [new.delete.placement] :

Ces fonctions sont réservées, un programme C++ ne peut pas définir des fonctions qui les versions déplacent dans la norme bibliothèque C de (17.6.3). Les dispositions de (3.7.4) ne s'appliquent pas à ces formes de placement réservées de l'opérateur nouveau et suppression opérateur.

void* operator new(std::size_t size, void* ptr) throw(); 
void* operator new[](std::size_t size, void* ptr) throw(); 
void operator delete(void* ptr, void*) throw(); 
void operator delete[](void* ptr, void*) throw(); 

encore, la section définissant les expressions juridiques pour passer à delete est 5.3.5, et non 3.7.4.

7

Les règles standard à [basic.stc.dynamic.deallocation] p3

Dans le cas contraire, la valeur fournie à operator delete(void*) dans la bibliothèque standard doit être l'une des valeurs renvoyées par une invocation précédente soit operator new(size_t) ou operator new(size_t, const std::nothrow_t&) dans la bibliothèque standard et la valeur fournie à operator delete[](void*) dans la bibliothèque standard doit être l'une des valeurs renvoyées par un appel précédent de operator new[](size_t) ou de operator new[](size_t, const std::nothrow_t&) dans la bibliothèque standard.

Votre appel delete appellera les bibliothèques operator delete(void*), sauf si vous avez remplacé il. Puisque vous n'avez rien dit à ce sujet, je suppose que vous ne l'avez pas fait. Le verbe "doit" devrait ressembler à "comportement non défini sinon". Il ne s'agit donc pas d'une règle pouvant être diagnostiquée, ce qui n'est pas le cas de [lib.res.on.arguments] p1. Ceci a été corrigé par n3225 donc on ne peut plus s'y tromper.

+0

Cela s'applique certainement à l'appel direct des fonctions de désallocation en utilisant la syntaxe d'appel de fonction. Mais je ne pense pas qu'il soit immédiatement applicable aux invocations de 'operator delete', puisque le pointeur que j'utilise comme opérande de' operator delete' n'est pas nécessairement celui qui est finalement passé à la fonction deallocation. Eh bien, en lisant 5.3.4 '[expr.new]' de plus près, il en est de même pour scalaire nouveau. Pensez donc en termes de placement de tableau à la place, suivi d'une suppression de tableau. –

+0

@Ben Je ne comprends pas votre commentaire. –

+0

Le compilateur est censé être responsable de suivre les règles pour les fonctions de désallocation, tant que je suis les règles pour * new-expression * et * delete-expression *. Ce que ce code fait malheureusement, selon le libellé actuel cité par James. –