2. Des classes et des objets — Solutions

Chapitre 2 : des classes et des objets — Solutions #

Série 2.1 #

Exercice 1 : classe compte bancaire #

1242.2_02.01_BankAccount

BankAccount.h

class BankAccount
{
public:
  BankAccount() = default;
  BankAccount(const BankAccount &other) = default;
  virtual ~BankAccount() = default;

  void deposit(double amount);
  void withdraw(double amount);
  void show() const;

private:
  double m_balance{0.0};
};

BankAccount.cpp

void BankAccount::deposit(double amount)
{
    if (amount > 0)
    {
      m_balance += amount;
    }
}

void BankAccount::withdraw(double amount)
{
    if (amount > 0 && m_balance >= amount)
    {
        m_balance -= amount;
    }
}

void BankAccount::show() const
{
    std::println("The amount on your bank account is : {:.2f}", m_balance);
}

main.cpp

#include <print>

#include "BankAccount.h"

int main()
{
  BankAccount myBankAccount;
  myBankAccount.show();

  // Impossible
  myBankAccount.withdraw(100);
  myBankAccount.show();
  myBankAccount.deposit(100);
  myBankAccount.show();
  // Impossible
  myBankAccount.withdraw(200);
  myBankAccount.show();
  // OK
  myBankAccount.withdraw(20);
  myBankAccount.show();

  BankAccount mySavings = myBankAccount;
  mySavings.show();
  myBankAccount.show();
}

Exercice 2 : classe Time #

1242.2_02.02_Time

Time.h

class Time
{
public:
    Time();
    Time(int hour, int minute);
    explicit Time(double realTime);
    virtual ~Time() = default;

    int getHour() const { return m_hour; }
    int getMinute() const { return m_minute; }

    // Not inlined because they involve validation logic
    void setHour(int hour);
    void setMinute(int minute);

    void show() const;

private:
    int m_hour{12};
    int m_minute{0};

    static constexpr int m_hours_per_day{24};
    static constexpr int m_minutes_per_hour{60};
};

Time.cpp

Time::Time()
{
  std::println("  -> Default constructor");
}

Time::Time(int hour, int minute) : m_hour(hour),
                                   m_minute(minute)
{
  std::println("  -> Standard constructor");
}

Time::Time(double decimalTime) : m_hour(static_cast<int>(decimalTime)),
                                 m_minute(static_cast<int>((decimalTime - m_hour) * 60))
{
  std::println("  -> Conversion constructor");
}

void Time::setHour(int hour)
{
  m_hour = (hour >= 0) ? hour % m_hours_per_day : 0;
}

void Time::setMinute(int minute)
{
  if (minute >= 0)
  {
    m_minute = minute % m_minutes_per_hour;
    setHour(m_hour + minute / m_minutes_per_hour); // carry over to hours
  }
  else
  {
    m_minute = 0;
  }
}

void Time::show() const
{
  std::println("{:02d}H{:02d}", m_hour, m_minute);
}

main.cpp

#include <print>
#include "Time.h"

int main()
{
	std::print("Time t1; ");
	Time t1;
	t1.show(); //-> t1: 12H00

	std::print("Time t2(10,9); ");
	Time t2(10, 9);
	t2.show();      //-> t2: 10H09

	std::print("Time t3(17.75); ");
	Time t3(17.75);
	t3.show();      //-> t3: 17H45

	std::print("t2.setHour(23); ");
	t2.setHour(23);
	t2.show(); //-> t2: 23H09

	std::print("t2.setMinute(-40); ");
	t2.setMinute(-40);
	t2.show();         //-> t2: 23H00

	std::print("t2.setMinute(86); ");
	t2.setMinute(86);
	t2.show();        //-> t2: 00H26

  t2.setMinute(t2.getMinute() + 5);
  std::print("t2.setMinute(t2.getMinute() + 5); ");

	return 0;
}

Exercice 3 : classe Point #

1242.2_02.03_Point

Point.h

class Point
{
public:
    explicit Point(char label, double x = 0, double y = 0);
    Point(const Point&);
    virtual ~Point() = default;

    void show() const;
    void translate(double dx, double dy);

private:
    double m_x{0.};
    double m_y{0.};
    char m_label{'?'};
};

Point.cpp

Point::Point(char label, double x, double y) : m_x(x), m_y(y), m_label(label)
{
  std::println("[Standard constructor]");
}

Point::Point(const Point &other) : m_x(other.m_x), m_y(other.m_y), m_label(other.m_label)
{
  std::println("[Copy constructor]");
}

void Point::show() const
{
  std::print("\n{} : ({:.2f},{:.2f})", m_label, m_x, m_y);
}

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

