35 votes

Allocation dynamique d'un tableau de classe avec destructeur protégé

Si j'ai une classe définie comme

class A {
protected:
    ~A(){ }
};

alors je peux allouer dynamiquement l'individu ainsi que le tableau d'objets comme

A* ptr1 = new A;
A* ptr2 = new A[10];

Cependant, lorsque je définis le constructeur de cette classe

class A {
public:
    A(){}
protected:
    ~A(){ }
};

puis je peux créer des objets individuels avec

A* ptr = new A;

mais quand j'essaie d'allouer dynamiquement le tableau d'objet avec

A* ptr = new A[10];

Le compilateur (gcc-5.1 et Visual Studio 2015) commence à se plaindre que A::~A() est inaccessible.

Quelqu'un peut-il m'expliquer ce qu'il en est:-

1- Pourquoi y a-t-il une différence de comportement entre un constructeur défini et un constructeur non défini ?

2- Lorsque le constructeur est défini, pourquoi suis-je autorisé à créer un objet individuel et non un tableau d'objets ?

17voto

Matteo Italia Points 53117

Rejeter un tableau new avec un destructeur protégé est correct, conformément à C++11, §5.3.4 ¶17 :

Si la nouvelle expression crée un objet ou un tableau d'objets de type classe, le contrôle d'accès et d'ambiguïté est effectué pour la fonction d'allocation, la fonction de désallocation (12.5), et le constructeur (12.1). Si la nouvelle expression crée un tableau d'objets de type classe, le contrôle d'accès et d'ambiguïté se fait pour le destructeur (12.4).

(c'est nous qui soulignons ; presque exactement la même formulation est utilisée dans C++03, §5.3.4 ¶16 ; C++14 déplace certaines choses, mais cela ne semble pas changer le cœur de la question - voir @Baum mit Augen (réponse de la Commission)

Cela vient du fait que new[] ne réussit que si tous les éléments ont été construits, et veut éviter les fuites d'objets au cas où l'un des appels au costructeur échoue ; ainsi, s'il parvient à construire - disons - les 9 premiers objets mais que le 10ème échoue avec une exception, il doit déstructurer les 9 premiers avant de propager l'exception.

Remarquez que cette restriction n'est logiquement pas nécessaire si le constructeur est déclaré en tant que noexcept mais la norme ne semble pas prévoir d'exception à cet égard.


Donc, ici, gcc a techniquement tort dans le premier cas, ce qui, en ce qui concerne la norme, devrait être rejeté également, bien que je soutienne que "moralement" gcc fait la bonne chose (car en pratique, il n'y a aucun moyen que le constructeur par défaut de A peut jamais lancer).

10voto

Baum mit Augen Points 3571

Il s'avère que gcc n'est pas correct ici. Dans N4141 (C++14), nous avons :

Si l'expression new-expression crée un tableau d'objets de type classe, le destructeur est potentiellement invoqué (12.4).

(5.3.4/19 [expr.new]) et

A Un programme est mal formé si un destructeur potentiellement invoqué est supprimé ou n'est pas accessible dans le contexte de l'invocation. de l'invocation.

(12.4/11 [classe.dtor]). Donc les deux cas d'array doivent être rejetés. (Clang le fait bien, en direct .)

La raison en est, comme mentionné par d'autres et par mon ancienne réponse incorrecte, que la construction des éléments de la classe type peut potentiellement échouer avec une exception. Lorsque cela se produit, le destructeur de tous les éléments entièrement construits doit être invoqué, et donc le destructeur doit être accessible.

Cette limitation ne s'applique pas lors de l'attribution d'un seul élément avec operator new (sans le [] ), parce qu'il ne peut y avoir d'instance entièrement construite de la classe si l'appel au constructeur unique échoue.

3voto

Walter Points 7554

Je ne suis pas un juriste spécialisé dans les langues (je connais très bien la norme), mais je soupçonne que la réponse va dans le sens de celle donnée précédemment par Baum mit Augen (supprimée, pour que seuls ceux qui ont une réputation suffisante puissent la voir).

Si la construction des éléments suivants du tableau échoue et lève une exception, les éléments déjà construits devront être supprimés, ce qui nécessite l'accès au destructeur.

Cependant, si le constructeur est noexcept Si l'accès au destructeur n'est pas possible, cela peut être exclu et l'accès au destructeur n'est pas nécessaire. Le fait que gcc et clang continuent à se plaindre même dans ce cas, pourrait bien être une conséquence de l'utilisation de l'accès au destructeur. bug du compilateur . C'est-à-dire que le compilateur ne prend pas en compte le fait que le constructeur est noexcept . Il se peut aussi que les compilateurs soient conformes à la norme, auquel cas, cela ressemble à un défaut de la norme .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X