C++で作るベクトルクラス その2

3次元版を作っていて気づいたこと。
ベクトルクラスを作るときに、x,y,zというメンバを使ってコードを書くか、配列v[i]を利用してコードを書くか迷うところですが、3次元ならx,y,zを使って書いた方が分かりやすいかもしれません。↓

Vector3D.h
class Vector3D{
public:
	//メンバ変数
	float x;
	float y;
	float z;
	//コンストラクタ
	Vector3D();
	Vector3D(float x,float y,float z);
	//代入演算子
	Vector3D& Vector3D::operator=(const Vector3D& v);
	//単項演算子
	Vector3D& operator+=(const Vector3D& v);
	Vector3D& operator-=(const Vector3D& v);
	Vector3D& operator*=(float k);
	Vector3D& operator/=(float k);
	Vector3D operator+()const;
	Vector3D operator-()const;
	//添え字演算子
	float& operator[](int i);
	//比較演算子
	bool operator==(const Vector3D& v ) const;
	bool operator!=(const Vector3D& v ) const;
	//べクトルの長さ
	float norm()const;
	//正規化
	void normalize();
};

//ベクトル演算
//Vector3D+Vector3D
Vector3D operator+(const Vector3D& u,const Vector3D& v);
//Vector3D-Vector3D
Vector3D operator-(const Vector3D& u,const Vector3D& v);
//float*Vector3D
Vector3D operator*(float k,const  Vector3D& v);
//Vector3D*float
Vector3D operator*(const Vector3D& v,float k);
//Vector3D/float
Vector3D operator/(const Vector3D& v,float k);
//内積 Vector3D*Vector3D
float operator*(const Vector3D& u,const Vector3D& v);
//外積 Vector3D%Vector3D
Vector3D operator%(const Vector3D& u,const Vector3D& v);

//2つのベクトルのなす角度
float angle(const Vector3D& u,const Vector3D& v);

//出力
#include <iostream>
inline std::ostream& operator<<(std::ostream& s, const Vector3D& v);

constメンバ関数

定数ベクトルを利用するときにはconstなインスタンスを使うことになります。constなインスタンスが呼び出すことのできるのは次のようなconstメンバ関数だけになります。定数ベクトルに関する演算のパターンを考えるとどのメンバ関数をconst化するべきかが分かります。

	Vector3D operator+()const;
	Vector3D operator-()const;

例えば、定数ベクトルの長さを取得したり、定数ベクトルを比較するときには、メンバの値を書き換えずに済むのでconstを付けても問題はなさそうです。一方、定数ベクトルに*=という演算を施す場合には各要素の値が変わります。したがって、operator*=をconstメンバにしてはいけないことが分かります。

	const Vector3D I1(1,0,0);
	const Vector3D I2(0,1,0);
	cout << +I1 <<endl;		//constメンバ関数の呼び出し
	cout << -I1 <<endl;		//constメンバ関数の呼び出し
	cout << a*I1 <<endl;		//引数がconst
	I1*=3;				//値を書き換えているので、コンパイルエラー、constは付けない
	cout << I2*I1 <<endl;		//constメンバ関数の呼び出し
	if(I1 == a){			//constメンバ関数の呼び出し
 		//処理
	}
	cout<<"angle(I1,I2)="<<angle(I1,I2)<<"\n";	//2つの引数がconst

inline関数

関数を呼び出すとそのたびにオーバーヘッドが生じます。関数の実装部分に"inline"を付けると、関数の呼び出し部分でその関数の処理内容を展開して書き下すことができます。
小さな処理で多く呼ばれるタイプの関数にはinlineを付けると効果的です。
関数の呼び出し部分にその処理を書き下すためには、コンパイル時にヘッダ内の宣言部分で実装内容を知らないといけません。そのため、inline関数の実装部分は、クラス宣言内に書くか、クラス宣言の外であるならヘッダ内で実装を書いておく必要があります。(.cppにinline関数の実装部分を記述するとコンパイルエラー)
inline関数ばかりならVector3D.hに全て定義を書いてしまうのが良いと思います。

比較演算子

ベクトルの比較は全ての要素が同じならtrueを返すようにします。!=は==の逆を返しましょう。

Vector3D.h
inline bool Vector3D::operator==(const Vector3D& v ) const{
    return (x == v.x) && (y == v.y) && (z == v.z);
}
inline bool Vector3D::operator!=(const Vector3D& v ) const{
    return !(*this == v);
}

添え字演算子

要素にアクセスするのに、配列と同じ書き方ができると便利です。範囲外の処理(例えばxを返すなど)も書く必要があります。

