213 votes

Désactiver le constructeur de copie

J'ai une classe :

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'autoriser que des codes comme :

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

1 votes

En fait, est-ce un singleton avec des dispositions pour l'héritage (protégé) ?

0 votes

J'ai un doute dans votre code, est-ce que chaque fois qu'une instance différente sera créée, je pense que GetUniqueInstance() donnera toujours une référence au même objet.

359voto

R. Martinho Fernandes Points 96873

Vous pouvez rendre le constructeur de copie privé et ne pas fournir d'implémentation :

private:
    SymbolIndexer(const SymbolIndexer&);

Ou en C++11, l'interdire explicitement :

SymbolIndexer(const SymbolIndexer&) = delete;

68 votes

En ce qui concerne le delete mot-clé J'aimerais ajouter ce qui suit. Mon habitude actuelle, lors de la conception d'une nouvelle classe, est de delete le constructeur de copie et l'opérateur d'affectation immédiatement. J'ai constaté que, selon le contexte, ils sont le plus souvent inutiles et que leur suppression permet d'éviter certains cas de comportement inattendu. Si une situation se présente où un constructeur de copie peut être nécessaire, déterminez si cela peut être fait avec une sémantique de déplacement. Si cela n'est pas souhaitable, fournissez une implémentation à la fois ( !) du ctor de copie et de l'opérateur d'affectation. Je laisse au lecteur le soin de décider si c'est une bonne approche.

1 votes

@pauluss86 J'aime votre approche mais je ne m'y engagerais pas complètement car je pense que le temps passé à suivre ce schéma est plus important que le temps gagné par les erreurs qu'il évite. J'interdis simplement la copie lorsque je ne suis pas sûr.

0 votes

@pauluss86 C'est en gros ce que fait Rust : Move-by-default (et const-by-default). Très utile à mon avis.

50voto

firegurafiku Points 81

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 très sûr que cela fonctionne dans tous les compilateurs, cependant. Il existe un Question connexe mais sans réponse pour l'instant.

UPD :

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

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

El delete Le mot-clé empêche les membres d'être construits par défaut, ils ne peuvent donc plus être utilisés dans les membres construits par défaut d'une classe dérivée. Essayer d'assigner 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 juste pour le même but, je suppose qu'elle est même implémentée de manière similaire. Cette classe s'appelle boost::noncopyable et est destiné à être utilisé comme dans l'exemple suivant :

#include <boost/core/noncopyable.hpp>

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

Je recommande de s'en tenir à la solution de Boost si la politique de votre projet le permet. Voir aussi un autre boost::noncopyable -Question connexe pour plus d'informations.

0 votes

Cela ne devrait-il pas être NonAssignable(const NonAssignable &other); ?

0 votes

Je pense que cette question obtiendrait beaucoup plus de votes positifs si elle était mise à jour pour C++11. delete syntaxe du mot-clé.

0 votes

@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 delete ils ne fonctionnent plus (je viens de vérifier).

4voto

Aaron Klotz Points 4346

Faire SymbolIndexer( const SymbolIndexer& ) privé. Si vous assignez à une référence, vous ne copiez pas.

-1voto

Grwww Points 11

Les développeurs C++ qui apprennent la différence entre référence et pointeur peuvent être tentés d'avoir

class MyClass {
    SomeClass acc;
public:
    MyClass() : acc() {}
    SomeClass &GetAccess( return acc; );
};

Comme un moyen pratique de le faire ensuite :

someblock->{
    MyClass inst;
    auto acc = inst.GetAccess();  

    acc.SomeFunction();
}

ce qui créera des problèmes avec les doubles libérations à l'intérieur de SomeClass, parce que vous vous retrouvez avec une copie par défaut...

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