37 votes

Macros pour interdire la copie et l'affectation des classes. Google -vs- Qt

Pour interdire la copie ou l'affectation d'une classe, il est courant de rendre le constructeur de copie et l'opérateur d'affectation privés. Google et Qt ont tous deux des macros pour rendre cela facile et visible. Ces macros sont :

Google :

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);   \
  void operator=(const TypeName&)

Qt :

#define Q__DISABLE_COPY(Class) \
  Class(const Class &); \     
  Class &operator=(const Class &);

Questions : Pourquoi les signatures des deux opérateurs d'affectation sont-elles différentes ? Il semble que la version Qt soit correcte. Quelle est la différence pratique entre les deux ?

3 votes

Une autre différence très mineure est que l'adaptation de Qt se termine par un point-virgule.

50voto

jalf Points 142628

Cela n'a pas d'importance. Le type de retour ne fait pas partie de la signature d'une fonction, car il ne participe pas à la résolution des surcharges. Ainsi, lorsque vous tentez d'effectuer une affectation, les deux déclarations correspondent, que vous utilisiez ou non le type de retour.

Et puisque le but de ces macros est que les fonctions ne soient jamais appelées, il importe peu que l'une d'entre elles renvoie void .

0 votes

C'est ce que je pensais ... merci. C'était juste un peu inhabituel de voir la version Google, car sa signature n'est pas celle que l'on voit habituellement.

16voto

bartsimpson Points 143

Je voudrais juste mentionner qu'il existe une stratégie alternative pour implémenter une abstraction pour interdire la copie et l'affectation d'une classe. L'idée est d'utiliser l'héritage au lieu du préprocesseur. Personnellement, je préfère cette approche car je suis la règle empirique selon laquelle il est préférable d'éviter d'utiliser le préprocesseur lorsque cela est possible.

boost::noncopyable est un exemple de mise en œuvre. Il est utilisé comme suit :

class A : noncopyable
{
    ...
};

1 votes

+1 Si je comprends bien, vous obtiendrez toujours une erreur du compilateur, alors qu'en les déclarant privés dans la même classe, vous n'obtiendrez qu'une erreur de l'éditeur de liens (car ils ne sont pas implémentés) dans certains cas (amis, utilisation interne).

0 votes

@RobertL, le fait est que vous êtes toujours autorisé à le faire. struct X { NON_COPYABLE(X); void f() { X x, y(x); } }; et n'obtient qu'une erreur au moment de la liaison. Hériter de noncopyable interdira également cela.

2 votes

L'utilisation de boost::noncopyable sur une classe qui hérite d'une autre classe nécessite un héritage multiple et ses pièges associés - y compris la possibilité de gonflement du code de l'objet que @ThreeBit mentionne. Le manuel de style de Google décourage l'héritage multiple, ce qui explique pourquoi il préconise la macro au lieu de noncopyable. (D'un autre côté, Google décourage également l'utilisation des macros, bien que cette macro soit une exception).

9voto

sellibitze Points 13607

Voir Boost.Utility, en particulier boost::noncopyable . Ce n'est pas une macro mais une classe de base avec copie et affectation privées. Elle empêche le compilateur de générer des copies et des affectations implicites dans les classes dérivées.

edit : Désolé, ce n'était pas une réponse à la question originale. Au fait, boost::noncopyable utilise une référence const comme type de retour pour l'opérateur d'affectation. J'avais l'impression que le type de la valeur de retour n'avait pas d'importance puisqu'elle n'est pas censée être utilisée. Néanmoins, le fait de rendre l'opérateur privé n'empêche pas son utilisation à l'intérieur de la classe ou des amis, auquel cas un type de retour inhabituel (comme void, une référence constante, etc.) pourrait entraîner des erreurs de compilation et des bogues supplémentaires.

5voto

sharptooth Points 93379

Il n'y a pas de différence pratique. Les signatures des opérateurs d'affectation diffèrent uniquement pour des raisons de style. Il est courant qu'un opérateur d'affectation renvoie une référence pour permettre le chaînage :

a = b = c ;

mais une version renvoyant void est également légal et fonctionnera très bien dans les cas où le seul but est de déclarer l'opérateur private et donc interdit d'utilisation.

4voto

David Thornley Points 39051

Extrait de la norme, 12.8, clause 9 : "Un opérateur d'affectation de copie déclaré par l'utilisateur X::operator= est une fonction membre non statique et non modèle de la classe X avec exactement un paramètre de type X , X& , const X& , volatile X& ou const volatile X& ." Il ne dit rien sur le type de retour, donc tout type de retour est autorisé.

La clause 10 dit "Si la définition de la classe ne déclare pas explicitement un opérateur d'affectation de copie, un opérateur est déclaré implicitement."

Par conséquent, déclarer tout X::operator=(const X&) (ou tout autre type d'affectation spécifié) est suffisant. Ni le corps ni le type de retour ne sont significatifs si l'opérateur ne sera jamais utilisé.

Il s'agit donc d'une différence de style, l'une des macros faisant ce à quoi nous nous attendons et l'autre sauvant quelques personnages et faisant le travail d'une manière qui risque de surprendre certaines personnes. Je pense que la macro Qt est meilleure d'un point de vue stylistique. Puisqu'il s'agit d'une macro, le programmeur n'a pas à taper quoi que ce soit de plus, et le fait de ne pas surprendre les gens est une bonne chose dans la construction d'un langage.

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