5. Transtypage et RTTI — Solutions

Chapitre 5 : opérateurs de transtypage et RTTI — Solutions #

Série 5.1 #

Exercice 1 : RTTI et dynamic_cast #

1242.2_05.01_RTTIAndDynamicCast

Point.h

#include <string>

class Point
{
public:
    Point(double x = 0., double y = 0., std::string name = "Point");
    Point(const Point& other);
    ~Point();

    void show() const;

    double getX() const { return m_x; }
    double getY() const { return m_y; }

    bool operator==(const Point& p) const { return m_x == p.m_x && m_y == p.m_y; }

    void translate(double dx, double dy);
    void translate(const Point& other);

    static int counter;

private:
    double m_x{0.};
    double m_y{0.};
    std::string m_name;
};

Point.cpp

int Point::counter = 0;

Point::Point(double x, double y, std::string name)
    : m_x(x), m_y(y), m_name(name)
{
  counter++;
}

Point::Point(const Point &other)
    : m_x(other.m_x), m_y(other.m_y), m_name(other.m_name)
{
  counter++;
}

Point::~Point()
{
  counter--;
}

void Point::show() const
{
  std::print("{} : ({}, {})", m_name, m_x, m_y);
}

void Point::translate(double dx, double dy)
{
  m_x += dx;
  m_y += dy;
}

void Point::translate(const Point &other)
{
  translate(other.m_x, other.m_y);
}

Figure.h

#include "Point.h"

class Figure
{
public:
    explicit Figure(const Point& pos) : m_pos(pos) {}
    virtual ~Figure() = default;

    virtual void show() const = 0;

    Point getPos() const { return m_pos; }
    virtual void translate(const Point& shift);

protected:
    Point m_pos{0., 0.};
};

Figure.cpp

void Figure::show() const
{
    m_pos.show();
}

void Figure::translate(const Point& shift)
{
    m_pos.translate(shift);
}

Circle.h

#include "Figure.h"

class Circle : public Figure
{
public:
    Circle(const Point& pos, double radius) : Figure(pos), m_radius(radius) {}
    ~Circle() override = default;

    double getRadius() const { return m_radius; }
    void show() const override;

private:
    double m_radius{0.};
};

Circle.cpp

void Circle::show() const
{
    std::print("Circle: ");
    Figure::show();
    std::println(", radius={}", m_radius);
}

Rectangle.h

#include "Figure.h"

class Rectangle : public Figure
{
public:
    Rectangle(const Point& pos, double height, double width)
        : Figure(pos), m_height(height), m_width(width) {}
    ~Rectangle() override = default;

    void show() const override;

private:
    double m_height{0.};
    double m_width{0.};
};

Rectangle.cpp

void Rectangle::show() const
{
    std::print("Rectangle: ");
    Figure::show();
    std::println(", w={}, h={}", m_width, m_height);
}

Triangle.h

#include "Figure.h"

class Triangle : public Figure
{
public:
    Triangle(const Point& pos, Point pos2, Point pos3)
        : Figure(pos), m_pos2(pos2), m_pos3(pos3) {}
    ~Triangle() override = default;

    void show() const override;
    void translate(const Point& shift) override;

private:
    Point m_pos2{0., 0.};
    Point m_pos3{0., 0.};
};

Triangle.cpp

void Triangle::show() const
{
    std::print("Triangle: ");
    Figure::show();
    std::print(", ");
    m_pos2.show();
    std::print(", ");
    m_pos3.show();
    std::println("");
}

void Triangle::translate(const Point& shift)
{
    Figure::translate(shift);
    m_pos2.translate(shift);
    m_pos3.translate(shift);
}

compareShapes() (dans main.cpp)

// Returns true if both figures are the same type and share the same position.
// typeid checks for exact type match — a Circle and a derived SpecialCircle would not match.
bool compareShapes(Figure *fig1, Figure *fig2)
{
  std::println("\n-- Comparing {} with {}", typeid(*fig1).name(), typeid(*fig2).name());

  if (typeid(*fig1) != typeid(*fig2))
  {
    std::println("   typeid differ");
    return false;
  }
  std::println("   typeid match");

  bool samePos = fig1->getPos() == fig2->getPos();
  std::println("   position: {}", samePos ? "same" : "different");
  return samePos;
}

main.cpp

int main()
{
  std::println("========== Exercise 1 : typeid ==========");

  Rectangle r1(Point(1, 2), 4.0, 10.0);
  Rectangle r2(Point(1, 2), 4.0, 10.0);
  Rectangle r3(Point(10, 20), 10.0, 20.0);
  Circle c1(Point(1.1, 5.3), 5.0);

  std::println("\n### r1 and r2");
  std::println("same: {}", compareShapes(&r1, &r2));

  std::println("\n### r1 and r3");
  std::println("same: {}", compareShapes(&r1, &r3));

  std::println("\n### r1 and c1");
  std::println("same: {}", compareShapes(&r1, &c1));

  std::println("\n========== Exercise 2 : dynamic_cast ==========");

  Figure *myShapes[3];
  myShapes[0] = new Circle(Point(1.1, 5.3), 5.0);
  myShapes[1] = new Triangle(Point(2, 2), Point(10, 3), Point(-1, -1));
  myShapes[2] = new Rectangle(Point(4, 2), 4.0, 10.0);

  std::println("\n-- Shapes with radius when applicable --");
  for (auto shapePtr : myShapes)
  {
    shapePtr->show();
  }

  // Use auto & to modify the pointers in the array
  for (auto &shape : myShapes)
  {
    delete shape;
    shape = nullptr;
  }

  return 0;
}

