Quaisquer gotchas em cópia ctor e operador de atribuição tendo uma semântica ligeiramente diferente?
-
18-09-2019 - |
Pergunta
Por favor, olhe o seguinte código e me diga se ele vai causar problemas no futuro, e se sim, como evitá-los.
class Note
{
int id;
std::string text;
public:
// ... some ctors here...
Note(const Note& other) : id(other.id), text(other.text) {}
void operator=(const Note& other) // returns void: no chaining wanted
{
if (&other == this) return;
text = other.text;
// NB: id stays the same!
}
...
};
Em suma, eu quero que o construtor de cópia para criar uma cópia exata de um objeto, incluindo a sua (banco de dados) campo ID. Por outro lado, quando eu ceder, eu quero apenas para copiar os campos de dados. Mas eu tenho algumas preocupações, já que geralmente o ctor cópia eo operador = têm mesma semântica.
O campo ID é usado apenas por nota e seus amigos. Para todos os outros clientes, o operador de atribuição cria uma cópia exata. O caso de uso: Quando eu quero editar uma nota, eu criar uma cópia usando a cópia ctor, editá-lo e, em seguida, chamar save na classe Notebook que administra Notas:
Note n(notebook.getNote(id));
n = editNote(n); // pass by const ref (for the case edit is canceled)
notebook.saveNote(n);
Quando, por outro lado eu quero criar uma nova nota com o mesmo conteúdo que uma nota existente, eu posso fazer isso:
Note n;
n = notebook.getNote(id);
n.setText("This is a copy");
notebook.addNote(n);
É esta abordagem plausível? Se não, por favor apontar o que as possíveis consequências negativas são! Muito obrigado!
Solução
Se você quiser semântica que não correspondem ao que é esperado do operador de atribuição, então não usá-lo. Em vez disso, desativá-lo, declarando uma operator=
privado e definir uma função com um nome que deixa claro o que está acontecendo, como copyDataFields
.
Outras dicas
Embora este trabalho poder para o seu caso específico, eu não recomendo em geral.
Bibliotecas, como a STL esperar que o construtor de cópia e operador de atribuição ao trabalho "como eles deveriam". Se você violar o C ++ semântica, então você pode achar que os recipientes de STL de seus objetos não vai funcionar direito. O STL irá chamar tanto o seu construtor de cópia e seu operador de atribuição, sob circunstâncias diferentes, dependendo do recipiente.
É muito fácil ficar totalmente confuso quando o código não faz o que você acha que ele faz.
Tecnicamente, é factível e tecnicamente vai funcionar, mas eu não faria isso dessa maneira. Os problemas que vejo são:
-
Você muda "natural" semântica do operador de atribuição como é conhecido pela população C ++.
-
Os dois operações individuais, construção e atribuição de cópia, são inconsistentes por causa da diferente semântica.
-
A solução é propenso a erros, porque é fácil para chamar acidentalmente construtor de cópia mesmo que pareça como atribuição. Se um programador escreve o seu segundo caso de uso dessa maneira:
Note n = notebook.getNote(id);
Em seguida, copie construtor é chamado, não atribuição , de modo a obter
n
como objeto diferente do que o esperado.
Por que não fazer as suas intenções apenas clara e explícita:
Note& Notebook::editNote(int id);
Note Notebook::createNote(int id);