//添え字演算子
inline float& Vector3D::operator[](int i){
	if(i == 0){
		return x;
	}
	else if(i == 1){
		return y;
	}
	else if(i == 2){
		return z;
	}
	else{
		return x;
	}
}

外積

3次元ベクトルでは外積の計算がよく使われるので追加してみました。演算子は例えば%を使うといいかもしれません。

//外積 Vector3D%Vector3D
inline Vector3D operator%(const Vector3D& u,const Vector3D& v){//IN:Vector3D Vector3D OUT:Vector3D
	Vector3D w;
	w.x=u.y*v.z-u.z*v.y;
	w.y=u.z*v.x-u.x*v.z;
	w.z=u.x*v.y-u.y*v.x;
	return w;
}

ベクトルに関する関数

一通りベクトルの基本演算を定義することができたので次はベクトルに関する関数を追加してみます。関数を定義するには、ベクトルのメンバにする方法と外部の関数で定義する方法があります。
グローバル関数で定義すると見た目は見やすいですが、グローバル関数が増えすぎると管理が煩雑になってしまいます。クラスのメソッドでは、引数がない関数はともかく2つのベクトルに関する計算は見た目の点でちょっとマイナスになるかもしれません。

メンバ関数として定義
Vector3D u(1,1,1);
Vector3D v(1,2,1);
cout<< u.angle(v) << endl;
グローバル関数として定義
Vector3D u(1,1,1);
Vector3D v(1,2,1);
cout<< angle(u,v) << endl;

結局作成したものはこんな感じになりました↓

Vector3D.h
#ifndef Vector3D_H
#define Vector3D_H

class Vector3D{
public:
	//メンバ変数
	float x;
	float y;
	float z;
	//コンストラクタ
	Vector3D();
	Vector3D(float x,float y,float z);
	//代入演算子
	Vector3D& Vector3D::operator=(const Vector3D& v);
	//単項演算子
	Vector3D& operator+=(const Vector3D& v);
	Vector3D& operator-=(const Vector3D& v);
	Vector3D& operator*=(float k);
	Vector3D& operator/=(float k);
	Vector3D operator+()const;
	Vector3D operator-()const;
	//添え字演算子
	float& operator[](int i);
    //比較演算子
	bool operator==(const Vector3D& v ) const;
	bool operator!=(const Vector3D& v ) const;
	//べクトルの長さ
	float norm()const;
	//正規化
	void normalize();
};
//ベクトル演算
//Vector3D+Vector3D
Vector3D operator+(const Vector3D& u,const Vector3D& v);
//Vector3D-Vector3D
Vector3D operator-(const Vector3D& u,const Vector3D& v);
//float*Vector3D
Vector3D operator*(float k,const  Vector3D& v);
//Vector3D*float
Vector3D operator*(const Vector3D& v,float k);
//Vector3D/float
Vector3D operator/(const Vector3D& v,float k);
//内積 Vector3D*Vector3D
float operator*(const Vector3D& u,const Vector3D& v);
//外積 Vector3D%Vector3D
Vector3D operator%(const Vector3D& u,const Vector3D& v);
//2つのベクトルのなす角度
float angle(const Vector3D& u,const Vector3D& v);
//出力
#include <iostream>
inline std::ostream& operator<<(std::ostream& s, const Vector3D& v);
//*----------------------メンバ関数の実装--------------------------*//
#include <cmath>
//コンストラクタ
inline Vector3D::Vector3D(){ x = y = z = 0; }
inline Vector3D::Vector3D(float x,float y,float z){
	this->x=x;		this->y=y;		this->z=z;
}
//代入演算子
inline Vector3D& Vector3D::operator=(const Vector3D& v){
	this->x=v.x;	this->y=v.y;	this->z=v.z;
	return *this;
}
//単項演算子
inline Vector3D& Vector3D::operator+=(const Vector3D& v){
	 this->x += v.x;	this->y += v.y;		this->z += v.z;
	 return *this;
}
inline Vector3D& Vector3D::operator-=(const Vector3D& v){
	 this->x -= v.x;	this->y -= v.y;		this->z -= v.z;
	 return *this;
}
inline Vector3D& Vector3D::operator*=(float k){
	 this->x *= k;		this->y *= k;		this->z *= k;
	 return *this;
}
inline Vector3D& Vector3D::operator/=(float k){
	this->x /= k;		this->y /= k;		this->z /= k;
	return *this;
}
inline Vector3D Vector3D::operator+()const{		//+Vector3D
	return *this;
}
inline Vector3D Vector3D::operator-()const{		//-Vector3D
	return Vector3D(-x,-y,-z);
}
//添え字演算子
inline float& Vector3D::operator[](int i){
	if(i == 0){
		return x;
	}
	else if(i == 1){
		return y;
	}
	else if(i == 2){
		return z;
	}
	else{
		return x;
	}
}
//比較演算子
inline bool Vector3D::operator==(const Vector3D& v ) const{
    return (x == v.x) && (y == v.y) && (z == v.z);
}
inline bool Vector3D::operator!=(const Vector3D& v ) const{
    return !(*this == v);
}
//べクトルの長さ
inline float Vector3D::norm()const{
	return pow(x*x+y*y+z*z,0.5f);
}
//正規化
inline void Vector3D::normalize(){
	*this /= norm();
}
//*----------------------グローバル関数の実装--------------------------*//
//二項演算子の定義
//Vector3D+Vector3D
inline Vector3D operator+(const Vector3D& u,const Vector3D& v){
	Vector3D w;
	w.x=u.x+v.x;
	w.y=u.y+v.y;
	w.z=u.z+v.z;
	return w;
}
//Vector3D-Vector3D
inline Vector3D operator-(const Vector3D& u,const Vector3D& v){
	Vector3D w;
	w.x=u.x-v.x;
	w.y=u.y-v.y;
	w.z=u.z-v.z;
	return w;
}
//float*Vector3D
inline Vector3D operator*(float k,const  Vector3D& v){
	return Vector3D(k*v.x,k*v.y,k*v.z);
}
//Vector3D*float
inline Vector3D operator*(const Vector3D& v,float k){
	return Vector3D(v.x*k,v.y*k,v.z*k);
}
//Vector3D/float
inline Vector3D operator/(const Vector3D& v,float k){
	return Vector3D(v.x/k,v.y/k,v.z/k);
}
//内積 Vector3D*Vector3D
inline float operator*(const Vector3D& u,const Vector3D& v){
	return u.x*v.x+u.y*v.y+u.z*v.z;
}
//外積 Vector3D%Vector3D
inline Vector3D operator%(const Vector3D& u,const Vector3D& v){
	Vector3D w;
	w.x=u.y*v.z-u.z*v.y;
	w.y=u.z*v.x-u.x*v.z;
	w.z=u.x*v.y-u.y*v.x;
	return w;
}
//画面への表示
#include <iostream>
inline std::ostream& operator<<(std::ostream& s, const Vector3D& v){
	return s <<'('<<v.x<<","<<v.y<<","<<v.z<<')';
}