main.cpp

#include "Point.h"

#include <print>
#include <iostream>
#include <cmath>
#include <numbers>

// OPTIONAL exercise
Point **generatePolygon(int n);

int main()
{
  std::println("\nPoint p1('B');");
  Point p1('B');
  p1.show();

  std::println("\nPoint p2('A', 3, 4);");
  Point p2('A', 3, 4);
  p2.show();

  std::println("\np2.translate(-12,7);");
  p2.translate(-12, 7);
  p2.show();

  std::println("\n\nDYNAMIC ALLOCATION");
  std::println("==================");
  Point *ptrPt3 = new Point('D', 10, -5);
  ptrPt3->show();
  delete ptrPt3;
  ptrPt3 = nullptr;

  int N = 4;
  std::print("\nHow many points do you want the polygon to have ? ");
  std::cin >> N;

  if (N > 2)
  {
    Point **polygonPoints = generatePolygon(N);
    std::println("\nPOLYGON {} points:", N);
    for (int i = 0; i < N; i++)
    {
      polygonPoints[i]->show();
    }
    for (int i = 0; i < N; i++)
    {
      delete polygonPoints[i];
      polygonPoints[i] = nullptr;
    }
    delete[] polygonPoints;
    polygonPoints = nullptr;
  }
  else
  {
    std::println("that is IMPOSSIBLE !");
  }

  std::println();
}

Point **generatePolygon(int n)
{
  Point **polygonPoints = new Point *[n];
  for (int i = 0; i < n; ++i)
  {
    double angle = (static_cast<double>(i) / n) * 2 * std::numbers::pi;
    polygonPoints[i] = new Point('A' + char(i), std::cos(angle), std::sin(angle));
  }
  return polygonPoints;
}

Série 2.2 #

Exercice 1 : classe Point améliorée #

1242.2_02.04_PointStatic

Point.h

class Point
{
public:
  Point();
  // Could be a conversion constructor
  explicit Point(char name, double x=0, double y=0);
  Point(const Point &);

  virtual ~Point(){--m_counter;}

  void show() const;
  void translate(double dx, double dy);

  static int getCounter(){return m_counter;}

private:
  double m_x{0.};
  double m_y{0.};
  char m_label{'?'};

  static int m_counter;
};

Point.cpp

int Point::m_counter = 0;

Point::Point() : m_x(0.), m_y(0.), m_label('?')
{
  std::println("[Default constructor]");
  ++m_counter;
}

Point::Point(char name, double x, double y) : m_x(x), m_y(y), m_label(name)
{
  std::println("[Standard constructor]");
  ++m_counter;
}

Point::Point(const Point &other) : m_x(other.m_x), m_y(other.m_y), m_label(other.m_label)
{
  std::println("[Copy constructor]");
  ++m_counter;
}

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

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

main.cpp

#include "Point.h"

#include <print>

int main()
{
  Point *ptrPoint = nullptr;
  {
    Point p1('A');
    Point p2('B', 3, 4);
    std::println("Point count (2): {}", Point::getCounter());

    ptrPoint = new Point('D', 10, -5);
    std::println("Point count (3): {}", Point::getCounter());
  } // p1 and p2 are destroyed here

  std::println("Point count (1): {}", Point::getCounter());

  delete ptrPoint;
  ptrPoint = nullptr;
  std::println("Point count (0): {}", Point::getCounter());
}

Exercice 2 : classe Rectangle (composition de points) #

1242.2_02.05_Rectangle

Point.h

class Point
{
public:
  explicit Point(char label, double x=0, double y=0);
  Point(const Point &);

  virtual ~Point(){--m_counter;}

  void show() const;
  void translate(double dx, double dy);

  static int getCounter(){return m_counter;}

private:
  double m_x{0.};
  double m_y{0.};
  char m_label{'?'};

  static int m_counter;

  friend class Rectangle;
};

Point.cpp

int Point::m_counter = 0;

Point::Point(char label, double x, double y) : m_x(x), m_y(y), m_label(label)
{
  std::println("[ctor] Point(char, double, double)");
  ++m_counter;
}

Point::Point(const Point &other) : m_x(other.m_x), m_y(other.m_y), m_label(other.m_label)
{
  std::println("[ctor] Point(const Point&)");
  ++m_counter;
}

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

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

Rectangle.h

#include "Point.h"

class Rectangle
{
public:
  // Could be a conversion constructor
  explicit Rectangle(double xLL=0, double yLL=0, double xUR=0, double yUR=0);
  // Passing by const ref avoids constructing extra Point copies
  Rectangle(const Point &cornerLL, const Point &cornerUR);
  virtual ~Rectangle() = default;

