Fácil C++

Formas geométricas

Enunciado

Crie uma classe base Forma com um método virtual area() que retorna 0.0. Depois, crie as classes Retangulo e Circulo que herdam publicamente de Forma e sobrescrevem area() com override. No main, crie um std::vector<std::unique_ptr<Forma>> com uma instância de cada e imprima a área de cada uma usando polimorfismo.

Requisitos

  • Forma tem método virtual double area() const que retorna 0.0.
  • Retangulo e Circulo herdam publicamente de Forma e usam override em area().
  • O main usa std::unique_ptr<Forma> e chama area() polimorficamente.
  • A saída mostra as áreas calculadas corretamente.

Código inicial

#include <iostream>
#include <memory>
#include <vector>

// TODO: definir classes Forma, Retangulo, Circulo

int main() {
    // TODO: criar vetor de unique_ptr<Forma> e imprimir áreas
    return 0;
}

Saída esperada

Area do retangulo: 12
Area do circulo: 28.2743
Ver dica

Use 3.14159 como valor de π. Lembre-se de que area() deve ser const para poder ser chamada em objetos constantes.

Mostrar solução
#include <iostream>
#include <memory>
#include <vector>

class Forma {
public:
    virtual double area() const {
        return 0.0;
    }
    virtual ~Forma() = default; // destrutor virtual
};

class Retangulo : public Forma {
    double largura_, altura_;
public:
    Retangulo(double l, double a) : largura_(l), altura_(a) {}
    double area() const override {
        return largura_ * altura_;
    }
};

class Circulo : public Forma {
    double raio_;
public:
    Circulo(double r) : raio_(r) {}
    double area() const override {
        return 3.14159 * raio_ * raio_;
    }
};

int main() {
    std::vector<std::unique_ptr<Forma>> formas;
    formas.push_back(std::make_unique<Retangulo>(3.0, 4.0));
    formas.push_back(std::make_unique<Circulo>(3.0));

    for (const auto& f : formas) {
        std::cout << "Area: " << f->area() << '\n';
    }
    return 0;
}

Passo a passo

  1. Definimos a classe base Forma com um método virtual double area() const que retorna 0.0. O virtual permite que as classes derivadas sobrescrevam esse método.
  2. Incluímos um destrutor virtual virtual ~Forma() = default; para garantir que, ao deletar um objeto via ponteiro para Forma, o destrutor correto da classe derivada seja chamado.
  3. A classe Retangulo herda publicamente de Forma e armazena largura e altura. Seu construtor inicializa esses valores.
  4. O método area() em Retangulo é marcado com override, indicando que estamos sobrescrevendo o método virtual da base. Ele retorna o produto da largura pela altura.
  5. A classe Circulo também herda publicamente de Forma e armazena o raio. Seu método area() retorna π vezes o raio ao quadrado.
  6. No main, criamos um std::vector<std::unique_ptr<Forma>> e adicionamos um Retangulo e um Circulo usando std::make_unique.
  7. Iteramos sobre o vetor e chamamos f->area(). Devido ao polimorfismo, a versão correta de area() é chamada para cada objeto.
  8. A saída mostra as áreas calculadas: 12 para o retângulo (34) e aproximadamente 28.2743 para o círculo (π3²).

Por que funciona

O polimorfismo dinâmico é possível porque area() é virtual na classe base. Quando chamamos area() através de um ponteiro ou referência para Forma, o programa consulta a tabela virtual do objeto e invoca a implementação da classe derivada correspondente. O uso de std::unique_ptr<Forma> garante que os objetos sejam destruídos corretamente, desde que o destrutor seja virtual.

Erros comuns

  • Esquecer virtual na classe base: Se area() não for virtual, a chamada f->area() sempre usará a versão de Forma, retornando 0.0. Corrija declarando virtual double area() const.
  • Não usar override: Se a assinatura não coincidir exatamente (por exemplo, faltar const), o método não sobrescreve e o compilador pode não avisar. Use override para que o compilador verifique.
  • Destrutor não virtual: Se ~Forma() não for virtual, ao deletar um Retangulo via unique_ptr<Forma>, apenas o destrutor de Forma seria chamado, causando vazamento de recursos da parte derivada. Declare virtual ~Forma() = default;.
  • Esquecer const no método: Se area() não for const, não poderá ser chamado em objetos constantes ou referências constantes, como no loop for (const auto& f : formas). Declare double area() const override.

Outra forma de resolver

Poderia-se usar std::variant ou herança múltipla, mas para polimorfismo clássico a herança com métodos virtuais é a abordagem idiomática. Uma alternativa é usar std::function para armazenar callables, mas isso foge do conceito de herança.

Saída esperada

Area do retangulo: 12
Area do circulo: 28.2743