#define PI 3.1415926535
//2つのベクトルのなす角
inline float angle(const Vector3D& u,const Vector3D& v){
	float cos =u*v/(u.norm()*v.norm());
	return float(acos(cos)/PI*180);
}

#endif

作成したクラスでテストしたのがこちら。

int main()
{
	Vector3D u(1,2,3);
	Vector3D v(4,1,0);
	u[2]=3;
	cout <<"u[2]="<<u[2]<<endl;	//3
	
	cout<<"u="<<u<<endl;		//(1,2,3)
	cout<<"v="<<v<<endl;		//(4,1,0)

	cout<<"-v="<<-v<<endl;		//(-4,-1,0)
	cout<<"u+v="<<u+v<<endl;	//(5,3,3)
	cout<<"u-v="<<u-v<<endl;	//(-3,1,3)
	cout<<"u*v="<<u*v<<endl;	//6
	cout<<"u%v="<<u%v<<endl;		//(-3,12,-7)

	u+=Vector3D(1,1,1);
	cout<<"u+(1,1,1)="<<u<<endl;	//u=(2,3,4)
	u-=Vector3D(1,1,1);
	cout<<"u-(1,1,1)="<<u<<endl;	//u=(1,2,3)
	u*=3;
	cout<<"u*3="<<u<<endl;			//u=(3,6,9)
	u/=3;
	cout<<"u/3="<<u<<endl;			//u=(1,2,3)

	cout <<"norm(u)^2="<<u.norm()*u.norm()<<endl;	//14	
	Vector3D a(1,2,3);
	Vector3D b;
	b = a;
	cout << (a == b) <<endl;		//1 true
	cout << (a != b) <<endl;		//0 false

	const Vector3D I1(1,0,0);
	const Vector3D I2(0,1,0);
	cout<<"angle(I1,I2)="<<angle(I1,I2)<<endl;		//(1,2,3)
	return 0;
}


u[2]=3
u=(1,2,3)
v=(4,1,0)
-v=(-4,-1,-0)
u+v=(5,3,3)
u-v=(-3,1,3)
u*v=6
u%v=(-3,12,-7)
u+(1,1,1)=(2,3,4)
u-(1,1,1)=(1,2,3)
u*3=(3,6,9)
u/3=(1,2,3)
norm(u)^2=14
1
0
angle(I1,I2)=90