Compound Assignment Operators

Tip

If a class defines +, -, *, and /, then it is a good practice to also define the corresponding compound assignment operators, +=, -=, *=, and /=.

The compound assignment operators provide a shorthand, e.g.:

a += b

is the same as

a = a + b

So when we overload the operator, we not only need to do the arithmetic operation, but we need to return a reference to the object we are operating on.

Back to our vector class, this means that our overloaded operator += will have the form:

Vector2d& operator+= (const Vector2d& vec) {
    x += vec.x;
    y += vec.y;
    return *this;
}

(since this is a pointer, we dereference it and return it via reference).

Here’s a full implementation (adding to our previous vector2d.H:

Listing 71 updated vector2d.H
#ifndef VECTOR_2D_H
#define VECTOR_2D_H

#include <iostream>
#include <cmath>

class Vector2d {

private:

    // our member data

    double x;
    double y;

public:

    // default constructor

    Vector2d()
        : x{0.0}, y{0.0}
    {}

    // another constructor

    Vector2d(double _x, double _y)
        : x{_x}, y{_y}
    {}

    // the destructor

    ~Vector2d() {}

    // copy and assignment constructors -- we don't need to define them, but we
    // explicitly say to use the default

    //Vector2d(const Vector2d& vec) = default;
    Vector2d(const Vector2d& vec)
        : x(vec.x), y(vec.y) {}

    // assignment here means that we want to set our current class's values to
    // those to the right of the = -- that's the Vector2d coming in through the
    // argument list

    //Vector2d& operator= (const Vector2d& vec) = default;
    Vector2d& operator= (const Vector2d& vec) {
        x = vec.x;
        y = vec.y;
        return *this;
    }


    // setters

    inline void set_x(double _x) {x = _x;}

    inline void set_y(double _y) {y = _y;}

    // operators

    // add two vectors

    Vector2d operator+ (const Vector2d& vec) {
        Vector2d vec_out(x + vec.x, y + vec.y);
        return vec_out;
    }

    // subtract two vectors

    Vector2d operator- (const Vector2d& vec) {
        Vector2d vec_out(x - vec.x, y - vec.y);
        return vec_out;
    }

    // unary minus

    Vector2d operator- () {
        Vector2d vec_out(-x, -y);
        return vec_out;
    }

    // multiplication

    // vec * a

    Vector2d operator* (double a) {
        Vector2d vec_out(a * x, a * y);
        return vec_out;
    }

    // a * vec -- needs to be a friend

    friend Vector2d operator* (double a, const Vector2d& vec);

    // division by a scalar

    Vector2d operator/ (double a) {
        Vector2d vec_out(x / a, y / a);
        return vec_out;
    }

    // compound operators

    Vector2d& operator+= (const Vector2d& vec) {
        x += vec.x;
        y += vec.y;
        return *this;
    }

    Vector2d& operator-= (const Vector2d& vec) {
        x -= vec.x;
        y -= vec.y;
        return *this;
    }

    Vector2d& operator*= (double a) {
        x *= a;
        y *= a;
        return *this;
    }

    Vector2d& operator/= (double a) {
        x /= a;
        y /= a;
        return *this;
    }

    // comparison operators

    bool operator== (const Vector2d& vec) const {
        return x == vec.x && y == vec.y;
    }

    bool operator!= (const Vector2d& vec) const {
        return x != vec.x || y != vec.y;
    }

    double abs() {
        return std::sqrt(x * x + y * y);
    }

    double dot(const Vector2d& vec) {
        return x * vec.x + y * vec.y;
    }

    // << and >> are not class members, but need access to the member data

    friend std::ostream& operator<< (std::ostream& os, const Vector2d& v);
    friend std::istream& operator>> (std::istream& is, Vector2d& v);
};

std::ostream& operator<< (std::ostream& os, const Vector2d& vec)
{
    os << "(" << vec.x << ", " << vec.y << ")";
    return os;
}

std::istream& operator>> (std::istream& is, Vector2d& vec)
{
    is >> vec.x >> vec.y;
    return is;
}

Vector2d operator*(double a, const Vector2d& vec) {
        Vector2d vec_out(a * vec.x, a * vec.y);
        return vec_out;
}

#endif

and here’s a driver:

Listing 72 updated compound_test_vectors.cpp
#include <iostream>

#include "vector2d.H"

int main() {

    // vectors displancing N, E, S, W

    Vector2d v_north(0.0, 1.0);
    Vector2d v_east(1.0, 0.0);
    Vector2d v_south(0.0, -1.0);
    Vector2d v_west(-1.0, 0.0);

    // test compound addition -- if we sum up the four vectors above,
    // we should get zero

    Vector2d v1;
    v1 += v_north;
    v1 += v_east;

    std::cout << "compound addition (intermediate result): " << v1 << std::endl;
    v1 += v_south;
    v1 += v_west;

    std::cout << "compound addition (final result): " << v1 << std::endl;

    // test compound subtraction

    Vector2d v2(1.0, 1.0);
    v2 -= v_north;

    std::cout << "compound subtraction: " << v2 << std::endl;

    // test compound multiplication

    Vector2d v3(2.0, 4.0);
    v3 *= 4;

    std::cout << "compound multiplication: " << v3 << std::endl;

    // test compound division

    Vector2d v4(1.0, -5.0);
    v4 /= 2;

    std::cout << "compound division: " << v4 << std::endl;


}