C++でベクトルクラスを作成する〜その1
演算子のオーバーロードを使って簡単なベクトルクラス(2D)を作ってみます。下のようにソース内に直接"+"や"-"を書いて簡単に計算できるようなクラスを目標にしてみたいと思います。
Vector u(1,2); Vector v(3,4); cout<< u + v <<endl;
(実際には計算速度の問題など細かい点で欠点があるようですが、見た目の点では数学の表記と一致しますのでとても分かりやすくなります。)
Vectorクラスの基本形
2次元なので要素をx,yとします。privateにしてみたいところですが、setter,getterなしのpublicで作ることにしてみます。
(要素の可視性をprivateにするか,publicにするべきかという問題についてですが、setter,getterのオーバーヘッドがどの程度か?inlineを付けるとpublicのときと同じ効率になるのか?という疑問について調べてみる価値がありそうです。。。)
ベクトルクラスのひな形は下のようになります。
class Vector{ public: Vector(); Vector(float _x,float _y); //ここに演算子の定義 float x; float y; }; //ここに演算子の定義
代入演算子の定義
まずは=の定義。
Vector& Vector::operator=(const Vector& v){ //代入演算子の定義 this->x=v.x; this->y=v.y; return *this; }
引数がない演算子の定義
"+v"や"-v"の計算がこれに相当します。
Vector operator+(){ //+の定義: +v return *this; } Vector operator-(){ //-の定義: -v return Vector(-x,-y); }
単項演算子の定義
次に引数が一つの演算子を定義します。例えば"+=", "-="という演算子は、vec1.operator+=(vec2)という呼び出し方ですのでメンバ関数として定義します。
Vector& operator+=(const Vector& v){ this->x += v.x; this->y += v.y; return *this; //+=の定義 v+= } Vector& operator-=(const Vector& v){ this->x -= v.x; this->y -= v.y; return *this; //-=の定義 v-= }
これに加えて演算子"*=", "/=" の定義を書けば終わりです。
二項演算子の定義
"+", "-", "*"(内積)などの二項演算子を定義します。これはoperator+(vec1,vec2)という呼び出し方ですので、通常の関数(クラススコープの外)で宣言しておきます。
Vector operator+(const Vector& u,const Vector& v){ //vector+vector Vector w; w.x = u.x + v.x; w.y = u.y + v.y; return w; } Vector operator-(const Vector& u,const Vector& v){ //vector-vector Vector w; w.x = u.x - v.x; w.y = u.y - v.y; return w; } float operator*(const Vector& u,const Vector& v){ //内積 vector*vector return u.x * v.x + u.y * v.y; }
さらに、"*"(数値*ベクトル,ベクトル*数値)、"/"(ベクトル/数値)に対応する演算子を定義すれば、ベクトルに関する一通りの計算ができることになります。
画面への出力
画面に簡単に出力できるようにするため"<<"演算子を定義します。
#include <iostream> using namespace std; ostream& operator<<(ostream& s, const Vector& v){ return s <<'('<<v.x<<","<<v.y<<')'; }
作成したベクトルクラスがこちら。
Vector2D.h
#ifndef Vector2D_H #define Vector2D_H class Vector2D{ public: Vector2D(); Vector2D(float _x,float _y); Vector2D& Vector2D::operator=(const Vector2D& v); Vector2D& operator+=(const Vector2D& v); Vector2D& operator-=(const Vector2D& v); Vector2D& operator*=(float k); Vector2D& operator/=(float k); Vector2D operator+(); Vector2D operator-(); float x; float y; }; //二項演算子 Vector2D operator+(const Vector2D& u,const Vector2D& v); Vector2D operator-(const Vector2D& u,const Vector2D& v); float operator*(const Vector2D& u,const Vector2D& v); Vector2D operator*(const Vector2D& v, float k); Vector2D operator*(float k ,const Vector2D& v); Vector2D operator/(const Vector2D& v, float k); //画面への出力 #include <iostream> std::ostream& operator<<(std::ostream& stream, const Vector2D& v); #endif
Vector2D.cpp
#include "Vector2D.h" Vector2D::Vector2D():x(0),y(0) { } Vector2D::Vector2D(float _x,float _y):x(_x),y(_y) { } //代入演算子の定義 Vector2D& Vector2D::operator=(const Vector2D& v){ this->x=v.x; this->y=v.y; return *this; } // +=の定義 Vector2D& Vector2D::operator+=(const Vector2D& v){ this->x += v.x; this->y += v.y; return *this; } // -=の定義 Vector2D& Vector2D::operator-=(const Vector2D& v){ this->x -= v.x; this->y -= v.y; return *this; } // *=の定義 Vector2D& Vector2D::operator*=(float k){ this->x *= k; this->y *= k; return *this; } // /=の定義 Vector2D& Vector2D::operator/=(float k){ this->x /= k; this->y /= k; return *this; } //+の定義: +v Vector2D Vector2D::operator+(){ return *this; } //-の定義: -v Vector2D Vector2D::operator-(){ return Vector2D(-x,-y); } //二項演算子 Vector2D operator+(const Vector2D& u,const Vector2D& v){ //vector+vector Vector2D w; w.x = u.x + v.x; w.y = u.y + v.y; return w; } Vector2D operator-(const Vector2D& u,const Vector2D& v){ //vector-vector Vector2D w; w.x = u.x - v.x; w.y = u.y - v.y; return w; } float operator*(const Vector2D& u,const Vector2D& v){ //内積 vector*vector return u.x * v.x + u.y * v.y; } Vector2D operator*(const Vector2D& v, float k){ //vector*scalar Vector2D w; w.x = v.x * k; w.y = v.y * k; return w; } Vector2D operator*(float k ,const Vector2D& v){ //scalar*vector Vector2D w; w.x = v.x * k; w.y = v.y * k; return w; } Vector2D operator/(const Vector2D& v, float k){ //vector/scalar Vector2D w; w.x = v.x / k; w.y = v.y / k; return w; } //画面への出力 #include <iostream> using namespace std; ostream& operator<<(ostream& stream, const Vector2D& v){ return stream <<'('<<v.x<<","<<v.y<<')'; }
TestVector2D.cpp
#include "Vector2D.h" #include <iostream> using namespace std; int main() { Vector u(1,2); Vector v(4,1); cout<<"u="<<u<<"\n"; cout<<"v="<<v<<"\n"; cout<<"-v="<<-v<<"\n"; cout<<"u+v="<<u+v<<"\n"; cout<<"u-v="<<u-v<<"\n"; cout<<"u*v="<<u*v<<"\n"; u+=Vector(1,1); v-=Vector(1,1); cout<<"u+(1,1)="<<u<<"\n"; cout<<"v-(1,1)="<<v<<"\n"; return 0; }
u=(1,2)
v=(4,1)
-v=(-4,-1)
u+v=(5,3)
u-v=(-3,1)
u*v=6
u+(1,1)=(2,3)
u-(1,1)=(1,2)
u*3=(3,6)
u/3=(1,2)
指摘&追記。
計算効率を上げるためのテクニック
上で述べた方法だと、演算子を呼び出すごとにベクトルクラスの一時オブジェクトが生成されるため、その分効率が悪いです。計算速度を向上させるには、Expression Templateというテクニックを利用すると良いみたいです。式の構造を渡して=が呼び出されるときに一度に計算してしまうという方法。
参考タイトル↓
Expression Template - Faith and Brave - C++で遊ぼう