# Cópia, movimento e propriedade

Cópia superficial e profunda, atribuição, autoatribuição, movimento e regras de três, cinco e zero.

Página: https://resumos.rgo.pt/cadeiras/p/copia-propriedade/

Copiar o relógio duplica o seu inteiro. As duas horas evoluem depois separadamente.

Se o membro fosse um apontador dono, a cópia gerada duplicaria apenas o endereço. A classe precisa de decidir se uma cópia terá outro recurso, partilhará o original ou será proibida.

## Construção de cópia e atribuição

Em `T b = a;`, `b` ainda não existe e é construído a partir de `a`. Em `b = a;`, `b` já existe e recebe outro estado. A construção de cópia usa habitualmente `T(const T&)`; a atribuição de cópia usa `T& operator=(const T&)`.

A versão gerada pelo compilador copia bases e membros. Se o membro é um `int`, copia o valor. Se é um `std::vector`, o vetor trata a cópia dos seus elementos. Se é um apontador cru, copia o endereço.

## Cópia superficial e profunda

Considera este excerto incorreto como classe dona de memória. Não o executes.

```
class DonoIncompleto {
public:
    DonoIncompleto() : dados_(new int(5)) {}
    ~DonoIncompleto() { delete dados_; }
private:
    int* dados_;
};
```

A cópia implícita de um `DonoIncompleto` deixa os dois objetos com o mesmo endereço. Os dois destrutores tentam depois libertar o mesmo inteiro; a propriedade fica duplicada sem uma regra válida de destruição.

Uma **cópia superficial** copia a representação, incluindo endereços. Uma **cópia profunda** cria recursos independentes e copia o seu conteúdo. Partilhar um recurso também pode ser correto, mas precisa de uma política explícita, como a de `shared_ptr`.

