Tarefa 1: Implementando Polimorfismo com Classes Abstratas
Este exercício demonstra o uso de polimorfismo em tempo de execução em C++ utilizando classes abstratas e funções virtuais puras. Definimos uma classe base abstrata Publisher com interfaces virtuais puras publish() e use(). Classes derivadas como Book, Film e Music implementam essas interfaces para fornecer comportamentos específicos.
Código-Fonte
publisher.hpp
#pragma once
#include <string>
// Classe base abstrata para publicações
class Publisher {
public:
Publisher(const std::string &name_ = "");
virtual ~Publisher() = default;
// Interfaces virtuais puras
virtual void publish() const = 0;
virtual void use() const = 0;
protected:
std::string name; // Nome da publicação
};
// Classe derivada para livros
class Book: public Publisher {
public:
Book(const std::string &name_ = "", const std::string &author_ = "");
void publish() const override;
void use() const override;
private:
std::string author; // Autor do livro
};
// Classe derivada para filmes
class Film: public Publisher {
public:
Film(const std::string &name_ = "", const std::string &director_ = "");
void publish() const override;
void use() const override;
private:
std::string director; // Diretor do filme
};
// Classe derivada para músicas
class Music: public Publisher {
public:
Music(const std::string &name_ = "", const std::string &artist_ = "");
void publish() const override;
void use() const override;
private:
std::string artist; // Artista da música
};
publisher.cpp
#include <iostream>
#include <string>
#include "publisher.hpp"
// Implementação do construtor de Publisher
Publisher::Publisher(const std::string &name_): name {name_} {
}
// Implementação de Book
Book::Book(const std::string &name_ , const std::string &author_ ): Publisher{name_}, author{author_} {
}
void Book::publish() const {
std::cout << "Publicando livro: \"" << name << "\" por " << author << '\n';
}
void Book::use() const {
std::cout << "Lendo o livro: \"" << name << "\" por " << author << '\n';
}
// Implementação de Film
Film::Film(const std::string &name_, const std::string &director_):Publisher{name_},director{director_} {
}
void Film::publish() const {
std::cout << "Publicando filme: \"" << name << "\" dirigido por " << director << '\n';
}
void Film::use() const {
std::cout << "Assistindo ao filme: \"" << name << "\" dirigido por " << director << '\n';
}
// Implementação de Music
Music::Music(const std::string &name_, const std::string &artist_): Publisher{name_}, artist{artist_} {
}
void Music::publish() const {
std::cout << "Publicando música: \"" << name << "\" por " << artist << '\n';
}
void Music::use() const {
std::cout << "Ouvindo a música: \"" << name << "\" por " << artist << '\n';
}
task1.cpp
#include <memory>
#include <iostream>
#include <vector>
#include "publisher.hpp"
// Teste com ponteiros brutos
void testRawPointers() {
std::vector<Publisher *> publications;
publications.push_back(new Book("O Senhor dos Anéis", "J.R.R. Tolkien"));
publications.push_back(new Film("O Poderoso Chefão", "Francis Ford Coppola"));
publications.push_back(new Music("Bohemian Rhapsody", "Queen"));
for(Publisher *pub_ptr : publications) {
pub_ptr->publish();
pub_ptr->use();
std::cout << '\n';
delete pub_ptr; // Liberação manual de memória
}
}
// Teste com ponteiros inteligentes (unique_ptr)
void testSmartPointers() {
std::vector<std::unique_ptr<Publisher>> publications;
publications.push_back(std::make_unique<Book>("1984", "George Orwell"));
publications.push_back(std::make_unique<Film>("Pulp Fiction", "Quentin Tarantino"));
publications.push_back(std::make_unique<Music>("Stairway to Heaven", "Led Zeppelin"));
for(const auto &pub_ptr : publications) {
pub_ptr->publish();
pub_ptr->use();
std::cout << '\n';
// A memória é liberada automaticamente pelo unique_ptr
}
}
// Teste com objetos locais
void testDirectUsage() {
Book book("O Guia do Mochileiro das Galáxias", "Douglas Adams");
book.publish();
book.use();
}
int main() {
std::cout << "--- Testando Polimorfismo em Tempo de Execução ---\n";
std::cout << "\n--- Teste 1: Usando Ponteiros Brutos ---\n";
testRawPointers();
std::cout << "\n--- Teste 2: Usando Ponteiros Inteligentes (unique_ptr) ---\n";
testSmartPointers();
std::cout << "\n--- Teste 3: Uso Direto das Classes Derivadas ---\n";
testDirectUsage();
return 0;
}
Respostas às Questões
Questão 1
- (1) A classe
Publisherpossui duas funções virtuais puras:publish()euse(). Isso é evidenciado pela sintaxevirtual ... = 0;. - (2) Não é possível instanciar um objeto da classe
Publisherdiretamente. Por ser uma classe abstrata devido às suas funções virtuais puras, o compilador impedirá a criação de objetosPublisher, resultando em um erro como "não é possível instanciar um objeto de tipo abstrato 'Publisher'".
Questão 2
- (1) Para que as classes derivadas compilem corretamente, elas devem implementar as funções virtuais puras
publish()euse(). A declaração deve usar a palavra-chaveoverridepara garantir que a função está, de fato, sobrescrevendo uma função virtual da classe base. Exemplo paraFilm:void publish() const override; - (2) Se uma função virtual pura da classe base não for implementada em uma classe derivada, o compilador emitirá um erro indicando a incompatibilidade ou a ausência da implementação necessária. Por exemplo, um erro como "a declaração é incompatível com 'void Film::publish() const'".
Questão 3
- (1) No loop
for(Publisher *ptr : v), a variávelptré declarada como um ponteiro do tipoPublisher. - (2) O ponteiro
ptr, embora declarado comoPublisher*, aponta na verdade para objetos das classes derivadas:Book,FilmeMusic, dependendo do objeto que foi adicionado ao vetor. - (3) A palavra-chave
virtualé essencial para o destruidor (virtual ~Publisher() = default;). Quando um objeto derivado é deletado através de um ponteiro da classe base (delete ptr;), o destruidor virtual garante que o destruidor da classe derivada apropriada seja chamado antes do destruidor da classe base. Sem ovirtual, apenas o destruidor da classe base seria chamado, o que poderia levar a vazamentos de memória e comportamento indefinido se os destruidores das classes derivadas tivessem lógica específica (como desalocação de recursos).
Tarefa 2: Gerenciamento de Dados de Vendas de Livros
Estee exercício foca na criação de classes para representar informações de livros e seus registros de vendas. Implementamos a sobrecarga do operador de inserção (<<) para facilitar a exibição formatada de objetos Book e BookSale, e utilizamos algoritmos de ordenação para processar os dados de vendas.
Código-Fonte
book.hpp
#pragma once
#include <string>
#include <ostream>
// Classe para representar informações de um livro
class Book {
public:
Book(const std::string &name_,
const std::string &author_,
const std::string &translator_,
const std::string &isbn_,
double price_);
// Sobrecarga do operador de inserção para exibição
friend std::ostream& operator<<(std::ostream &out, const Book &book);
private:
std::string name; // Título do livro
std::string author; // Autor
std::string translator; // Tradutor
std::string isbn; // Código ISBN
double price; // Preço de capa
};
book.cpp
#include <iomanip>
#include <iostream>
#include <string>
#include "book.hpp"
// Implementação do construtor de Book
Book::Book(const std::string &name_,
const std::string &author_,
const std::string &translator_,
const std::string &isbn_,
double price_):name{name_}, author{author_}, translator{translator_}, isbn{isbn_}, price{price_} {
}
// Implementação da sobrecarga do operador << para Book
std::ostream& operator<<(std::ostream &out, const Book &book) {
using std::left;
using std::setw;
out << left; // Alinhamento à esquerda
out << setw(15) << "Título:" << book.name << '\n'
<< setw(15) << "Autor:" << book.author << '\n'
<< setw(15) << "Tradutor:" << book.translator << '\n'
<< setw(15) << "ISBN:" << book.isbn << '\n'
<< setw(15) << "Preço:" << book.price;
return out;
}
booksale.hpp
#pragma once
#include <string>
#include "book.hpp"
// Classe para registrar vendas de livros
class BookSale {
public:
BookSale(const Book &book_ref_, double sale_price_, int quantity_);
int get_quantity() const; // Retorna a quantidade vendida
double get_revenue() const; // Retorna a receita total
// Sobrecarga do operador de inserção para exibição
friend std::ostream& operator<<(std::ostream &out, const BookSale &item);
private:
Book book_details; // Detalhes do livro vendido
double sale_price; // Preço unitário de venda
int quantity_sold; // Quantidade vendida
};
booksale.cpp
#include <iomanip>
#include <iostream>
#include <string>
#include "booksale.hpp"
// Implementação do construtor de BookSale
BookSale::BookSale(const Book &book_ref_,
double sales_price_,
int sales_amount_): book_details{book_ref_}, sale_price{sales_price_}, quantity_sold{sales_amount_} {
}
// Retorna a quantidade vendida
int BookSale::get_quantity() const {
return quantity_sold;
}
// Calcula e retorna a receita total
double BookSale::get_revenue() const {
return quantity_sold * sale_price;
}
// Implementação da sobrecarga do operador << para BookSale
std::ostream& operator<<(std::ostream &out, const BookSale &item) {
using std::left;
using std::setw;
out << left;
// Exibe os detalhes do livro usando o operador << sobrecarregado para Book
out << item.book_details << '\n'
<< setw(15) << "Preço Unitário:" << item.sale_price << '\n'
<< setw(15) << "Quantidade Vendida:" << item.quantity_sold << '\n'
<< setw(15) << "Receita Total:" << item.get_revenue();
return out;
}
task2.cpp
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include "booksale.hpp"
// Função de comparação para ordenar por quantidade vendida (decrescente)
bool compareSalesByQuantity(const BookSale &sale1, const BookSale &sale2) {
return sale1.get_quantity() > sale2.get_quantity();
}
void manageSales() {
using std::cin;
using std::cout;
using std::getline;
using std::sort;
using std::string;
using std::vector;
using std::ws; // Para consumir espaços em branco
vector<BookSale> sales_ledger; // Registro de vendas
int num_books;
cout << "Digite a quantidade de registros de vendas a inserir: ";
cin >> num_books;
cout << "\n--- Inserindo Registros de Vendas ---\n";
for(int i = 0; i < num_books; ++i) {
string title, author, translator, isbn_code;
double cover_price;
cout << string(10, '*') << " Registro #" << (i + 1) << " " << string(10, '*') << '\n';
cout << "Título: "; getline(cin >> ws, title);
cout << "Autor: "; getline(cin >> ws, author);
cout << "Tradutor: "; getline(cin >> ws, translator);
cout << "ISBN: "; getline(cin >> ws, isbn_code);
cout << "Preço de Capa: "; cin >> cover_price;
Book current_book(title, author, translator, isbn_code, cover_price);
double selling_price;
int units_sold;
cout << "Preço de Venda Unitário: "; cin >> selling_price;
cout << "Unidades Vendidas: "; cin >> units_sold;
BookSale sale_record(current_book, selling_price, units_sold);
sales_ledger.push_back(sale_record);
cout << '\n';
}
// Ordena os registros de vendas pela quantidade vendida (decrescente)
sort(sales_ledger.begin(), sales_ledger.end(), compareSalesByQuantity);
// Exibe o relatório de vendas ordenado
cout << string(20, '=') << " Relatório de Vendas de Livros " << string(20, '=') << '\n';
for(const auto &record : sales_ledger) {
cout << record << "\n" << string(45, '-') << '\n';
}
}
int main() {
manageSales();
return 0;
}
Respostas às Questões
Questão 1
-
(1) O operador de inserção
<<foi sobrecarregado duas vezes: uma para a classeBooke outra para a classeBookSale. -
(2) O código para a sobrecarga do operador
<<paraBooké: ```std::ostream& operator<<(std::ostream &out, const Book &book) { using std::left; using std::setw; out << left; out << setw(15) << "Título:" << book.name << '\n' << setw(15) << "Autor:" << book.author << '\n' << setw(15) << "Tradutor:" << book.translator << '\n' << setw(15) << "ISBN:" << book.isbn << '\n' << setw(15) << "Preço:" << book.price; return out; }
E para `BookSale`: ``` std::ostream& operator<<(std::ostream &out, const BookSale &item) { using std::left; using std::setw; out << left; out << item.book_details << '\n' << setw(15) << "Preço Unitário:" << item.sale_price << '\n' << setw(15) << "Quantidade Vendida:" << item.quantity_sold << '\n' << setw(15) << "Receita Total:" << item.get_revenue(); return out; }
Questão 2
-
(1) Os dados de vendas são armazenados em um
std::vector<BookSale>chamadosales_ledger. A funçãostd::sorté chamada para ordenar este vetor do início (sales_ledger.begin()) ao fim (sales_ledger.end()), utilizando a função de comparaçãocompareSalesByQuantitypara definir a ordem (decrescente pela quantidade vendida). -
(2) A ordenação pode ser feita utilizando uma expressão lambda diretamente na chamada de
std::sort: ```// Ordena os registros de vendas pela quantidade vendida (decrescente) usando lambda std::sort(sales_ledger.begin(), sales_ledger.end(), [](const BookSale& saleA, const BookSale& saleB) { return saleA.get_quantity() > saleB.get_quantity(); } );
Tarefa 4: Simulação de Interações de Animais de Estimação Mecânicos
Este exercício explora o polimorfismo com classes derivadas de uma classe base abstrata MachinePet. Definimos PetCat e PetDog que herdam de MachinePet e implementam a função virtual pura talk() para produzir sons distintos.
Código-Fonte
pet.hpp
#pragma once
#include <string>
// Classe base abstrata para animais de estimação mecânicos
class MachinePet
{
public:
MachinePet(const std::string &name0) : nick_name{name0} {}
std::string get_nickname() const { return nick_name; }
// Função virtual pura para o som do animal
virtual std::string talk() const = 0;
private:
std::string nick_name; // Nome do animal
};
// Classe derivada para um gato mecânico
class PetCat : public MachinePet
{
public:
PetCat(const std::string& name0 = "GatoPadrão") : MachinePet{name0} { }
// Implementação do som do gato
std::string talk() const override { return "miau"; }
};
// Classe derivada para um cachorro mecânico
class PetDog : public MachinePet
{
public:
PetDog(const std::string& name0 = "CachorroPadrão") : MachinePet{name0} { }
// Implementação do som do cachorro
std::string talk() const override { return "woof"; }
};
task4.cpp
#include <iostream>
#include <memory>
#include <vector>
#include "pet.hpp"
// Teste com ponteiros brutos para animais
void testRawPointersPets() {
std::vector<MachinePet *> zoo;
zoo.push_back(new PetCat("Miku"));
zoo.push_back(new PetDog("Rex"));
for(MachinePet *pet_ptr : zoo) {
std::cout << pet_ptr->get_nickname() << " diz: " << pet_ptr->talk() << '\n';
delete pet_ptr; // Necessário liberar memória manualmente
}
}
// Teste com ponteiros inteligentes (unique_ptr) para animais
void testSmartPointersPets() {
std::vector<std::unique_ptr<MachinePet>> zoo;
zoo.push_back(std::make_unique<PetCat>("Luna"));
zoo.push_back(std::make_unique<PetDog>("Buddy"));
for(const auto &pet_ptr : zoo)
std::cout << pet_ptr->get_nickname() << " diz: " << pet_ptr->talk() << '\n';
// Memória liberada automaticamente
}
// Teste com uso direto das classes derivadas
void testDirectUsagePets() {
// MachinePet pet("Criatura"); // Erro de compilação: não pode instanciar classe abstrata
const PetCat cat("Mia");
std::cout << cat.get_nickname() << " diz: " << cat.talk() << '\n';
const PetDog dog("Thor");
std::cout << dog.get_nickname() << " diz: " << dog.talk() << '\n';
}
int main() {
std::cout << "--- Testando Animais Mecânicos ---\n";
std::cout << "\n--- Teste 1: Ponteiros Brutos ---\n";
testRawPointersPets();
std::cout << "\n--- Teste 2: Ponteiros Inteligentes ---\n";
testSmartPointersPets();
std::cout << "\n--- Teste 3: Uso Direto ---\n";
testDirectUsagePets();
return 0;
}
Tarefa 5: Implementação de Números Complexos com Templates
Este exercício desenvolve uma classe template Complex para representar números complexos. A clasce suporta operações como adição, comparação e entrada/saída de dados, utilizando templates para permitir que os componentes real e imaginário sejam de diferentes tipos numéricos (como int ou double).
Código-Fonte
Complex.hpp
#pragma once
#include <iostream>
template <typename T>
class Complex
{
public:
// Construtor padrão e com inicialização
Complex() = default;
Complex(T real_part, T imag_part) : real{real_part}, imag{imag_part} { }
// Sobrecarga do operador de atribuição de adição
void operator+=(const Complex& c) {
real += c.real;
imag += c.imag;
}
// Getters para as partes real e imaginária
T get_real() const { return real; }
T get_imag() const { return imag; }
// Declaração de operadores friend como templates
template <typename U> friend std::ostream& operator<<(std::ostream& out, const Complex& c);
template <typename U> friend std::istream& operator>>(std::istream& in, Complex& c);
template <typename U> friend Complex operator+(const Complex& c1, const Complex& c2);
template <typename U> friend bool operator==(const Complex& c1, const Complex& c2);
private:
T real; // Parte real
T imag; // Parte imaginária
};
// Implementação da sobrecarga do operador << para Complex
template <typename T>
std::ostream& operator<<(std::ostream& out, const Complex& c)
{
out << c.real << " + i*" << c.imag; // Formato: real + i*imag
return out;
}
// Implementação da sobrecarga do operador >> para Complex
template <typename T>
std::istream& operator>>(std::istream& in, Complex& c)
{
in >> c.real >> c.imag; // Lê real e imaginário
return in;
}
// Implementação do operador + para Complex
template <typename T>
Complex operator+(const Complex& c1, const Complex& c2)
{
// Retorna um novo Complexo com a soma das partes
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
// Implementação do operador == para Complex
template <typename T>
bool operator==(const Complex& c1, const Complex& c2)
{
// Compara se ambas as partes são iguais
return(c1.real == c2.real && c1.imag == c2.imag);
}
task5.cpp
#include <iostream>
#include "Complex.hpp" // Inclui a definição da classe template Complex
// Teste 1: Operações básicas com Complex<int>
void testComplexInt() {
using std::cout;
using std::boolalpha;
Complex<int> c1(2, -5); // Cria um número complexo 2 - 5i
Complex<int> c2(c1); // Cria uma cópia de c1
cout << "c1 = " << c1 << '\n';
cout << "c2 = " << c2 << '\n';
// Testando o operador +
Complex<int> sum = c1 + c2;
cout << "c1 + c2 = " << sum << '\n';
// Testando o operador +=
c1 += c2;
cout << "Após c1 += c2, c1 = " << c1 << '\n';
// Testando o operador ==
cout << "c1 == c2? " << boolalpha << (c1 == c2) << '\n';
}
// Teste 2: Entrada/Saída e acesso a partes com Complex<double>
void testComplexDouble() {
using std::cin;
using std::cout;
Complex<double> num1, num2; // Cria dois números complexos com partes double
cout << "Digite os componentes real e imaginário para c1 (separados por espaço): ";
cin >> num1; // Usa o operador >> sobrecarregado
cout << "Digite os componentes real e imaginário para c2 (separados por espaço): ";
cin >> num2; // Usa o operador >> sobrecarregado
cout << "c1 inserido = " << num1 << '\n'; // Usa o operador << sobrecarregado
cout << "c2 inserido = " << num2 << '\n'; // Usa o operador << sobrecarregado
// Testando os métodos get_real() e get_imag()
const Complex<double> c3(num1); // Cria uma constante a partir de num1
cout << "Parte real de c3 = " << c3.get_real() << '\n';
cout << "Parte imaginária de c3 = " << c3.get_imag() << '\n';
}
int main() {
std::cout << "--- Teste 1: Operações com Complex<int> ---\n";
testComplexInt();
std::cout << "\n--- Teste 2: Entrada/Saída com Complex<double> ---\n";
testComplexDouble();
return 0;
}
</double></int>