Desativar construtor de cópias

173

Eu tenho uma aula:

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

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

Como devo modificá-lo para desativar o código, como:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

e permita apenas códigos como:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );
Depurador humilde
fonte
1
Btw, este é um singleton com provisões para herança (dada protegida)?
R. Martinho Fernandes
Eu tenho uma dúvida no seu código sempre que uma instância diferente for criada, acho que GetUniqueInstance () sempre dará referência ao mesmo objeto.
Pratham Shah 03/04

Respostas:

286

Você pode tornar o construtor de cópias privado e não fornecer implementação:

private:
    SymbolIndexer(const SymbolIndexer&);

Ou no C ++ 11, proíba-o explicitamente:

SymbolIndexer(const SymbolIndexer&) = delete;
R. Martinho Fernandes
fonte
43
Em relação à deletepalavra - chave, gostaria de adicionar o seguinte. Meu hábito de hábito atual ao projetar uma nova classe é deleteimediatamente ao construtor de cópias e ao operador de atribuição. Descobri que, dependendo do contexto, eles são na maior parte desnecessários e a exclusão deles evita alguns casos de comportamento inesperado. Se ocorrer uma situação em que um copiador possa ser necessário, determine se isso pode ser feito com a semântica de movimentação. Se isso for indesejável, forneça uma implementação para (!) O copiador de cópias e o operador de atribuição. Se essa é uma boa abordagem, deixarei para o leitor.
pauluss86
1
@ pauluss86 Gosto da sua abordagem, mas não me comprometo totalmente, pois acho que o tempo gasto seguindo esse padrão é maior que o tempo economizado pelos erros que impede. Eu simplesmente proíbo copiar sempre que não tiver certeza.
Tomáš Zato - Restabelece Monica
@ pauluss86 Isso é basicamente o que o Rust faz: Mover por padrão (e const-por-padrão). Muito útil na minha opinião.
Kapichu 31/12/16
33

Se você não se importa com a herança múltipla (afinal, não é tão ruim assim), você pode escrever uma classe simples com o construtor de cópia privada e o operador de atribuição e subclassificá-la adicionalmente:

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

class SymbolIndexer: public Indexer, public NonAssignable {
};

Para o GCC, isso fornece a seguinte mensagem de erro:

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

Não tenho muita certeza de que isso funcione em todos os compiladores. Existe uma pergunta relacionada , mas sem resposta ainda.

UPD:

No C ++ 11, você também pode escrever a NonAssignableclasse da seguinte maneira:

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

A deletepalavra-chave impede que os membros sejam construídos por padrão, portanto, eles não podem ser usados ​​mais nos membros construídos por padrão de uma classe derivada. Tentar atribuir gera o seguinte erro no 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:

O Boost já tem uma classe para o mesmo propósito, acho que é implementada de maneira semelhante. A classe é chamada boost::noncopyablee deve ser usada como a seguir:

#include <boost/core/noncopyable.hpp>

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

Eu recomendaria seguir a solução do Boost se a política do seu projeto permitir. Consulte também outra boost::noncopyablepergunta relacionada para obter mais informações.

firegurafiku
fonte
Isso não deveria ser NonAssignable(const NonAssignable &other);?
Troyseph
Eu acho que essa pergunta seria muito mais positiva se fosse atualizada para a deletesintaxe da palavra-chave C ++ 11 .
Tomáš Zato - Restabelece Monica
@ TomášZato: A idéia é manter o construtor de cópias e o operador de atribuição presentes, mas privados. Se você deleteeles, ele pára de funcionar (acabei de verificar).
firegurafiku
@ TomášZato: Ah, desculpe, meu método de teste estava um pouco errado. A exclusão também funciona. Atualizará a resposta em um minuto.
firegurafiku
3
@ Troseph: const Class&e Class const&são praticamente os mesmos. Para ponteiros, você pode ter um Class const * consttipo par .
firegurafiku
4

Tornar SymbolIndexer( const SymbolIndexer& )privado. Se você está atribuindo a uma referência, não está copiando.

Aaron Klotz
fonte