Série 5.2 #

Exercice 1 : copie par dynamic_cast #

1242.2_05.02_CloneDynamicCast

Hiérarchie Point/Figure/Circle/Rectangle/Triangle identique à la série 5.1.

main.cpp

int main()
{
    Figure* myShapes[3];
    myShapes[0] = new Circle(Point(1.1, 5.3), 5.0);
    myShapes[1] = new Triangle(Point(2, 2), Point(10, 3), Point(-1, -1));
    myShapes[2] = new Rectangle(Point(4, 2), 4.0, 10.0);

    Figure* myShapesCopy[3];

    // dynamic_cast identifies the concrete type so we can call the right copy constructor.
    // Without clone(), there is no polymorphic way to copy through a base pointer.
    for (int i = 0; i < 3; ++i) {
        if (auto c = dynamic_cast<Circle*>(myShapes[i])) {
            myShapesCopy[i] = new Circle(*c);
        } else if (auto t = dynamic_cast<Triangle*>(myShapes[i])) {
            myShapesCopy[i] = new Triangle(*t);
        } else if (auto r = dynamic_cast<Rectangle*>(myShapes[i])) {
            myShapesCopy[i] = new Rectangle(*r);
        }
    }

    for (auto &shape : myShapes) {
        delete shape;
        shape = nullptr;
    }

    std::println("\n-- Copied shapes --");
    for (auto shape : myShapesCopy) {
        shape->show();
    }

    for (auto &shape : myShapesCopy) {
        delete shape;
        shape = nullptr;
    }

    return 0;
}

Exercice 2 : copie via clone() virtuel #

1242.2_05.03_CloneVirtual

Point.h/cpp et les .cpp de la hiérarchie sont identiques à la série 5.1.

Figure.h (ajout de clone() pure virtuelle)

#include "Point.h"

class Figure
{
public:
    explicit Figure(const Point& pos) : m_pos(pos) {}
    virtual ~Figure() = default;

    virtual void show() const = 0;

    // Returns a heap-allocated copy of this object with the correct derived type.
    // Avoids the need for dynamic_cast when copying a heterogeneous collection.
    virtual Figure* clone() const = 0;

    Point getPos() const { return m_pos; }
    virtual void translate(const Point& shift);

protected:
    Point m_pos{0., 0.};
};

Circle.h

#include "Figure.h"

class Circle : public Figure
{
public:
    Circle(const Point& pos, double radius) : Figure(pos), m_radius(radius) {}
    ~Circle() override = default;

    Figure* clone() const override { return new Circle(*this); }

    double getRadius() const { return m_radius; }
    void show() const override;

private:
    double m_radius{0.};
};

Rectangle.h

#include "Figure.h"

class Rectangle : public Figure
{
public:
    Rectangle(const Point& pos, double height, double width)
        : Figure(pos), m_height(height), m_width(width) {}
    ~Rectangle() override = default;

    Figure* clone() const override { return new Rectangle(*this); }

    void show() const override;

private:
    double m_height{0.};
    double m_width{0.};
};

Triangle.h

#include "Figure.h"

class Triangle : public Figure
{
public:
    Triangle(const Point& pos, Point pos2, Point pos3)
        : Figure(pos), m_pos2(pos2), m_pos3(pos3) {}
    ~Triangle() override = default;

    Figure* clone() const override { return new Triangle(*this); }

    void show() const override;
    void translate(const Point& shift) override;

private:
    Point m_pos2{0., 0.};
    Point m_pos3{0., 0.};
};

main.cpp

int main()
{
    Figure* myShapes[3];
    myShapes[0] = new Circle(Point(1.1, 5.3), 5.0);
    myShapes[1] = new Triangle(Point(2, 2), Point(10, 3), Point(-1, -1));
    myShapes[2] = new Rectangle(Point(4, 2), 4.0, 10.0);

    Figure* myShapesCopy[3];

    // clone() dispatches to the correct derived type — no dynamic_cast needed.
    for (int i = 0; i < 3; ++i) {
        myShapesCopy[i] = myShapes[i]->clone();
    }

    for (auto &shape : myShapes) {
        delete shape;
        shape = nullptr;
    }

    std::println("\n-- Copied shapes --");
    for (auto shape : myShapesCopy) {
        shape->show();
    }

    for (auto &shape : myShapesCopy) {
        delete shape;
        shape = nullptr;
    }

    return 0;
}