![Cópia superficial de DonoIncompleto: a e b apontam para o mesmo inteiro 5. Os dois destrutores tentariam libertá-lo.](https://resumos.rgo.pt/cadeiras/p/copia-propriedade/figura-1.svg)

Para um buffer com os valores `4` e `6`, a cópia profunda cria outro array, por isso depois de `b.at(0) = 9` cada objeto tem o seu recurso:

![Após cópia profunda e alteração de b, a possui os valores 4 e 6, e b possui outro recurso com 9 e 6.](https://resumos.rgo.pt/cadeiras/p/copia-propriedade/figura-2.svg)

## Construção de uma cópia independente

O exemplo usa `unique_ptr<int[]>` para garantir a libertação, mas implementa a cópia para dar ao `Buffer` semântica de valor.

O invariante é que existem `tamanho_` inteiros acessíveis; quando o tamanho é zero, não existe elemento válido. `at` verifica o índice.

Começa pela construção de cópia; a atribuição e o movimento vêm a seguir:

```
// Excerto: a cópia reserva e copia, nunca partilha o endereço.
Buffer(const Buffer& outro) : Buffer(outro.tamanho_) {
    for (std::size_t i = 0; i < tamanho_; ++i) dados_[i] = outro.dados_[i];
}
```

Com `Buffer a(2)` contendo `4` e `6`, `Buffer b = a` reserva dois inteiros novos e copia cada posição. Alterar `b.at(0)` para `9` não afeta `a`. A delegação `Buffer(outro.tamanho_)` constrói primeiro um destino válido; se a reserva falhar, a exceção sai antes de existir qualquer cópia parcial.

## Um buffer com propriedade exclusiva

O programa completo seguinte reúne cópia, atribuição e movimento. Na atribuição, o destino já tem um recurso: no exemplo abaixo, `c` contém `2`, `3` e `5` e recebe `b` com dois inteiros. A técnica é preparar primeiro a cópia completa num objeto temporário e só depois trocar com o destino, para que o destino continue intacto se a reserva falhar. A tabela depois da listagem mostra os três momentos: preparar, trocar e destruir o antigo.

Programa completo para compilar localmente:

```
#include <cstddef>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <utility>

class Buffer {
public:
    explicit Buffer(std::size_t n)
        : tamanho_(n), dados_(n == 0 ? nullptr : std::make_unique<int[]>(n)) {}

    Buffer(const Buffer& outro) : Buffer(outro.tamanho_) {
        for (std::size_t i = 0; i < tamanho_; ++i) dados_[i] = outro.dados_[i];
    }

    Buffer& operator=(const Buffer& outro) {
        if (this == &outro) return *this;
        Buffer copia(outro);
        trocar(copia);
        return *this;
    }

    Buffer(Buffer&& outro) noexcept
        : tamanho_(std::exchange(outro.tamanho_, 0)),
          dados_(std::move(outro.dados_)) {}

    Buffer& operator=(Buffer&& outro) noexcept {
        if (this == &outro) return *this;
        dados_ = std::move(outro.dados_);
        tamanho_ = std::exchange(outro.tamanho_, 0);
        return *this;
    }

    ~Buffer() = default;
    std::size_t size() const { return tamanho_; }
    int& at(std::size_t i) {
        if (i >= tamanho_) throw std::out_of_range("Indice invalido");
        return dados_[i];
    }
    const int& at(std::size_t i) const {
        if (i >= tamanho_) throw std::out_of_range("Indice invalido");
        return dados_[i];
    }
private:
    void trocar(Buffer& outro) noexcept {
        std::swap(tamanho_, outro.tamanho_);
        dados_.swap(outro.dados_);
    }
    std::size_t tamanho_;
    std::unique_ptr<int[]> dados_;
};

int main() {
    Buffer a(2);
    a.at(0) = 4;
    a.at(1) = 6;
    Buffer b = a;
    b.at(0) = 9;
    Buffer c(3);
    c.at(0) = 2;
    c.at(1) = 3;
    c.at(2) = 5;
    c = b;
    const Buffer& alias = c;
    c = alias;
    Buffer d = std::move(b);
    std::cout << a.at(0) << " " << c.at(0) << " " << d.at(0) << "\n";
    std::cout << b.size() << " " << d.size() << "\n";
}
```

Saída:

```
4 9 9
0 2
```

`std::make_unique<int[]>(n)` reserva um array de `n` inteiros inicializados a zero. A expressão `n == 0 ? nullptr : ...` escolhe não reservar quando o tamanho é zero. `std::exchange(outro.tamanho_, 0)` devolve o tamanho antigo e coloca zero na origem.

`Buffer b = a` reserva dois inteiros novos e copia `4` e `6`, por isso alterar `b.at(0)` para `9` não afeta `a`. `c = b` substitui os três inteiros de um destino que já existia pelos dois inteiros `9` e `6`.

| Momento | Recurso de `c` | Recurso de `copia` |
| --- | --- | --- |
| Antes da atribuição | `2, 3, 5` | ainda não existe |
| Preparar `copia(b)` | `2, 3, 5`, intacto | novo array com `9, 6` |
| `trocar(copia)` | `9, 6` | antigo array com `2, 3, 5` |
| Fim da atribuição | `9, 6` | destruído com o antigo array |

A autoatribuição através de `alias` mantém `c` intacto. `Buffer d = std::move(b)` transfere o endereço sem copiar inteiros; `b` fica com tamanho zero.

## Atribuir sem perder o recurso anterior

A técnica **copy-and-swap** prepara o novo recurso antes de perder o anterior e dá garantia forte: a atribuição constrói primeiro `copia`, por isso se a reserva falhar a exceção sai antes de tocares no destino; depois, `trocar` troca tamanho e propriedade sem lançar.

A alternativa “libertar o destino e só depois copiar” perde o estado anterior se a reserva falhar. Também falha na autoatribuição se destrói o recurso que ainda pretende ler. Testar `this == &outro` resolve a autoatribuição, mas não resolve sozinho a segurança perante falhas de reserva.

`operator=` devolve `*this` por referência, permitindo expressões como `a = b = c`. A atribuição não deve devolver uma referência ao temporário de cópia.

## Movimento não é uma cópia profunda

`std::move` permite tratar uma expressão como candidata a movimento. Não transfere nada sozinho. A operação selecionada, aqui o construtor `Buffer(Buffer&&)`, é que transfere a propriedade.

O movimento de `b` para `d` transfere o endereço sem copiar os inteiros e põe o tamanho de `b` a zero. Por isso, `b` fica vazio e pode ser destruído ou receber nova atribuição.

O estado de um objeto movido depende do contrato do tipo. Não assumes que qualquer `std::string` ou `std::vector` movido fica vazio. A biblioteca garante um estado válido, mas não especificado, salvo garantias adicionais do tipo.

Podes destruí-lo, atribuir-lhe outro valor e usar operações cujas pré-condições satisfaças. Por exemplo, `v.empty()` e `v.size()` são consultas válidas depois de mover um vetor. `v.front()` exige confirmar que não está vazio.

Num movimento entre dois objetos distintos, o `Buffer` desta página deixa a origem com tamanho zero. A autoatribuição por movimento é detetada por `this == &outro` e conserva o objeto. `noexcept` indica que as operações de movimento apresentadas não lançam, o que também ajuda coleções a escolher movimento durante uma realocação.

## Regras de três, cinco e zero

A **regra de três** manda rever destrutor, construtor de cópia e atribuição de cópia quando a gestão manual de um recurso exige uma destas operações. Em C++ com movimento, a **regra de cinco** acrescenta construtor e atribuição de movimento. Não significa que todos os tipos devam implementar cinco funções; uma operação pode ser gerada, definida ou proibida com `= delete`, consoante o contrato.

A **regra de zero** é preferir membros que já gerem corretamente os recursos. A classe seguinte não precisa de nenhuma operação especial escrita à mão.

**main.cpp**

```cpp
#include <cstddef>
#include <iostream>
#include <vector>

class Leituras {
public:
    void adicionar(int valor) { valores_.push_back(valor); }
    int primeiro() const { return valores_.at(0); }
    std::size_t tamanho() const { return valores_.size(); }
private:
    std::vector<int> valores_;
};

int main() {
    Leituras a;
    a.adicionar(4);
    Leituras b = a;
    b.adicionar(8);
    std::cout << a.primeiro() << " " << b.primeiro() << "\n";
    std::cout << a.tamanho() << " " << b.tamanho() << "\n";
}
```

Saída:

```
4 4
1 2
```

Cada `Leituras` possui o seu vetor. Acrescentar `8` à cópia deixa o original com um elemento e a cópia com dois. O compilador compõe as operações corretas a partir das do membro. O exemplo manual anterior ensina a mecânica; uma coleção habitual de inteiros deve usar `vector` para evitar reproduzir essa gestão.

## Escolher a semântica

Se um tipo representa um valor, a cópia deve ser independente. Se representa propriedade exclusiva, proíbe a cópia e permite movimento. Decide isto antes de escreveres o destrutor, porque a política de cópia faz parte da interface.

## Exercícios

Independência entre cópias

Uma classe possui um array dinâmico de dois inteiros. A cópia gerada pelo compilador copia só o apontador, e o destrutor usa `delete[]`. Explica o que corre mal e duas políticas válidas para o tipo.

Pista

Segue quem julga ser dono do mesmo array depois da cópia.

Solução

Uma alteração através de um objeto aparece no recurso do outro. Na destruição, ambos tentam libertar o mesmo array, causando comportamento indefinido. Uma política válida é implementar cópia profunda com recursos independentes. Outra é proibir cópia e permitir transferência por movimento. Se o significado exige partilha, usa uma propriedade partilhada explícita.

Confere a tua resposta:

*   Identifiquei o recurso único com dois donos indevidos.
*   Expliquei aliasing e dupla libertação.
*   Propus duas políticas coerentes com o contrato.

Erros frequentes

Acrescentar apenas um destrutor não completa a semântica de uma classe dona.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/copia-propriedade/#c%C3%B3pia-superficial-e-profunda)

Operações válidas depois do movimento

No `Buffer` da lição, executa-se `Buffer a(3); Buffer b = std::move(a);`. Indica os tamanhos finais e se `a.at(0)` pode devolver um inteiro. Noutro programa, move-se um `std::vector<int> v`. É válido consultar `v.size()`? E chamar `v.front()` sem verificar nada? Justifica usando o contrato de cada tipo.

Pista

Um objeto válido não garante a existência de um primeiro elemento.

Solução

O construtor de movimento do `Buffer` põe o tamanho de `a` a zero e transfere os três elementos para `b`. `a.at(0)` lança `out_of_range` porque não existe índice válido. No vetor movido, o estado é válido mas não se promete que fique vazio. `size()` é uma consulta válida; antes de `front()`, confirma `!v.empty()`. `std::move` sozinho não impõe estas garantias.

Confere a tua resposta:

*   Indiquei a.size() igual a zero e b.size() igual a três no Buffer.
*   Distingui consultar o tamanho de aceder a um elemento.
*   Não pressupus que um vector movido fica vazio.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/copia-propriedade/#movimento-n%C3%A3o-%C3%A9-uma-c%C3%B3pia-profunda)

Atribuir sem perder o recurso anterior

Um `Buffer c(3)` contém `2, 3, 5` e recebe `c = b`, onde `b` contém `9, 6`. Descreve os três momentos: preparar a cópia, trocar e destruir o antigo. Explica o que aconteceria se a reserva da cópia falhasse em cada alternativa.

Pista

Segue quem é dono de cada array em cada momento.

Solução

Primeiro constrói-se `copia(b)` com recurso próprio. Depois a troca entrega esse recurso a `c`. No fim, o recurso antigo de `c` morre com `copia`. Se a reserva falhar na preparação, `c` continua com os três valores `2, 3, 5`. Na alternativa de libertar primeiro, a falha deixaria `c` sem recurso e sem valor anterior.

Confere a tua resposta:

*   Preparei a cópia antes de tocar no destino.
*   Expliquei quando o recurso antigo é libertado.
*   Mostrei o que corre mal ao libertar primeiro e copiar depois.

Erros frequentes

Testar autoatribuição não chega. A ordem prepara-depois-troca é que dá a garantia forte.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/p/copia-propriedade/#atribuir-sem-perder-o-recurso-anterior)
