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 umstd::unique_ptr<Registro>apontando para umRegistroconstruído com esse valor. Usestd::make_unique.consome(std::unique_ptr<Registro> registro)— recebe o ponteiro por valor, devolve ovalor()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. Devolvetruese acrescentou,falsese já existia.contem(const std::string& item) const— devolvetruese 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. Devolvetruese removeu,falsese 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;
}