Pergunta

Por uma razão ou outra, eu sou forçado a fornecer um construtor de cópia e um operador = para minha classe. Eu pensei que eu não precisava operator= se eu definido um ctor cópia, mas QList quer um. Colocando isso de lado, eu odeio a duplicação de código, assim há de errado com qualquer coisa fazê-lo desta maneira?

Fixture::Fixture(const Fixture& f) {
    *this = f;
}

Fixture& Fixture::operator=(const Fixture& f) {
    m_shape         = f.m_shape;
    m_friction      = f.m_friction;
    m_restitution   = f.m_restitution;
    m_density       = f.m_density;
    m_isSensor      = f.m_isSensor;
    return *this;
}

E só por curiosidade, não há nenhuma maneira de mudar isso para que a maior parte do código está na ctor cópia e operator= alguma forma utiliza-lo? Tentei return Fixture(f); mas isso não aconteceu assim.


Parece que eu preciso para torná-lo mais claro que o construtor de cópia e operador de atribuição foram desativados implicitamente pela classe que estou herdando. Por quê? Porque é uma classe base abstrata que não deve ser instanciado por conta própria. Esta classe, no entanto, é significava para ficar sozinho.

Foi útil?

Solução

Isso é ruim, porque o operator= não pode confiar em um objeto set-up mais. Você deve fazê-lo o contrário, e pode usar o idioma copy-swap.

No caso em que você só tem que copiar todos os elementos, você pode usar o operador de atribuição gerado implicitamente.

Em outros casos, você terá que fazer algo além, principalmente liberando e copiando memória. Este é o lugar onde o idioma cópia-swap é bom para. Não só é elegante, mas também fornecem assim uma atribuição não lançar exceções se ele só troca primitivos. Vamos uma classe apontando para um buffer que você precisa copiar:

Fixture::Fixture():m_data(), m_size() { }

Fixture::Fixture(const Fixture& f) {
    m_data = new item[f.size()];
    m_size = f.size();
    std::copy(f.data(), f.data() + f.size(), m_data);
}

Fixture::~Fixture() { delete[] m_data; }

// note: the parameter is already the copy we would
// need to create anyway. 
Fixture& Fixture::operator=(Fixture f) {
    this->swap(f);
    return *this;
}

// efficient swap - exchanging pointers. 
void Fixture::swap(Fixture &f) {
    using std::swap;
    swap(m_data, f.m_data);
    swap(m_size, f.m_size);
}

// keep this in Fixture's namespace. Code doing swap(a, b)
// on two Fixtures will end up calling it. 
void swap(Fixture &a, Fixture &b) {
  a.swap(b);
}

É assim que eu escrever o operador de atribuição normalmente. Leia velocidade quer? Passagem por valor sobre a assinatura operador de atribuição incomum (passagem por valor).

Outras dicas

Copiar ctor e atribuição são inteiramente distintas - atribuição normalmente precisa liberar recursos no objeto que está substituindo, copiar ctor está trabalhando em um objeto ainda-não-inicializado. Desde aqui você aparentemente não têm requisitos especiais (sem "lançando" necessária na atribuição), sua abordagem é muito bem. Mais em geral, você pode ter um "liberar todos os recursos do objeto está segurando" método auxiliar (para ser chamado no dtor e no início da atribuição), bem como a parte "copiar essas outras coisas para o objeto" que é razoavelmente perto para o trabalho de um ctor cópia típica (ou na maior parte, de qualquer maneira; -).

Você está simplesmente fazendo uma cópia membro-wise e atribuição em seus exemplos. Isso não é algo que você precisa para escrever-se. O compilador pode gerar operações de cópia e de atribuição implícitas que fazem exatamente isso. Você só precisa escrever seu próprio construtor de cópia, cessão e / ou destruidor se aqueles gerados pelo compilador não são apropriadas (isto é, no caso de você conseguir algum recurso através de um ponteiro ou algo parecido)

Eu acho que você executar em problemas se o seu operador = cada vez torna-se virtual.

Eu recomendaria escrevendo uma função (talvez estática) que faz a cópia, então, a cópia construtor e operador = chamar essa função em vez disso.

Sim, esta é uma boa prática e deve (quase) sempre ser feito. Além atirar em um construtor destructor e padrão (mesmo se você torná-lo privado).

O livro de James Em Coplien 1991 avançada C ++ , isto é descrito como parte de "Canonical Form ortodoxa". Nela, ele defende um construtor padrão, um construtor de cópia, o operador de atribuição e um destruidor.

Em geral, você deve usar a forma canônica ortodoxa se:

  • Você quer apoiar atribuição de objeto da classe, ou quer passar esses objetos como parâmetros de chamada por valor para uma função, e
  • O objecto contém ponteiros para objectos que são contadas-referência, ou os executa classe destructor um delete sobre um membro de dados do objecto.

Você deve usar a forma canônica ortodoxa para qualquer classe não trivial em um programa, por uma questão de uniformidade em todas as classes e para gerir a crescente complexidade de cada classe ao longo da evolução do programa.

Coplien ofertas páginas de razões para este padrão e eu não podia fazer-lhes justiça aqui. No entanto, um item de chave que já foi abordado é a capacidade de limpar o objeto que está sendo substituído.

Eu acho que você deve inicializar as variáveis ??membro objeto usando initializer list. Se as suas variáveis ??são de primitive-types, então não importa. Caso contrário, a atribuição é diferente da inicialização.


Você poderia fazê-lo com um pequeno truque por inicializar os ponteiros dentro do copy constructor para 0, então você poderia chamar excluir com segurança na assignment operator:

Fixture::Fixture(const Fixture& f) : myptr(0) {
    *this = f;
}
Fixture& Fixture::operator=(const Fixture& f) {
    // if you have a dynamic array for example, delete other wise.
    delete[] myptr;
    myptr = new int[10];
    // initialize your array from the other object here.
    ......
    return *this;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top