DMarlon Studio

Lección 20 · 20 de 20

Proyecto: editor de formas · C++

Proyecto final del curso avanzado de C++: un editor de formas con clases, polimorfismo, unique_ptr y algoritmos de la STL, listo para F9 en DMarlon Studio.

C++ · Proyecto: editor de formas en DMarlon Studio: el código del ejemplo y su salida en la terminal

Este proyecto reúne lo visto en el curso. Shape es la interfaz común: toda forma tiene un nombre y un area() virtual puro, y la base lleva un destructor virtual porque las formas se destruirán a través de punteros a Shape. Circle y Rect heredan de ella, construyen la parte común con Shape(n) en su lista de inicialización y calculan el área cada una a su manera.

El lienzo es un std::vector<std::unique_ptr<Shape>>: guarda formas de tipos distintos y es su dueño. Cada std::make_unique<Rect>(...) crea una forma y se la entrega. En todo el programa no hay ni un delete, y no se pierde memoria.

Las operaciones del editor son algoritmos de la STL con lambdas. remove_if con erase quita las formas de menos de una unidad de área (el punto «dot», de 0,2) y, al destruirse su unique_ptr, la forma se libera sola. std::sort ordena de mayor a menor área llamando a area(), que en cada forma ejecuta su propia versión. Las lambdas reciben const auto& porque un unique_ptr no se puede copiar.

El último bucle escribe cada forma y suma el área total: el banner (15), el logotipo (π · 1,5² ≈ 7,07) y el botón (3). Para ampliarlo, añada más tipos de forma: el resto del programa no cambia. En DMarlon Studio puede ejecutarlo con el destino Consola (F9) y, después, llevar estas mismas clases a una ventana de escritorio con FLTK.

Código · main.cpp

#include <algorithm>#include <iostream>#include <memory>#include <vector>struct Shape {                     // the interface every shape on the canvas follows    const char* name;    explicit Shape(const char* n) : name(n) {}    virtual ~Shape() = default;    virtual double area() const = 0;};struct Circle : Shape {    double r;    Circle(const char* n, double r) : Shape(n), r(r) {}    double area() const override { return 3.14159 * r * r; }};struct Rect : Shape {    double w, h;    Rect(const char* n, double w, double h) : Shape(n), w(w), h(h) {}    double area() const override { return w * h; }};int main() {    std::vector<std::unique_ptr<Shape>> canvas;      // the canvas owns its shapes    canvas.push_back(std::make_unique<Rect>("banner", 6, 2.5));    canvas.push_back(std::make_unique<Circle>("logo", 1.5));    canvas.push_back(std::make_unique<Rect>("dot", 0.5, 0.4));    canvas.push_back(std::make_unique<Rect>("button", 3, 1));    canvas.erase(std::remove_if(canvas.begin(), canvas.end(), [](const auto& s) { return s->area() < 1; }), canvas.end());    std::sort(canvas.begin(), canvas.end(), [](const auto& a, const auto& b) { return a->area() > b->area(); });    double total = 0;    for (const auto& s : canvas) { total += s->area(); std::cout << s->name << ": " << s->area() << "\n"; }    std::cout << canvas.size() << " shapes, total area " << total << "\n";}

Salida · c++ -std=c++17 main.cpp -o main && ./main

banner: 15
logo: 7.06858
button: 3
3 shapes, total area 25.0686

Para recordar

  • Una interfaz abstracta y varias clases derivadas modelan las formas.
  • std::vector<std::unique_ptr<Base>> guarda objetos polimórficos y los libera solo.
  • remove_if + erase y sort con lambdas hacen el trabajo del editor.
  • Añadir una forma nueva no obliga a cambiar el código que las usa.
  • Con RAII y punteros inteligentes no hace falta ningún delete.

Ejercicio

Añada una clase Triangle (base y altura) con su área, ponga en el lienzo un triángulo «arrow» de 4 × 2,5 y, al final, escriba cuántas formas de cada tipo hay usando un std::map<std::string, int> y un método virtual kind().