Overview
This lab builds a small Vector2 value type that overloads +, -, ==, != and << so it behaves in ordinary expressions the way a built-in float or int already does. Starting from a Vector2 with just two public members, x and y, you will add member operators that let two vectors be added, subtracted and compared with the same symbols used for numbers, then a free function operator that lets a Vector2 be printed straight into std::cout. By the end, a small program builds several vectors, combines and compares them, and sums a list of them in a loop, with no named function such as add(a, b) or equals(a, b) written anywhere.
Learning Objectives
By the end of this lab you will be able to:
- Declare and implement a binary operator such as operator+ as an ordinary member function that returns a new object
- Declare and implement comparison operators operator== and operator!= as member functions returning bool
- Explain why an operator whose left-hand operand is not the class itself, such as operator<< with std::cout, cannot be a member function
- Implement operator<< as a free function that takes both operands and returns std::ostream& so calls can chain
- Use overloaded operators in ordinary expressions, including inside a range-based for loop, exactly as they would be used with a built-in type
Concept
Without operator overloading, combining two objects of a custom type needs a named function. adding two Vector2s would mean writing something like add(a, b), and every call site involving Vector2 would read differently from the plain a + b that already works for two floats or ints. Operator overloading lets a class define what an existing operator symbol means for its own type, so Vector2 can take part in the same +, -, == and << expressions that built-in numeric types already support.
Most operators overload as ordinary member functions. When the left-hand operand of an expression is always an instance of the class, such as a + b where a is a Vector2, operator+ can be declared and implemented on Vector2 the same way start() or any other method would be. writing a + b is really just a more readable way of writing a.operator+(b). operator+ and operator- each build and return a new Vector2 from x and y, while operator== and operator!= return a bool built from comparing both members.
operator<< cannot be a member of Vector2. In std::cout << myVector, the left-hand operand of << is the std::ostream, and C++ looks up which operator<< to call based on that left-hand type first, not Vector2. Overloading an operator for a type that was not written by the same class, here std::ostream, means declaring it as a free function that takes both operands as ordinary parameters, operator<<(std::ostream& stream, const Vector2& vector), and returns the stream by reference so multiple << calls can chain in a single expression.
Tasks
Task 1 - Member operators: operator+, operator-, operator==, operator!=
Open Vector2.h and Vector2.cpp and complete the constructor and the four member operators. Each one behaves like an ordinary method, the left-hand operand of the expression is always the Vector2 the method runs on.
- In
Vector2.cpp, implement both constructors soxandyare set from their arguments (the default constructor sets both to0.0f). - Implement
operator+to return a newVector2whosexandyare the sum ofthisandother's matching members. - Implement
operator-the same way, using subtraction instead of addition. - Implement
operator==to returntrueonly when bothxandymatchother's. - Implement
operator!=by reusingoperator==,return !(*this == other);, rather than repeating the comparison.
Success condition: a small test in main(), Vector2 a(2.0f, 3.0f); Vector2 b(5.0f, -1.0f); Vector2 c = a + b;, compiles, and a == a evaluates to true while a == b evaluates to false.
#pragma once
#include <iostream>
// Vector2 is a small value type. Without operator overloading, combining
// two of them would need named functions - add(a, b), equals(a, b) - and
// every call site would read differently from the plain arithmetic a
// built-in float or int supports.
class Vector2
{
public:
float x;
float y;
Vector2();
Vector2(float x, float y);
// TODO: declare operator+(const Vector2& other) const returning Vector2
// TODO: declare operator-(const Vector2& other) const returning Vector2
// TODO: declare operator==(const Vector2& other) const returning bool
// TODO: declare operator!=(const Vector2& other) const returning bool
};
#include "Vector2.h"
Vector2::Vector2()
{
// TODO: set x and y to 0.0f
}
Vector2::Vector2(float x, float y)
{
// TODO: set this->x and this->y from the constructor arguments
}
Vector2 Vector2::operator+(const Vector2& other) const
{
// TODO: return a new Vector2 built from x + other.x and y + other.y
}
Vector2 Vector2::operator-(const Vector2& other) const
{
// TODO: return a new Vector2 built from x - other.x and y - other.y
}
bool Vector2::operator==(const Vector2& other) const
{
// TODO: return true only if x and y both match other's
}
bool Vector2::operator!=(const Vector2& other) const
{
// TODO: return !(*this == other), reusing operator== above
}
Task 2 - operator<< as a free function
Open Vector2.h again and add one declaration below the class, then open Vector2.cpp and add its definition at the bottom of the file.
- In
Vector2.h, below the closing};of the class, declarestd::ostream& operator<<(std::ostream& stream, const Vector2& vector);as a free function, not a class member. - In
Vector2.cpp, define it to write(x, y)tostreamusingvector.xandvector.y, thenreturn stream;so further<<calls can chain onto the same expression.
Success condition: std::cout << "a = " << a << std::endl; compiles and prints a = (2, 3) for Vector2 a(2.0f, 3.0f);, with no changes needed to Vector2's member list.
// Add below the closing "};" of the Vector2 class from Task 1.
// operator<< cannot be a Vector2 member: in an expression like
// "std::cout << myVector", the left-hand operand is a std::ostream, not a
// Vector2, and C++ always looks up an operator on its left operand's type
// first. Declaring it as a free function taking both sides as parameters
// is the only way to overload it for a type you did not write std::ostream
// yourself.
// TODO: declare operator<<(std::ostream& stream, const Vector2& vector)
// returning std::ostream&
// Add at the bottom of Vector2.cpp, below operator!= from Task 1.
std::ostream& operator<<(std::ostream& stream, const Vector2& vector)
{
// TODO: write "(" << vector.x << ", " << vector.y << ")" to stream,
// then return stream so further << calls can chain
}
Task 3 - Using the overloaded operators
Open OperatorOverloadingDemo.cpp and complete main() so it exercises every operator from Task 1 and Task 2.
- Construct
Vector2 a(2.0f, 3.0f);andVector2 b(5.0f, -1.0f);, then print both usingoperator<<. - Print
a + banda - b, each wrapped in parentheses inside the<<expression. - Print
std::boolalphaonce, then print the results ofa == b,a == aanda != b. - Build
std::vector<Vector2> steps = { Vector2(1.0f, 0.0f), Vector2(0.0f, 1.0f), Vector2(2.0f, 2.0f) };and aVector2 total(0.0f, 0.0f);, then sumstepsintototalusing a range-basedforloop and plain+, the same way you would sum astd::vector<float>. - Print
total.
Success condition: the program prints a, b, their sum and difference, three boolean comparisons, and a final total displacement line, with no named function such as add, subtract or equals written anywhere in this file.
#include <iostream>
#include <vector>
#include "Vector2.h"
int main()
{
// TODO: construct Vector2 a(2.0f, 3.0f) and Vector2 b(5.0f, -1.0f),
// then print "a = " << a and "b = " << b, each followed by std::endl
// TODO: print "a + b = " << (a + b) and "a - b = " << (a - b)
// TODO: print std::boolalpha, then "a == b: " << (a == b),
// "a == a: " << (a == a) and "a != b: " << (a != b)
std::cout << "---" << std::endl;
// TODO: build std::vector<Vector2> steps with three Vector2s, sum them
// into a Vector2 total(0.0f, 0.0f) using a range-based for loop and
// plain +, then print "total displacement = " << total
}
Solution
Try the tasks above before revealing the solution.