Перейти к содержимому

Перегрузка операторов

Реализуйте тип для Vector2 с 2-мя float полями в отдельном модуле (Vector2.h, по надобности Vector2.cpp). Vector2 должен быть обычной структурой с публичными полями.

Перегрузите операторы (v, w это векторы, а a - float):

  • v * a
  • a * v (то же, что v * a)
  • v *= a
  • v / a
  • v /= a
  • v + w
  • v - w
  • v += w (c поддержкой синтаксиса (v += w) += w)
  • v -= w
  • Чтобы std::cout << v печатал компоненты v через запятую, в скобках
  • v[i] для получения ссылки на компонент (i равен 0 или 1).
  1. Перегрузите их как свободные inline, внутри Vector2.h;
  2. В Vector2.h оставьте только объявления перегруженных операторов, определения задайте в cpp файле;
  3. Используйте вместо свободных функций методы, объявленные внутри класса, определенные в cpp файле (там, где это возможно). Постарайтесь изменить прототипы и тела функций текстуальной заменой в вашем текстовом редакторе (search-and-replace).

В main.cpp протестируйте функционал через assert-ы.

  • Почему оператор v * w это плохая идея?

    Ответ

    Потому что есть скалярное произведение (dot product), а есть перемножение компонентов (hadamard product). Не очевидно, какую из этих операций выполнит v * w.

  • Как вызвать тот или иной оператор как функцию?

  • Что такое “fluent interface”? Как добавить его поддержку к операторам <<, +=, -= и т.п.?

    Заметка

    Fluent interface можно делать и без перегрузки операторов. Можно через методы тоже.

    Со своим типом Vector2, например, могли бы сделать так:

    struct Vector2
    {
    // ...
    public:
    Vector2& add(Vector2 v)
    {
    *this = *this + v;
    return *this;
    }
    Vector2& sub(Vector2 v)
    {
    *this = *this - v;
    return *this;
    }
    Vector2& scale(float a)
    {
    *this = *this * a;
    return *this;
    }
    Vector2& printInto(std::ostream& out)
    {
    // ...
    return *this;
    }
    };
    void usage()
    {
    Vector2 val{1, 2};
    val.add({ 1, 2 })
    .sub({ 2, 3 })
    .printInto(std::cout) // (0, 1)
    .scale(5)
    .add({ 1, 2 })
    .printInto(std::cout); // (1, 7)
    val.printInto(std::cout); // (1, 7)
    }

    Например, с печатью, дизайнеры языка могли бы сделать подобный интерфейс, вместо <<:

    std::cout
    .print(v)
    .print(std::endl)
    .print(w)
    .print(std::endl);

    Тут только минус в том, что нельзя было бы перегрузить print под свой тип, не имея дополнительного механизма перегрузки, как, например, метод внутри Vector2 с определенным именем, ожидаемым методом print (print надо вызвать перегруженную логику, используя, например, статический полиморизм через template).