Désactiver le constructeur de copie

173

J'ai un cours :

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

public:
  static inline SymbolIndexer & GetUniqueInstance ( ) 
  { 
    static SymbolIndexer uniqueinstance_ ;
    return uniqueinstance_ ; 
  }
};

Comment dois-je le modifier pour désactiver le code comme:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

et n'autorisez que le code comme:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );
Humble débogueur
la source
1
Btw, est-ce un singleton avec des dispositions pour l'héritage (donné protégé)?
R. Martinho Fernandes
J'ai un doute dans votre code à chaque fois qu'une instance différente sera créée. Je pense que GetUniqueInstance () donnera toujours référence au même objet.
Pratham Shah le

Réponses:

286

Vous pouvez rendre le constructeur de copie privé et ne fournir aucune implémentation:

private:
    SymbolIndexer(const SymbolIndexer&);

Ou en C ++ 11, interdisez-le explicitement:

SymbolIndexer(const SymbolIndexer&) = delete;
R. Martinho Fernandes
la source
43
En ce qui concerne le deletemot clé, j'aimerais ajouter ce qui suit. Mon habitude actuelle lors de la conception d'une nouvelle classe est de deletecopier immédiatement le constructeur et l'opérateur d'affectation. J'ai constaté que, selon le contexte, ils sont pour la plupart inutiles et leur suppression évite certains cas de comportement inattendu. Si une situation se produit où un cteur de copie peut être nécessaire, déterminez si cela peut être fait avec la sémantique de déplacement. Si cela n'est pas souhaitable, fournissez une implémentation pour (!) Le cteur de copie et l'opérateur d'affectation. Je vais laisser au lecteur le soin de savoir si c'est une bonne approche.
pauluss86
1
@ pauluss86 J'aime votre approche mais je ne m'y engage pas totalement car je pense que le temps passé à suivre ce modèle est supérieur au temps économisé par les erreurs qu'il évite. J'interdis simplement la copie en cas de doute.
Tomáš Zato - Réintégrer Monica le
@ pauluss86 C'est essentiellement ce que fait Rust: Déplacer par défaut (et const-by-default). Très utile à mon avis.
Kapichu
33

Si l'héritage multiple ne vous dérange pas (ce n'est pas si mal, après tout), vous pouvez écrire une classe simple avec un constructeur de copie privée et un opérateur d'affectation et la sous-classer en plus:

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

class SymbolIndexer: public Indexer, public NonAssignable {
};

Pour GCC, cela donne le message d'erreur suivant:

test.h: In copy constructor ‘SymbolIndexer::SymbolIndexer(const SymbolIndexer&)’:
test.h: error: ‘NonAssignable::NonAssignable(const NonAssignable&)’ is private

Je ne suis pas sûr que cela fonctionne dans tous les compilateurs. Il y a une question connexe , mais sans réponse pour le moment.

UPD:

En C ++ 11, vous pouvez également écrire la NonAssignableclasse comme suit:

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

Le deletemot-clé empêche les membres d'être construits par défaut, ils ne peuvent donc pas être utilisés davantage dans les membres construits par défaut d'une classe dérivée. Essayer d'attribuer donne l'erreur suivante dans GCC:

test.cpp: error: use of deleted function
          ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
test.cpp: note: ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
          is implicitly deleted because the default definition would
          be ill-formed:

UPD:

Boost a déjà une classe dans le même but, je suppose qu'elle est même implémentée de la même manière. La classe est appelée boost::noncopyableet est destinée à être utilisée comme suit:

#include <boost/core/noncopyable.hpp>

class SymbolIndexer: public Indexer, private boost::noncopyable {
};

Je vous recommande de vous en tenir à la solution Boost si la politique de votre projet le permet. Voir également une autre boost::noncopyablequestion connexe pour plus d'informations.

firegurafiku
la source
Cela ne devrait-il pas être NonAssignable(const NonAssignable &other);?
Troyseph
Je pense que cette question obtiendrait beaucoup plus de votes positifs si elle était mise à jour avec la deletesyntaxe des mots clés C ++ 11 .
Tomáš Zato - Réintégrer Monica le
@ TomášZato: L'idée est de garder le constructeur de copie et l'opérateur d'affectation présents, mais privés. Si vous deleteeux, ça arrête de fonctionner (je viens de vérifier).
firegurafiku
@ TomášZato: Ah, désolé, ma méthode de test était un peu fausse. La suppression fonctionne aussi. Mettra à jour la réponse dans une minute.
firegurafiku le
3
@Troyseph: const Class&et Class const&sont à peu près les mêmes. Pour les pointeurs, vous pouvez même Class const * consttaper.
firegurafiku
4

Rendre SymbolIndexer( const SymbolIndexer& )privé. Si vous attribuez une référence, vous ne copiez pas.

Aaron Klotz
la source