349 votes

Rule-of-Three devient Rule-of-Five avec C ++11?

Donc, après avoir vu cette magnifique conférence sur les références rvalue, j'ai pensé que chaque classe devrait bénéficier d'un "constructeur de déplacement", template<class T> MyClass(T&& other) modifier et bien sûr un "opérateur d'assignation de déplacement", template<class T> MyClass& operator=(T&& other) que Philipp souligne dans sa réponse, si elle a alloué dynamiquement les membres, ou plus généralement stocke des pointeurs. Tout comme vous devriez avoir une copie-ctor, opérateur d'affectation et le destructeur si les points mentionnés précédemment s'appliquent. Pensées?

336voto

Philipp Points 21479

Je dirais que la Règle de Trois devient la Règle de Trois, Quatre et Cinq:

Chaque classe doit définir explicitement exactement un de l'ensemble des membres spéciales fonctions:

  • Aucun
  • Destructeur, constructeur de copie, copie opérateur d'affectation

En outre, chaque classe qui définit explicitement d'un destructeur peut définir explicitement un constructeur de déplacement et/ou un opérateur d'assignation de déplacement.

Habituellement, l'un des ensembles suivants de membre spécial fonctions est sensible:

  • Aucun (pour beaucoup de simples classes où le générées implicitement spécial des fonctions membres sont correct et rapide)
  • Destructeur, constructeur de copie, copie opérateur d'affectation (dans ce cas la la classe ne sera pas mobile)
  • Destructeur, constructeur de déplacement, déplacer opérateur d'affectation (dans ce cas, la classe ne sera pas copiable, utile pour les ressources, la gestion de classes où la ressource sous-jacente n'est pas copiable)
  • Destructeur, constructeur de copie, copie opérateur d'affectation, constructeur de déplacement (à cause de la copie élision, il n'y a pas de frais généraux si la copie opérateur d'affectation prend son argument par valeur)
  • Destructeur, constructeur de copie, copie opérateur d'affectation, constructeur de déplacement, opérateur d'assignation de déplacement

Notez que déplacer le constructeur et l'opérateur d'assignation de déplacement ne seront pas générées pour une classe qui déclare explicitement qu'aucune des autres fonctions de membre de, que le constructeur de copie et l'opérateur d'assignation de copie ne sera pas généré pour une classe qui déclare explicitement un constructeur de déplacement ou de déplacer l'opérateur d'assignation, et qu'une classe avec un déclarées explicitement le destructeur et implicitement défini constructeur de copie ou implicitement défini copie opérateur d'affectation est considérée comme obsolète. En particulier, les suivantes parfaitement valide en C++03 polymorphe de la classe de base

class C {
  virtual ~C() { }   // allow subtype polymorphism
};

doit être réécrit comme suit:

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }
};

Un peu ennuyeux, mais sans doute mieux que l'alternative (génération automatique de toutes les fonctions membres).

Contrairement à la Règle des Trois Grands, où à défaut de respecter la règle peut causer de graves dommages, de ne pas déclarer explicitement le constructeur de déplacement et de l'opérateur d'assignation de déplacement est généralement bon, mais souvent sous-optimale à l'égard de l'efficacité. Comme mentionné ci-dessus, constructeur de déplacement et de déplacer des opérateurs d'affectation ne sont générées que si il n'est pas explicitement déclaré constructeur de copie, copie opérateur d'affectation ou destructeur. Ce n'est pas symétrique à la traditionnelle C++03 comportement à l'égard de l'auto-génération de constructeur de copie et l'opérateur d'assignation de copie, mais il est beaucoup plus sûr. Donc, la possibilité de définir déplacer les constructeurs et les opérateurs d'assignation de déplacement est très utile et crée de nouvelles possibilités (purement mobilière classes), mais les classes qui adhèrent à la C++03 Règle des Trois Grands seront toujours beaux.

Pour les ressources, la gestion de classes, vous pouvez définir le constructeur de copie et l'opérateur d'assignation de copie comme supprimé (qui compte comme définition) si la ressource sous-jacente ne peut être copié. Souvent, vous voulez toujours déplacer le constructeur et l'opérateur d'assignation de déplacement. Copier et déplacer des opérateurs d'affectation sera souvent mis en œuvre à l'aide de swap, comme en C++03. Si vous avez un constructeur de déplacement et de déménagement opérateur d'affectation, spécialisée std::swap deviendra sans importance, car le générique std::swap utilise le constructeur de déplacement et de l'opérateur d'assignation de déplacement si disponible, et qui devrait être assez rapide.

Les Classes qui ne sont pas destinés à la gestion des ressources (c'est à dire, pas de non-vide destructeur) ou sous-type de polymorphisme (c'est à dire, pas de destructeur virtuel) doit déclarer aucun des cinq fonctions de membre; ils vont tous être auto-généré et comportement correct et rapide.

73voto

NoSenseEtAl Points 2342

ne peux pas croire que personne n'lié à ceci:
http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html Fondamentalement, l'article plaide pour "la Règle du Zéro". Il n'est pas approprié pour moi de citer tout l'article mais je crois que c'est le point principal: (Règle de Zéro)

Les Classes qui ont des destructeurs, copier/déplacer les constructeurs ou les copier/déplacer l'affectation >les opérateurs devraient traiter exclusivement de la propriété. Les autres classes ne devraient pas avoir personnalisé les destructeurs, copier/déplacer les constructeurs ou les copier/déplacer des opérateurs d'affectation.

Aussi ce bit est à mon humble avis important:

Commune de la "propriété-dans-un-paquet" classes sont incluses dans la norme bibliothèque: std::unique_ptr et std::shared_ptr. Grâce à l'utilisation de personnalisé deleter les objets, les deux ont été suffisamment flexible pour gérer pratiquement n'importe quel type de ressource.

21voto

Motti Points 32921

Je ne pense pas, la règle de trois est une règle de base que les états qu'une classe qui implémente l'un des suivants, mais pas tous est probablement buggy.

  1. Constructeur de copie
  2. Opérateur d'affectation
  3. Destructeur

Toutefois quitter le déplacer, le constructeur ou l'opérateur d'assignation de déplacement n'implique pas un bug. Il peut être une occasion manquée d'optimisation (dans la plupart des cas) ou que la sémantique de déplacement ne sont pas pertinentes pour cette classe, mais ce n'est pas un bug.

Si c'est peut-être la meilleure pratique pour définir un constructeur de déplacement le cas échéant, il n'est pas obligatoire. Il existe de nombreux cas dans lesquels un constructeur de déplacement n'est pas approprié pour une classe (par exemple, std::complex) et de toutes les classes qui se comportent correctement en C++03 continuera à se comporter correctement en C++0x, même si elles ne définissent pas un constructeur de déplacement.

14voto

peoro Points 12875

Oui, je pense qu'il serait bien de fournir un constructeur de déplacement pour ces classes, mais n'oubliez pas que:

  • C'est seulement une optimisation.

    La mise en œuvre, une ou deux seulement le constructeur de copie, opérateur d'affectation ou destructeur ne sera probablement conduire à des bugs, tout en n'ayant pas un constructeur de déplacement sera juste potentiellement réduire les performances.

  • Constructeur de déplacement ne peuvent pas toujours être appliquées sans modification.

    Certaines classes ont toujours leurs pointeurs alloués, et donc des classes toujours supprimer leurs pointeurs dans le destructeur. Dans ces cas, vous aurez besoin d'ajouter des contrôles supplémentaires-à-dire si leurs pointeurs sont alloués ou ont été déplacés loin (sont maintenant null).

4voto

sellibitze Points 13607

En gros, c'est comme ça: Si vous n'avez pas déclarer toutes les opérations de déplacement, il faut respecter la règle de trois. Si vous déclarez une opération de déplacement, il n'y a pas de mal à "violer" la règle de trois que la génération d'généré par le compilateur opérations a eu de très restrictive. Même si vous n'avez pas à déclarer les opérations de déplacement et de violer la règle de trois, un compilateur C++0x est prévu pour vous donner un avertissement dans le cas d'une fonction spéciale a été de l'utilisateur-déclarés et d'autres fonctions spéciales ont été auto-généré en raison d'une désormais obsolète "C++03 compatibilité de la règle".

Je pense qu'il est sûr de dire que cette règle devient un peu moins importante. Le véritable problème en C++03, c'est que la mise en œuvre de la sémantique de copie requis de l'utilisateur de déclarer toutes liées à des fonctions spéciales, de sorte qu'aucun d'eux n'est généré par le compilateur (ce qui serait autrement faire la mauvaise chose). Mais le C++0x change les règles spéciales de fonction de membre de la génération. Si l'utilisateur déclare juste une de ces fonctions pour modifier la sémantique de copie, il va empêcher le compilateur de l'auto-générer le reste des fonctions spéciales. C'est bien parce que l'absence de déclaration s'avère une erreur d'exécution dans une erreur de compilation maintenant (ou au moins un avertissement). En tant que C++03 compatibilité de mesurer certaines opérations sont toujours générés, mais cette génération est réputée abandonnée et doit au moins produire un avertissement dans C++0x mode.

En raison de la relativement restrictif des règles sur la générées par le compilateur fonctions spéciales et le C++03 compatibilité, la règle de trois séjours à la règle de trois.

Voici quelques exaples qui doit être fine avec le plus récent de C++0x règles:

template<class T>
class unique_ptr
{
   T* ptr;
public:
   explicit unique_ptr(T* p=0) : ptr(p) {}
   ~unique_ptr();
   unique_ptr(unique_ptr&&);
   unique_ptr& operator=(unique_ptr&&);
};

Dans l'exemple ci-dessus, il n'est pas nécessaire de déclarer toutes les autres fonctions spéciales comme supprimé. Ils ne pourront tout simplement pas être généré en raison d'une réglementation restrictive. La présence d'un utilisateur déclaré les opérations de déplacement désactive généré par le compilateur les opérations de copie. Mais dans un cas comme celui-ci:

template<class T>
class scoped_ptr
{
   T* ptr;
public:
   explicit scoped_ptr(T* p=0) : ptr(p) {}
   ~scoped_ptr();
};

un compilateur C++0x est maintenant prévu pour produire un avertissement concernant éventuellement généré par le compilateur copie des opérations susceptibles de faire la mauvaise chose. Ici, la règle des trois questions et doit être respecté. Un avertissement dans ce cas est totalement approprié et donne à l'utilisateur la possibilité de gérer le bug. Nous pouvons nous débarrasser de la question par le biais d'supprimé fonctions:

template<class T>
class scoped_ptr
{
   T* ptr;
public:
   explicit scoped_ptr(T* p=0) : ptr(p) {}
   ~scoped_ptr();
   scoped_ptr(scoped_ptr const&) = delete;
   scoped_ptr& operator=(scoped_ptr const&) = delete;
};

Donc, la règle de trois s'applique encore ici, tout simplement parce que le C++03 compatibilité.

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