  bool contains(const Point &) const;
  double getPerimeter() const;
  void show() const;
  void translate(double dx, double dy);

private:
  Point m_cornerLL;
  Point m_cornerUR;
};

Rectangle.cpp

// Initializer list required: Point has no default constructor
Rectangle::Rectangle(const Point &p1, const Point &p2) : m_cornerLL(p1), m_cornerUR(p2)
{
  std::println("[ctor] Rectangle(const Point&, const Point&)");
}

Rectangle::Rectangle(double xLL, double yLL, double xUR, double yUR) : m_cornerLL('A', xLL, yLL), m_cornerUR('B', xUR, yUR)
{
  std::println("[ctor] Rectangle(double, double, double, double)");
}

bool Rectangle::contains(const Point &p) const
{
  return (p.m_x >= m_cornerLL.m_x && p.m_x <= m_cornerUR.m_x &&
          p.m_y >= m_cornerLL.m_y && p.m_y <= m_cornerUR.m_y);
}

double Rectangle::getPerimeter() const
{
  double width = m_cornerUR.m_x - m_cornerLL.m_x;
  double height = m_cornerUR.m_y - m_cornerLL.m_y;
  return 2 * (width + height);
}

void Rectangle::show() const
{
  std::print("[");
  m_cornerLL.show();
  std::print("  -  ");
  m_cornerUR.show();
  std::println("]  Perimeter: {}", getPerimeter());
}

void Rectangle::translate(double dx, double dy)
{
  m_cornerLL.m_x += dx;
  m_cornerUR.m_x += dx;
  m_cornerLL.m_y += dy;
  m_cornerUR.m_y += dy;
}

main.cpp

#include "Point.h"
#include "Rectangle.h"

#include <print>

int main()
{
  std::println("*** POINTS CONSTRUCTION ***");
  Point pt1('A', 5., 5.), pt2('B', 10., 12.), ptX('X', 7., 8.);

  std::println("*** R CONSTRUCTION (from 2 points) ***");
  Rectangle R(pt1, pt2); // 2 calls to the Point copy constructor

  std::println("*** R2 CONSTRUCTION (from coordinates) ***");
  Rectangle R2(1, 2, 3, 4);
  std::println("---");

  R.show();
  std::println("Perimeter: {}", R.getPerimeter());
  std::print("is ptX ");
  ptX.show();
  std::println(" contained in R ? {}", R.contains(ptX));

  pt1.translate(3, 5);
  R.show(); // R is unchanged: constructor copied pt1 and pt2 by value

  std::println("*** COPY CONSTRUCTION ***");
  Rectangle anotherRectangle(R);
  R.translate(-10., -15.);
  std::println("R after translate(-10,-15):");
  R.show();
  std::println("anotherRectangle (independent copy):");
  anotherRectangle.show();
}

Exercice 3 : classe RectangleAgreg (agrégation de points) #

1242.2_02.06_RectangleAgreg

Point.h

class Point
{
public:
  // Could be a conversion constructor
  explicit Point(char label, double x=0, double y=0);
  Point(const Point &);
  virtual ~Point(){--m_counter;}

  void show() const;
  void translate(double dx, double dy);

  static int getCounter(){return m_counter;}

private:
  double m_x{0.};
  double m_y{0.};
  char m_label{'?'};

  static int m_counter;

  friend class RectangleAgreg;
};

Point.cpp

int Point::m_counter = 0;

Point::Point(char label, double x, double y) : m_x(x), m_y(y), m_label(label)
{
  std::println("[ctor] Point(char, double, double)");
  ++m_counter;
}

Point::Point(const Point &other) : m_x(other.m_x), m_y(other.m_y), m_label(other.m_label)
{
  std::println("[ctor] Point(const Point&)");
  ++m_counter;
}

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

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

RectangleAgreg.h

#include "Point.h"

class RectangleAgreg
{
public:
  RectangleAgreg(Point *ptrLL, Point *ptrUR);
  RectangleAgreg(const RectangleAgreg &other);

  virtual ~RectangleAgreg();

  double getPerimeter() const;
  void show() const;
  void translate(double dx, double dy);

private:
  Point *m_cornerLL{nullptr};
  Point *m_cornerUR{nullptr};
};

RectangleAgreg.cpp

// Shallow copy
RectangleAgreg::RectangleAgreg(Point *ptrLL, Point *ptrUR)
    : m_cornerLL(ptrLL), m_cornerUR(ptrUR)
{
}

// Shallow copy
RectangleAgreg::RectangleAgreg(const RectangleAgreg &other)
    : m_cornerLL(other.m_cornerLL), m_cornerUR(other.m_cornerUR)
{
}

