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 ( );

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;

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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.