When I was working on my math library (well, actually I'm always working on my math library), I found it helpful to look at as many examples (good or bad) as possible. So here's an older version of my Vector3 class:
// --------------------------------------------------------------------------------------// FILE: Vector3.h// AUTHOR: Jesse Krebs// --------------------------------------------------------------------------------------#ifndef VECTOR3_H#define VECTOR3_H#include "MathUtil.h"class Vector3{public: Vector3() {} Vector3(const Vector3& v) {Set(v);} Vector3(float v[]) {Set(v);} Vector3(float x, float y, float z) {Set(x, y, z);} void SetX(float x) {m_x = x;} void SetY(float y) {m_y = y;} void SetZ(float z) {m_z = z;} void Zero() {Set(0.0f, 0.0f, 0.0f);} void Set(const Vector3& v) {*this = v;} void Set(float v[]) {assert(v); m_x = v[0]; m_y = v[1]; m_z = v[2];} void Set(float x, float y, float z) {m_x = x; m_y = y; m_z = z;} inline float operator[](int i) const; inline bool operator==(const Vector3& v) const; // No epsilon inline bool operator!=(const Vector3& v) const; // No epsilon inline Vector3 operator-() const; inline Vector3& operator=(const Vector3& v); inline Vector3& operator+=(const Vector3& v); inline Vector3& operator-=(const Vector3& v); inline Vector3& operator*=(float scale); inline Vector3& operator/=(float scale); inline friend Vector3 operator+(const Vector3& v1, const Vector3& v2); inline friend Vector3 operator-(const Vector3& v1, const Vector3& v2); inline friend Vector3 operator*(float scale, const Vector3& v); inline friend Vector3 operator*(const Vector3& v, float scale); inline friend Vector3 operator/(const Vector3& v, float scale); inline void Minimize(const Vector3& v); inline void Maximize(const Vector3& v); inline float Normalize(); inline float Length() const; inline float LengthSquared() const; inline float Dot(const Vector3& v) const; inline Vector3 Cross(const Vector3& v) const; inline Vector3 UnitCross(const Vector3& v) const; inline Vector3 Absolute() const; inline Vector3 Reflect(const Vector3& v) const; private: float m_x; float m_y; float m_z;};// --------------------------------------------------------------------------------------inline float Vector3::operator[](int i) const{ assert(i >= 0 && i < 3); return (&m_x);}// --------------------------------------------------------------------------------------inline bool Vector3::operator==(const Vector3& v) const{ return m_x == v.m_x && m_y == v.m_y && m_z == v.m_z;}// --------------------------------------------------------------------------------------inline bool Vector3::operator!=(const Vector3& v) const{ return m_x != v.m_x || m_y != v.m_y || m_z != v.m_z;}// --------------------------------------------------------------------------------------inline Vector3 Vector3::operator-() const{ return Vector3(-m_x, -m_y, -m_z);}// --------------------------------------------------------------------------------------inline Vector3& Vector3::operator=(const Vector3& v){ m_x = v.m_x; m_y = v.m_y; m_z = v.m_z; return *this;}// --------------------------------------------------------------------------------------inline Vector3& Vector3::operator+=(const Vector3& v){ m_x += v.m_x; m_y += v.m_y; m_z += v.m_z; return *this;}// --------------------------------------------------------------------------------------inline Vector3& Vector3::operator-=(const Vector3& v){ m_x -= v.m_x; m_y -= v.m_y; m_z -= v.m_z; return *this;}// --------------------------------------------------------------------------------------inline Vector3& Vector3::operator*=(float scale){ m_x *= scale; m_y *= scale; m_z *= scale; return *this;}// --------------------------------------------------------------------------------------inline Vector3& Vector3::operator/=(float scale){ assert(fabsf(scale) > Math::EPSILON); float inverse = 1.0f / scale; m_x *= inverse; m_y *= inverse; m_z *= inverse; return *this;}// --------------------------------------------------------------------------------------inline Vector3 operator+(const Vector3& v1, const Vector3& v2){ return Vector3(v1.m_x + v2.m_x, v1.m_y + v2.m_y, v1.m_z + v2.m_z);}// --------------------------------------------------------------------------------------inline Vector3 operator-(const Vector3& v1, const Vector3& v2){ return Vector3(v1.m_x - v2.m_x, v1.m_y - v2.m_y, v1.m_z - v2.m_z);}// --------------------------------------------------------------------------------------inline Vector3 operator*(float scale, const Vector3& v){ return Vector3(scale * v.m_x, scale * v.m_y, scale * v.m_z);}// --------------------------------------------------------------------------------------inline Vector3 operator*(const Vector3& v, float scale){ return Vector3(v.m_x * scale, v.m_y * scale, v.m_z * scale);}// --------------------------------------------------------------------------------------inline Vector3 operator/(const Vector3& v, float scale){ assert(fabsf(scale) > Math::EPSILON); float inverse = 1.0f / scale; return Vector3(v.m_x * inverse, v.m_y * inverse, v.m_z * inverse);}// --------------------------------------------------------------------------------------void Vector3::Minimize(const Vector3& v){ Set(Math::Min(m_x, v.m_x), Math::Min(m_y, v.m_y), Math::Min(m_z, v.m_z));}// --------------------------------------------------------------------------------------void Vector3::Maximize(const Vector3& v){ Set(Math::Max(m_x, v.m_x), Math::Max(m_y, v.m_y), Math::Max(m_z, v.m_z));}// --------------------------------------------------------------------------------------inline float Vector3::Normalize(){ float length = Length(); *this /= length; return length;}// --------------------------------------------------------------------------------------inline float Vector3::Length() const{ return Math::Sqrt(m_x * m_x + m_y * m_y + m_z * m_z);}// --------------------------------------------------------------------------------------inline float Vector3::LengthSquared() const{ return m_x * m_x + m_y * m_y + m_z * m_z;}// --------------------------------------------------------------------------------------inline float Vector3::Dot(const Vector3& v) const{ return m_x * v.m_x + m_y * v.m_y + m_z * v.m_z;}// --------------------------------------------------------------------------------------inline Vector3 Vector3::Cross(const Vector3& v) const{ return Vector3(m_y * v.m_z - m_z * v.m_y, m_z * v.m_x - m_x * v.m_z, m_x * v.m_y - m_y * v.m_x);}// --------------------------------------------------------------------------------------inline Vector3 Vector3::UnitCross(const Vector3& v) const{ Vector3 cross = Cross(v); cross.Normalize(); return cross;}// --------------------------------------------------------------------------------------inline Vector3 Vector3::Absolute() const{ return Vector3(fabsf(m_x), fabsf(m_y), fabsf(m_z));}// --------------------------------------------------------------------------------------inline Vector3 Vector3::Reflect(const Vector3& v) const{ return v - 2.0f * Dot(v) * *this;}// --------------------------------------------------------------------------------------#endif
I deleted some stuff for this post, so I may have messed something up.
Also, I don't think you have to put the 'inlines' in the class declaration, but I didn't know that at the time.
There are a few things here - public vs. private data, member vs. friend dot and cross functions - that people differ in opinion about. So remember that this is just one way of doing things.
Edit: a couple more things. I think my newer version uses member initializer lists where appropriate. Also, I've read that prefixing variables with underscores is not recommended, as it may cause conflicts with some compilers.