RectangleAgreg::~RectangleAgreg()
{
  // Do not delete the points
  m_cornerLL = nullptr;
  m_cornerUR = nullptr;
}

void RectangleAgreg::show() const
{
  std::print("[");
  m_cornerLL->show();
  std::print(" - ");
  m_cornerUR->show();
  std::println("]");
}

double RectangleAgreg::getPerimeter() const
{
  return 2 * (m_cornerUR->m_x - m_cornerLL->m_x + m_cornerUR->m_y - m_cornerLL->m_y);
}

void RectangleAgreg::translate(double dx, double dy)
{
  m_cornerLL->m_x += dx;
  m_cornerUR->m_x += dx;
  m_cornerLL->m_y += dy;
  m_cornerUR->m_y += dy;
}

main.cpp

#include "Point.h"
#include "RectangleAgreg.h"

#include <print>

int main()
{
  Point pt1('A', 5, 5), pt2('B', 10, 12);

  RectangleAgreg R(&pt1, &pt2);
  std::print("R before translation: ");
  R.show();
  pt1.translate(3, 5);
  std::print("R after translating pt1: ");
  R.show();

  RectangleAgreg copyR(R);
  std::print("copyR before translation: ");
  copyR.show();
  R.translate(-5, -5);
  std::print("R after translation: ");
  R.show();
  std::print("copyR after translating R: ");
  copyR.show();
}

Exercice 4 : classe RectangleComp (composition de points avec copie en profondeur) #

1242.2_02.07_RectangleComp

Point.h

class Point
{
public:
  // Could be a conversion constructor
  explicit Point(char label, double x=0, double y=0);
  Point(const Point &);
  virtual ~Point(){--m_counter;}

  void show() const;
  void translate(double dx, double dy);

  static int getCounter(){return m_counter;}

private:
  double m_x{0.};
  double m_y{0.};
  char m_label{'?'};

  static int m_counter;

  friend class RectangleComp;
};

Point.cpp

int Point::m_counter = 0;

Point::Point(char label, double x, double y) : m_x(x), m_y(y), m_label(label)
{
  std::println("[ctor] Point(char, double, double)");
  ++m_counter;
}

Point::Point(const Point &other) : m_x(other.m_x), m_y(other.m_y), m_label(other.m_label)
{
  std::println("[ctor] Point(const Point&)");
  ++m_counter;
}

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

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

RectangleComp.h

#include "Point.h"

class RectangleComp
{
public:
  RectangleComp(Point *ptrLL, Point *ptrUR);
  RectangleComp(const RectangleComp &other);

  virtual ~RectangleComp();

  double getPerimeter() const;
  void show() const;
  void translate(double dx, double dy);

private:
  Point *m_cornerLL{nullptr};
  Point *m_cornerUR{nullptr};
};

RectangleComp.cpp

// Deep copy
RectangleComp::RectangleComp(Point *ptrLL, Point *ptrUR)
    : m_cornerLL(new Point(*ptrLL)), m_cornerUR(new Point(*ptrUR))
{
}

// Deep copy
RectangleComp::RectangleComp(const RectangleComp &other)
    : m_cornerLL(new Point(*other.m_cornerLL)), m_cornerUR(new Point(*other.m_cornerUR))
{
}

RectangleComp::~RectangleComp()
{
  // Must delete the points
  delete m_cornerLL;
  m_cornerLL = nullptr;
  delete m_cornerUR;
  m_cornerUR = nullptr;
}

void RectangleComp::show() const
{
  std::print("[");
  m_cornerLL->show();
  std::print(" - ");
  m_cornerUR->show();
  std::println("]");
}

double RectangleComp::getPerimeter() const
{
  return 2 * (m_cornerUR->m_x - m_cornerLL->m_x + m_cornerUR->m_y - m_cornerLL->m_y);
}

void RectangleComp::translate(double dx, double dy)
{
  m_cornerLL->m_x += dx;
  m_cornerUR->m_x += dx;
  m_cornerLL->m_y += dy;
  m_cornerUR->m_y += dy;
}

main.cpp

#include "Point.h"
#include "RectangleComp.h"

#include <print>

int main()
{
  Point pt1('A', 5, 5), pt2('B', 10, 12);

  RectangleComp R(&pt1, &pt2);
  std::print("R before translation: ");
  R.show();
  pt1.translate(3, 5);
  std::print("R after translating pt1: ");
  R.show();

  RectangleComp copyR(R);
  std::print("copyR before translation: ");
  copyR.show();
  R.translate(-5, -5);
  std::print("R after translation: ");
  R.show();
  std::print("copyR after translating R: ");
  copyR.show();
}