Quels sont les avantages de boost::noncopyable
pour éviter de copier une classe, vous pouvez très facilement déclarer un constructeur de copie privée / opérateurs de cession. Mais vous pouvez aussi hériter de boost::noncopyable
.
Quels sont les avantages / inconvénients de l'utilisation de boost dans ce cas?
11 réponses
je ne vois pas de documentation avantage:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
};
vs:
struct A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
};
lorsque vous ajoutez des types de déménagement seulement, je vois même la documentation comme trompeuse. Les deux exemples suivants ne sont pas reproductibles, bien qu'ils soient mobiles:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
A(A&&) = default;
A& operator=(A&&) = default;
};
vs:
struct A
{
A(A&&) = default;
A& operator=(A&&) = default;
};
sous héritage multiple, il peut même y avoir une pénalité d'espace:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
};
struct D
: public B,
public C,
private boost::noncopyable
{
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
pour moi cette gravure sortie:
3
mais ceci, que je crois avoir une documentation supérieure:
struct A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
C(const C&) = delete;
C& operator=(const C&) = delete;
};
struct D
: public B,
public C
{
D(const D&) = delete;
D& operator=(const D&) = delete;
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
sorties:
2
je trouve beaucoup plus facile de déclarer mes opérations de copie que de raisonner si oui ou non je dérive de boost::non_copyable
plusieurs fois et si cela va me coûter. Surtout si je ne suis pas l'auteur complète de la hiérarchie d'héritage.
il rend l'intention explicite et claire , sinon il faut voir la définition de la classe,et rechercher la déclaration relative à la copie-sémantique, et puis chercher le spécificateur d'accès dans lequel il est déclaré , afin de déterminer si la classe est non-copiable ou non. Une autre façon de le découvrir en écrivant du code qui nécessite une copie-sémantique activée et voir l'erreur de compilation.
résumant ce que d'autres ont dit:
avantages de boost::noncopyable
sur les méthodes de copie privée :
- Il est plus explicite et descriptif de l'intention. Utiliser des fonctions de copie privée est un idiome qui prend plus de temps à repérer que
noncopyable
. - c'est moins de code / moins de Dactylographie / moins d'encombrement / moins de place pour l'erreur (le plus facile serait accidentellement fournir une implémentation).
- il intègre le sens droit dans les métadonnées du type, similaire à un attribut C#. Vous pouvez maintenant écrire une fonction qui n'accepte que les objets qui ne peuvent pas être copiés.
- il peut détecter des erreurs plus tôt dans le processus de construction. L'erreur sera présentée au moment de la compilation plutôt qu'au moment du lien, dans le cas où la classe elle-même ou des amis de la classe font la copie erronée.
- (presque le même que #4) empêche la classe SE ou des amis de la classe d'appeler les méthodes de copie privée.
avantages des méthodes de copie privée par rapport à boost::noncopyable
:
- Pas de boost de dépendance
- l'intention de boost::noncopyable est plus claire.
- Boost:: noncopyable empêche les méthodes de classes d'utiliser accidentellement le constructeur de copie privé.
- moins de code avec boost:: non-copiable.
Je ne comprends pas pourquoi personne d'autre ne semble le mentionner, mais:
avec noncopyable
vous n'écrivez le nom de votre classe qu'une seule fois.
Without, five duplication : Un Un de "classe A", deux pour désactiver l'affectation, et deux pour désactiver le constructeur de copie.
citant la documentation:
" la façon traditionnelle de traiter ces cas est de déclarer un constructeur de copie privée et une cession de copie, puis de documenter pourquoi cela est fait. Mais dériver de non-copyable est plus simple et plus clair, et ne nécessite pas de documentation supplémentaire."
http://www.boost.org/libs/utility/utility.htm#Class_noncopyable
un avantage concret (au-delà de l'expression de votre intention un peu plus clairement) est que l'erreur sera détectée plus tôt, au stade de la compilation et non au stade du lien, si une fonction membre ou ami essaie de copier un objet. Le constructeur/affectation de la classe de base n'est accessible nulle part, ce qui donne une erreur de compilation.
il vous empêche également de définir accidentellement les fonctions (i.e. taper {}
au lieu de ;
), une petite erreur qui pourrait bien aller sans qu'on s'en aperçoive, mais qui permettrait alors aux membres et aux amis de faire des copies invalides de l'objet.
l'avantage est que vous n'avez pas à écrire un constructeur de copie privée et un opérateur de copie privée vous-même et il exprime clairement votre intention sans écrire de documentation supplémentaire.
A small désavantage (spécifique à GCC) est que, si vous compilez votre programme avec g++ -Weffc++
et que vous avez des classes contenant des pointeurs, par exemple
class C : boost::noncopyable
{
public:
C() : p(nullptr) {}
private:
int *p;
};
GCC ne comprend pas ce qui se passe:
avertissement: "classe C" est le pointeur de données des membres de la [-Weffc++]
attention: mais ne remplace pas 'C (const S&)' [- Weffc++]
avertissement: ou 'operator=(const C&)' [- Weffc++]
bien qu'il ne se plaint pas:
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete
class C
{
public:
C() : p(nullptr) {}
DISALLOW_COPY_AND_ASSIGN(C);
private:
int *p;
};
PS I know GCC-Weffc++ has several issues. Le code qui vérifie les "problèmes" est assez simple, de toute façon... parfois, ça aide.
je préfère utiliser boost::Non-copyable que supprimer ou privatiser manuellement le constructeur de copie et l'opérateur de tâche.
cependant, je n'utilise presque jamais soit méthode, parce que:
si je fais un objet non-copiable, il doit y avoir une raison pour qu'il ne soit pas copiable. Cette raison, 99% du temps, est parce que j'ai des membres qui ne peuvent pas être copiés de manière significative. Il y a des Chances, de tels membres seraient également mieux adaptés en tant que privé détails de mise en œuvre. Donc je fais la plupart de ces classes comme ceci:
struct Whatever {
Whatever();
~Whatever();
private:
struct Detail;
std::unique_ptr<Detail> detail;
};
donc maintenant, j'ai une structure d'implémentation privée, et depuis que j'ai utilisé std: unique_ptr, ma classe de premier niveau est non-copiable gratuitement. Les erreurs de lien qui viennent de cela sont compréhensibles parce qu'elles parlent de comment vous ne pouvez pas copier un std::unique_ptr. Pour moi, ce sont tous les avantages de boost::Non-copyable et une implémentation privée réunis en un seul.
l'avantage avec Ce modèle est plus tard, si je décide que je voulais effectivement rendre mes objets de cette classe copiable, je peux juste ajouter et implémenter un constructeur de copie et/ou un opérateur de tâche sans changer la hiérarchie de classe.
La disavantage, selon Scott Meyers, le nom est "non-natrual", si vous avez besoin de trouver une disavantage.