Pular para conteúdo

Lista 10 · Destrutor, RAII e a regra do zero

Exercícios da Semana 11 — Destrutor, RAII e a regra do zero. Nos exercícios 1 a 3, siga a regra do zero: use std::vector/std::string como membro interno, e não escreva destrutor nem construtor de cópia — o teste verifica exatamente que copiar seu objeto produz uma cópia independente (profunda), sem você precisar escrever nada extra para isso.

Como resolver

Copie o bloco de código de cada exercício, cole no seu compilador online, preencha a parte marcada como ESCREVA SUA SOLUCAO AQUI e rode. A saída mostra quais testes passaram.

ex01

Complete a classe Mochila (campo privado itens_, do tipo std::vector<std::string>, já declarado — siga a regra do zero: não escreva destrutor nem construtor de cópia). Implemente adiciona(const std::string& item), contem(const std::string& item) const e tamanho() const.

Mochila m;
m.adiciona("corda");
m.adiciona("lanterna");
m.tamanho();          // -> 2
m.contem("corda");    // -> true
m.contem("faca");      // -> false

Mochila copia = m;      // copia -- deve ser independente de m
copia.adiciona("faca");
m.tamanho();             // -> continua 2 (a copia nao afeta o original)
copia.tamanho();          // -> 3
// ex01 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX01_H
#define EX01_H

