59 votes

Avec les fonctions membres explicitement supprimées dans C ++11, est-il toujours utile d’hériter d’une classe de base non-copiable?

Explicitement membre supprimé des fonctions en C++11, est-il encore la peine d'hériter d'une noncopyable de la classe de base?

Je parle du truc où vous en privé hériter d'une classe de base qui a privé ou supprimé constructeur de copie et de copie d'affectation (par exemple, boost::noncopyable).

Sont les avantages mis en avant dans cette question , toujours en vigueur à C++11?


Je ne comprends pas pourquoi certaines personnes prétendent qu'il est plus facile de faire une classe non-copiable en C++11.

En C++03:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

En C++11:

MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;

EDIT:

Comme beaucoup de gens l'ont souligné, c'était une erreur de fournir des corps vides (c'est à dire {}) pour le privé constructeur de copie et l'opérateur d'assignation de copie, parce que cela permettrait à la classe elle-même invoquer ces opérateurs sans aucune erreur. J'ai d'abord commencé ne pas ajouter de la {}, mais a couru dans certains linker questions que m'a fait ajouter les {} pour certains idiot raison (je n'ai pas oublier les circonstances). Je sais mieux connaître. :-)

75voto

Nicol Bolas Points 133791

Eh bien, c':

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

Encore permet techniquement MyClass à être copiés par les membres et les amis. Bien sûr, ces types et les fonctions sont théoriquement sous votre contrôle, mais la classe est toujours copiable. Au moins avec boost::noncopyable et = delete, personne ne peut copier la classe.


Je ne comprends pas pourquoi certaines personnes prétendent qu'il est plus facile de faire une classe non-copiable en C++11.

Ce n'est pas tellement "facile" comme "plus facile à digérer".

Réfléchissez à ceci:

class MyClass
{
private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}
};

Si vous êtes un programmeur C++ qui a lu un texte d'introduction sur le C++, mais a peu d'exposition à idiomatiques C++ (c'est à dire: beaucoup de programmeurs C++), c'est... déroutant. Il déclare copie des constructeurs et copie des opérateurs d'affectation, mais ils sont vides. Alors pourquoi les déclarer? Oui, ils font private, mais qui ne soulève plus de questions: pourquoi les rendre privés?

Afin de comprendre pourquoi cela empêche la copie, vous devez vous rendre compte qu'en les déclarant privé, vous faites en sorte que les non-membres/amis ne peut pas le copier. Ce n'est pas immédiatement évident pour les novices. Ni est le message d'erreur qu'ils vont obtenir quand ils essayer de le copier.

Maintenant, le comparer avec le C++11 version:

class MyClass
{
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

Que faut-il comprendre que cette classe ne peut pas être copié? Rien de plus que de comprendre ce que l' = delete de la syntaxe de moyens. Un livre expliquant les règles de syntaxe du C++11 vais vous dire exactement ce que cela fait. L'effet de ce code est évident pour les novices C++ de l'utilisateur.

Ce qui est formidable à propos de ce langage est qu'il devient un idiome parce que c'est la plus claire, la plus évidente façon de dire exactement ce que tu veux dire.

Même boost::noncopyable nécessite un peu plus de réflexion. Oui, il est appelé "noncopyable", c'est donc l'auto-documentation. Mais si vous ne l'avez jamais vu avant, il soulève des questions. Pourquoi êtes-vous découlant de quelque chose qui ne peut être copié? Pourquoi mes messages d'erreur de parler d' boost::noncopyables'constructeur de copie? Etc. Encore une fois, la compréhension de l'idiome nécessite plus d'effort mental.

24voto

La première chose est que, comme d'autres avant moi, vous avez l'idiome de mal, vous déclarer comme privé et de ne pas définir:

class noncopyable {
   noncopyable( noncopyable const & );
   noncopyable& operator=( noncopyable const & );
};

Où le type de retour de la operator= peuvent être fondamentalement quoi que ce soit. À ce stade, si vous lisez ce code dans l'en-tête, qu'est-ce que ça veut dire? Il ne peut être copiée par des amis ou il ne peut pas être copiée jamais? Notez que si vous fournissez une définition comme dans votre exemple, il dit que j'ai peut être copié qu'à l'intérieur de la classe et par les amis, c'est le manque d'une définition de ce que cela traduit en je ne peut pas être copié. Mais l'absence d'une définition dans l'en-tête n'est pas synonyme d'un manque de définition de partout, comme il a pu être défini dans le fichier cpp.

C'est là que héritent d'un type appelé noncopyable rend explicite le fait que l'intention est d'éviter les copies, de la même façon que si vous avez manuellement écrit le code ci-dessus, vous devriez document avec un commentaire dans la ligne que l'intention est de désactiver les copies.

C++11 ne change pas de tout cela, c'est tout simplement la documentation explicite dans le code. Dans la même ligne que vous déclarez le constructeur de copie est supprimé, vous êtes documenté que vous voulez complètement désactivée.

Un dernier commentaire, la fonctionnalité de C++11 n'est pas seulement être capable d'écrire noncopyable avec moins de code ou mieux, mais plutôt d'en inhibant le compilateur de générer le code que vous ne voulez pas généré. C'est juste une utilisation de cette fonctionnalité.

10voto

David Stone Points 3822

Outre les points de d'autres en ont fait...

Avoir un constructeur de copie et l'opérateur d'assignation de copie que vous ne définissez pas empêche quiconque d'en faire des copies. Toutefois, si une fonction membre ou un ami de la fonction tente d'en faire une copie, ils recevront un lien erreur. S'ils tentent de le faire lorsque vous avez explicitement supprimé ces fonctions, ils recevront une erreur de compilation.

Je fais toujours mes erreurs se produisent dès que possible. Faire exécuter à temps les erreurs se produisent au moment de l'erreur au lieu de plus tard (donc faire l'erreur de se produire lorsque vous modifiez la variable au lieu de quand vous le lire). Faire toutes les erreurs d'exécution en lien erreurs de sorte que le code n'a jamais une chance de se tromper. Faire tous les lien des erreurs en erreurs de compilation pour accélérer le développement et ont un peu plus de messages d'erreur utiles.

5voto

dauphic Points 7322

C'est plus lisible et permet au compilateur de donner de meilleures erreurs.

Le lecteur 'effacé' est plus clair pour le lecteur, surtout s'il survole la classe. De même, le compilateur peut vous dire que vous essayez de copier un type non copiable, au lieu de vous donner une erreur générique 'essayer d'accéder à un membre privé'.

Mais vraiment, c'est juste une fonctionnalité pratique.

2voto

ThreeBit Points 203

Les gens ici recommandent de déclarer les fonctions des membres sans les définir. J'aimerais souligner qu'une telle approche n'est pas transférable. Certains compilateurs / lieurs exigent que si vous déclarez une fonction membre, vous devez également la définir, même si elle n'est pas utilisée. Si vous utilisez uniquement VC ++, GCC, clang, vous pouvez vous en tirer, mais si vous essayez d'écrire du code réellement portable, certains autres compilateurs (par exemple, Green Hills) échoueront.

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