DMarlon Studio

Lesson 20 · 20 of 20

Project: shape editor · C++

Final project of the advanced C++ course: a shape editor with classes, polymorphism, unique_ptr and STL algorithms, ready for F9 in DMarlon Studio.

C++ · Project: shape editor in DMarlon Studio: the example's code and its output in the terminal

This project brings the course together. Shape is the common interface: every shape has a name and a pure virtual area(), and the base has a virtual destructor because shapes will be destroyed through Shape pointers. Circle and Rect derive from it, build the shared part with Shape(n) in their initializer lists and each work out the area in their own way.

The canvas is a std::vector<std::unique_ptr<Shape>>: it holds shapes of different types and owns them. Each std::make_unique<Rect>(...) creates a shape and hands it over. There is not a single delete in the program, and no memory leaks.

The editor's operations are STL algorithms with lambdas. remove_if with erase drops the shapes with an area under one unit (the "dot", at 0.2) and, as its unique_ptr is destroyed, the shape frees itself. std::sort orders them from largest to smallest by calling area(), which runs each shape's own version. The lambdas take const auto& because a unique_ptr cannot be copied.

The final loop prints each shape and adds up the total area: the banner (15), the logo (π · 1.5² ≈ 7.07) and the button (3). To extend it, add more kinds of shape: the rest of the program stays the same. In DMarlon Studio you can run it with the Console target (F9) and then bring these same classes into an FLTK desktop window.

Code · 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";}

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

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

Key points

  • An abstract interface and several derived classes model the shapes.
  • std::vector<std::unique_ptr<Base>> holds polymorphic objects and frees them automatically.
  • remove_if + erase and sort with lambdas do the editor's work.
  • Adding a new shape does not require changing the code that uses them.
  • With RAII and smart pointers there is no need for any delete.

Exercise

Add a Triangle class (base and height) with its area, put an "arrow" triangle of 4 × 2.5 on the canvas and, at the end, print how many shapes of each kind there are using a std::map<std::string, int> and a virtual kind() method.