class Mochila {
private:
    std::vector<std::string> itens_;

public:
    void adiciona(const std::string& item);
    bool contem(const std::string& item) const;
    int tamanho() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void Mochila::adiciona(const std::string& item) {
    // TODO: adicione "item" ao final de itens_.
}

bool Mochila::contem(const std::string& item) const {
    // TODO: percorra itens_ e retorne true se algum elemento for igual a "item".
    return false;
}

int Mochila::tamanho() const {
    // TODO: retorne o tamanho de itens_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Mochila m;
    m.adiciona("corda");
    m.adiciona("lanterna");
    assert(m.tamanho() == 2);
    assert(m.contem("corda"));
    assert(!(m.contem("faca")));

    Mochila copia = m;
    copia.adiciona("faca");
    assert(m.tamanho() == 2);
    assert(copia.tamanho() == 3);
    assert(!(m.contem("faca")));
    assert(copia.contem("faca"));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex02

Complete a classe CarrinhoDeCompras (campo privado precos_, std::vector<double>, já declarado — regra do zero). Implemente adiciona_item(double preco), total() const (soma de todos os preços) e quantidade_itens() const.

CarrinhoDeCompras c;
c.adiciona_item(10.0);
c.adiciona_item(25.5);
c.total();             // -> 35.5
c.quantidade_itens();  // -> 2

CarrinhoDeCompras copia = c;
copia.adiciona_item(100.0);
c.total();               // -> continua 35.5
copia.total();            // -> 135.5
// ex02 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>
#include <vector>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX02_H
#define EX02_H

class CarrinhoDeCompras {
private:
    std::vector<double> precos_;

public:
    void adiciona_item(double preco);
    double total() const;
    int quantidade_itens() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void CarrinhoDeCompras::adiciona_item(double preco) {
    // TODO: adicione "preco" ao final de precos_.
}

double CarrinhoDeCompras::total() const {
    // TODO: some todos os elementos de precos_.
    return 0.0;
}

int CarrinhoDeCompras::quantidade_itens() const {
    // TODO: retorne o tamanho de precos_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    CarrinhoDeCompras c;
    c.adiciona_item(10.0);
    c.adiciona_item(25.5);
    assert(std::abs((c.total()) - (35.5)) <= (0.0001));
    assert(c.quantidade_itens() == 2);

    CarrinhoDeCompras copia = c;
    copia.adiciona_item(100.0);
    assert(std::abs((c.total()) - (35.5)) <= (0.0001));
    assert(std::abs((copia.total()) - (135.5)) <= (0.0001));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex03

Complete a classe HistoricoNavegacao (campo privado paginas_, std::vector<std::string>, já declarado — regra do zero). Implemente visita(const std::string& pagina) (adiciona ao final), ultima_visitada(std::string& pagina) const (escreve a última página no parâmetro e devolve true; devolve false se o histórico estiver vazio) e total_visitas() const.

HistoricoNavegacao h;
h.visita("home");
h.visita("perfil");

std::string p;
h.ultima_visitada(p);   // -> true, e p vale "perfil"
h.total_visitas();      // -> 2

HistoricoNavegacao vazio;
vazio.ultima_visitada(p); // -> false
// ex03 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX03_H
#define EX03_H

class HistoricoNavegacao {
private:
    std::vector<std::string> paginas_;

public:
    void visita(const std::string& pagina);
    // Escreve a ultima pagina visitada em "pagina" e devolve true.
    // Devolve false e nao mexe em "pagina" se o historico estiver vazio.
    bool ultima_visitada(std::string& pagina) const;
    int total_visitas() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void HistoricoNavegacao::visita(const std::string& pagina) {
    // TODO: adicione "pagina" ao final de paginas_.
}

bool HistoricoNavegacao::ultima_visitada(std::string& pagina) const {
    // TODO: se paginas_ estiver vazio, retorne std::nullopt.
    // Senao, retorne o ultimo elemento de paginas_.
    return false;
}

int HistoricoNavegacao::total_visitas() const {
    // TODO: retorne o tamanho de paginas_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    HistoricoNavegacao h;
    h.visita("home");
    h.visita("perfil");
    std::string ultima;
    assert(h.ultima_visitada(ultima));
    assert(ultima == std::string("perfil"));
    assert(h.total_visitas() == 2);

    HistoricoNavegacao vazio;
    std::string nada = "intocada";
    assert(!vazio.ultima_visitada(nada));
    assert(nada == std::string("intocada"));
    assert(vazio.total_visitas() == 0);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex04

Dada a classe Registro (campo valor_, construtor e getter valor() const já implementados, e um contador estático Registro::total_vivos que sobe na construção e desce na destruição), escreva duas funções:

  • cria_registro(int valor) — devolve um std::unique_ptr<Registro> apontando para um Registro construído com esse valor. Use std::make_unique.
  • consome(std::unique_ptr<Registro> registro) — recebe o ponteiro por valor, devolve o valor() do registro, e deixa o objeto ser destruído quando a função termina.

O segundo é o exercício de verdade, e o assunto dele é posse. Um std::unique_ptr não pode ser copiado — se pudesse, dois ponteiros achariam que são donos do mesmo objeto e os dois dariam delete nele. Por isso consome(p) não compila: quem chama precisa escrever consome(std::move(p)), dizendo explicitamente "não estou copiando, estou entregando".

Depois do std::move, o unique_ptr de quem chamou fica valendo nullptr — ele não é mais dono de nada. E quando consome termina, o parâmetro sai de escopo e o Registro é destruído sozinho: não há delete em lugar nenhum do seu código. Os testes verificam as duas coisas, usando Registro::total_vivos.

Dentro de consome, leia o valor antes de a função terminar — e devolva-o. Depois do return o objeto já não existe mais.

auto r = cria_registro(21);
r->valor();                  // -> 21

int v = consome(std::move(r));
v;                            // -> 21
r == nullptr;                 // -> true;  r entregou a posse
// o Registro ja foi destruido aqui, sem nenhum delete escrito por voce
// ex04 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <memory>
#include <utility>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX04_H
#define EX04_H

class Registro {
private:
    int valor_;

public:
    static inline int total_vivos = 0;

    explicit Registro(int valor) : valor_(valor) { total_vivos++; }
    ~Registro() { total_vivos--; }

    int valor() const { return valor_; }
};

// Cria um Registro(valor) alocado dinamicamente e devolve um unique_ptr para ele.
std::unique_ptr<Registro> cria_registro(int valor);

// Recebe a POSSE do registro (por valor, exigindo std::move na chamada),
// devolve o valor guardado e deixa o objeto ser destruido no fim da funcao.
int consome(std::unique_ptr<Registro> registro);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::unique_ptr<Registro> cria_registro(int valor) {
    // TODO: use std::make_unique<Registro>(valor) e retorne o resultado.
    return nullptr;
}

int consome(std::unique_ptr<Registro> registro) {
    // TODO: guarde registro->valor() numa variavel e devolva essa variavel.
    // Nao escreva delete: quando a funcao terminar, o unique_ptr sai de
    // escopo e destroi o Registro sozinho.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Registro::total_vivos = 0;

    auto r = cria_registro(21);
    assert(r != nullptr);
    assert(r->valor() == 21);
    assert(Registro::total_vivos == 1);

    // std::move entrega a posse: r fica sem dono nenhum...
    int v = consome(std::move(r));
    assert(v == 21);
    assert(r == nullptr);

    // ...e o Registro foi destruido no fim de consome(), sem nenhum delete
    assert(Registro::total_vivos == 0);

    // um unique_ptr que sai de escopo sozinho tambem destroi o objeto
    {
        auto temporario = cria_registro(7);
        assert(Registro::total_vivos == 1);
        assert(temporario->valor() == 7);
    }
    assert(Registro::total_vivos == 0);

    // o resultado de cria_registro pode ser entregue direto, sem variavel
    assert(consome(cria_registro(0)) == 0);
    assert(Registro::total_vivos == 0);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex05

Complete a classe Sensor: ela tem um contador estático total_destruidos (compartilhado por todos os objetos da classe, já declarado no .h) que deve ser incrementado toda vez que um Sensor é destruído. Implemente o destrutor ~Sensor().

Sensor::total_destruidos = 0;   // (o teste zera o contador antes de comecar)
{
    Sensor s1;
    Sensor s2;
}   // s1 e s2 saem de escopo aqui -- os destrutores rodam AUTOMATICAMENTE
Sensor::total_destruidos;  // -> 2
// ex05 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX05_H
#define EX05_H

class Sensor {
public:
    static inline int total_destruidos = 0;

    ~Sensor();
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
Sensor::~Sensor() {
    // TODO: incremente Sensor::total_destruidos (ou apenas total_destruidos,
    // dentro de um metodo da propria classe).
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Sensor::total_destruidos = 0;
    {
        Sensor s1;
        Sensor s2;
        assert(Sensor::total_destruidos == 0);   // ainda nao saiu de escopo
    }
    assert(Sensor::total_destruidos == 2);

    {
        Sensor s3;
    }
    assert(Sensor::total_destruidos == 3);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex06

Complete a classe ListaDeCompras (campo privado itens_, do tipo std::vector<std::string>, já declarado — regra do zero: nada de destrutor ou construtor de cópia, o vector cuida da memória sozinho).

  • adiciona(const std::string& item) — acrescenta o item ao fim apenas se ele ainda não estiver na lista. Devolve true se acrescentou, false se já existia.
  • contem(const std::string& item) const — devolve true se o item está na lista.
  • quantidade() const — quantos itens a lista tem.
ListaDeCompras l;
l.adiciona("cafe");    // -> true
l.adiciona("cafe");    // -> false  (ja estava)
l.quantidade();        // -> 1
// ex06 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX06_H
#define EX06_H

class ListaDeCompras {
private:
    std::vector<std::string> itens_;

public:
    // Acrescenta o item so se ele ainda nao estiver na lista.
    // Retorna true se acrescentou, false se ja existia.
    bool adiciona(const std::string& item);
    bool contem(const std::string& item) const;
    int quantidade() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
bool ListaDeCompras::adiciona(const std::string& item) {
    // TODO: se contem(item) for true, devolva false sem mexer na lista.
    // Senao, faca push_back e devolva true.
    return false;
}

bool ListaDeCompras::contem(const std::string& item) const {
    // TODO: percorra itens_ procurando por item.
    return false;
}

int ListaDeCompras::quantidade() const {
    // TODO: devolva o tamanho de itens_ (cuidado: .size() devolve size_t,
    // converta para int com static_cast<int>).
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    ListaDeCompras l;
    assert(l.quantidade() == 0);
    assert(!(l.contem("cafe")));

    assert(l.adiciona("cafe"));
    assert(l.contem("cafe"));
    assert(l.quantidade() == 1);

    assert(!(l.adiciona("cafe")));
    assert(l.quantidade() == 1);

    assert(l.adiciona("acucar"));
    assert(l.quantidade() == 2);

    // Regra do zero: copiar a lista funciona sem escrever construtor de copia.
    ListaDeCompras copia = l;
    assert(copia.quantidade() == 2);
    copia.adiciona("leite");
    assert(copia.quantidade() == 3);
    assert(l.quantidade() == 2);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex07

Complete a classe ListaDeTarefas (campo privado tarefas_, std::vector<std::string>, já declarado — regra do zero), que é o outro lado do exercício anterior: aqui o que interessa é remover.

  • adiciona(const std::string& tarefa) — acrescenta ao fim, sem checar duplicata.
  • remove(const std::string& tarefa) — remove a primeira ocorrência da tarefa, se ela existir. Devolve true se removeu, false se a tarefa não estava na lista.
  • quantidade() const — quantas tarefas restam.

Para remover um elemento pelo índice, use tarefas_.erase(tarefas_.begin() + i).

ListaDeTarefas t;
t.adiciona("estudar");
t.remove("estudar");   // -> true
t.remove("estudar");   // -> false  (ja tinha saido)
// ex07 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX07_H
#define EX07_H

class ListaDeTarefas {
private:
    std::vector<std::string> tarefas_;

public:
    void adiciona(const std::string& tarefa);
    // Remove a primeira ocorrencia da tarefa. Retorna true se removeu.
    bool remove(const std::string& tarefa);
    int quantidade() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void ListaDeTarefas::adiciona(const std::string& tarefa) {
    // TODO: push_back da tarefa.
}

bool ListaDeTarefas::remove(const std::string& tarefa) {
    // TODO: percorra tarefas_ com um indice; ao achar a tarefa, chame
    // tarefas_.erase(tarefas_.begin() + i) e devolva true.
    // Se o laco terminar sem achar, devolva false.
    return false;
}

int ListaDeTarefas::quantidade() const {
    // TODO: devolva o tamanho de tarefas_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    ListaDeTarefas t;
    assert(!(t.remove("estudar")));

    t.adiciona("estudar");
    t.adiciona("comprar cabo");
    assert(t.quantidade() == 2);

    assert(t.remove("estudar"));
    assert(t.quantidade() == 1);

    assert(!(t.remove("estudar")));
    assert(t.quantidade() == 1);

    // So a primeira ocorrencia sai.
    ListaDeTarefas repetida;
    repetida.adiciona("revisar");
    repetida.adiciona("revisar");
    assert(repetida.remove("revisar"));
    assert(repetida.quantidade() == 1);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex08

Complete a classe Serie (campo privado valores_, std::vector<int>, já declarado — regra do zero: não escreva destrutor nem construtor de cópia). Implemente adiciona(int), quantidade() const e tem_par_com_soma(int alvo) const.

O último devolve true se existirem dois elementos em posições diferentes cuja soma seja o alvo. O detalhe que decide o exercício está aí: um elemento não pode ser somado com ele mesmo, mas dois elementos iguais em posições diferentes valem. Fazer o laço interno começar em i + 1 resolve os dois casos de uma vez.

Serie s;   // 2, 7, 11, 15
s.tem_par_com_soma(9);     // -> true   (2 + 7)
s.tem_par_com_soma(100);   // -> false
// ex08 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <vector>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX08_H
#define EX08_H

// Regra do zero: o vector cuida de si, entao esta classe nao precisa de
// destrutor, construtor de copia nem operador de atribuicao.
class Serie {
private:
    std::vector<int> valores_;

public:
    void adiciona(int valor);
    int quantidade() const;

    // Retorna true se existirem DOIS elementos em posicoes diferentes
    // cuja soma seja igual a alvo.
    bool tem_par_com_soma(int alvo) const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void Serie::adiciona(int valor) {
    // TODO: acrescente valor ao fim de valores_.
}

int Serie::quantidade() const {
    // TODO: devolva o tamanho de valores_.
    return 0;
}

bool Serie::tem_par_com_soma(int alvo) const {
    // TODO: laco duplo. Para cada i, teste os j MAIORES que i -- assim
    // nenhum elemento e somado com ele mesmo e nenhum par se repete.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Serie s;
    for (int x : {2, 7, 11, 15}) {
        s.adiciona(x);
    }
    assert(s.quantidade() == 4);
    assert(s.tem_par_com_soma(9));    // 2 + 7
    assert(s.tem_par_com_soma(26));   // 11 + 15
    assert(!(s.tem_par_com_soma(100)));

    // nao vale somar um elemento com ele mesmo
    Serie um;
    um.adiciona(4);
    assert(!(um.tem_par_com_soma(8)));

    // ...mas dois elementos iguais em posicoes diferentes valem
    Serie dois;
    dois.adiciona(4);
    dois.adiciona(4);
    assert(dois.tem_par_com_soma(8));

    Serie vazia;
    assert(!(vazia.tem_par_com_soma(0)));

    // regra do zero: copiar funciona sem escrever nada
    Serie copia = s;
    copia.adiciona(1);
    assert(copia.quantidade() == 5);
    assert(s.quantidade() == 4);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex09

Complete a classe Playlist (campo privado musicas_, um std::vector<std::string> já declarado). Implemente adiciona(const std::string& musica) e quantidade() const.

Siga a regra do zero: não escreva destrutor, construtor de cópia nem operador de atribuição. O teste copia a playlist e confere que as duas passam a viver independentes — o que já funciona sozinho, porque o vector sabe se copiar.

Playlist p;
p.adiciona("musica A");
p.adiciona("musica B");
p.quantidade();          // -> 2

Playlist copia = p;
copia.adiciona("musica C");
p.quantidade();            // -> continua 2 (copia independente)
copia.quantidade();         // -> 3
// ex09 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX09_H
#define EX09_H

class Playlist {
private:
    std::vector<std::string> musicas_;

public:
    void adiciona(const std::string& musica);
    int quantidade() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
void Playlist::adiciona(const std::string& musica) {
    // TODO: adicione "musica" ao final de musicas_.
}

int Playlist::quantidade() const {
    // TODO: retorne o tamanho de musicas_.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Playlist p;
    p.adiciona("musica A");
    p.adiciona("musica B");
    assert(p.quantidade() == 2);

    Playlist copia = p;
    copia.adiciona("musica C");
    assert(p.quantidade() == 2);
    assert(copia.quantidade() == 3);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}