C++ 参照を返す関数と演算子オーバーロード 書いて覚えるための初心者自己中記事
参照を返すメンバ関数を作れば、アクセス指定子がprivate な変数でも
それを参照している状態が返されるので数値を入れられる。
class Array { public: Array(int index); virtual ~Array(); int& At(int index); //参照を返す void Show(int index); private: int* m_array;//privateなメンバ変数の参照を返す }; Array::Array(int index) { m_array = new int[index]; } Array::~Array() { delete[] m_array; } int& Array::At(int index) { //参照を返す return m_array[index]; } void Array::Show(int index) { cout << m_array[index] << endl; } int main() { Array array(3); cin >> array.At(0); //参照が返されているからcin 入れられる!! array.Show(0); system("pause"); }
ポインタでも同じ
class Array { public: Array(int index); virtual ~Array(); int* At(int index); //ポインタを返す void Show(int index); private: int* m_array; }; Array::Array(int index) { m_array = new int[index]; } Array::~Array() { delete[] m_array; } int* Array::At(int index) { //ポインタを返す return &m_array[index]; } void Array::Show(int index) { cout << m_array[index] << endl; } int main() { Array array(3); cin >> *array.At(0); //ポインタが返されているからcin 入れられる!! array.Show(0); system("pause"); }
次に
cin >> array.At(0); //参照が返されているからcin 入れられる!!
この部分の処理をもっと楽にするために [ ] を演算子オーバーロードでうまいことする。
さっきの処理とほぼ同じでやる。
class Array { public: Array(int index); virtual ~Array(); int& At(int index); //参照を返す void Show(int index); int& operator[](int index); // []演算子オーバーロード private: int* m_array; }; Array::Array(int index) { m_array = new int[index]; } Array::~Array() { delete[] m_array; } int& Array::At(int index) { //参照を返す return m_array[index]; } void Array::Show(int index) { cout << m_array[index] << endl; } int& Array::operator[](int index) { // []演算子オーバーロード return m_array[index]; } int main() { Array array(3); cin >> array.At(0); //参照が返されているからcin 入れられる!! array.Show(0); cin >> array[1]; //参照が返されているからcin 入れられる!! array.Show(1); system("pause"); }
プロトタイプ例
operator T();
( T ) n
みたいな。
戻り型は言わずもがな T なので書かなくて良い。
class.h
class Franction { public: Franction(double numer = 0, double denom = 1); public: double GetNumer() const; double GetDenom() const; private: double m_numer; double m_denom; };
class.cpp
Franction::Franction(double numer, double denom) : m_numer(numer), m_denom(denom) { } double Franction::GetNumer()const { return m_numer; } double Franction::GetDenom()const { return m_denom; }
double m_numer;
double m_denom;
二つのメンバ変数、これ分子と分母役
これを m_numer / m_denom
として分数をdouble 型で出したいらしい。
この場合にGetメンバ関数とか作って
bouble n ;
Fraction f ( 5 , 9 );
n = f . Get();
みたいな感じでやればいいけど、テキスト的にはそれじゃお勉強にならないからキャスト演算子をオーバーロードしてやりましょうと。
んで、さっき書いた
operator T();
これ。
この場合は
class.h
operator double()const;
class.cpp
Franction::operator double()const { return m_numer / m_denom; }
return m_numer / m_denom ;
としちゃってる。
これで、いや、さらにこのオーバーロードしたキャスト演算子は暗黙的なんだそうで、必要なら勝手にやってくれるそう。
int main() { Franction f(3, 7); double n; n = f; //勝手に暗黙でオーバーロードしたキャスト演算子が動いている cout << n << endl; system("pause"); }
//output
0.428571
int main() { Franction f(3, 7); double n; n = (f); //本来はこう(オーバーロードしたのを使うときのハナシ) cout << n << endl; system("pause"); }
これはあれか?
double型のn に Franction型の f を代入しようとしてるから
キャストが必要だと判断したときにオーバーロードされてるキャストがあるから使ってくれるのか?
* 演算子の場合
class.h
Franction operator *(const Franction& rop)const;
class.cpp
Franction Franction::operator*(const Franction& rop)const { return Franction( m_numer * rop.m_numer, m_denom * rop.m_denom ); }
戻り値の型がFranction なので凄くFranctionしてる。
return の書き方が自分的には初。
クラス名で囲ってやるんだなぁ。
* は二項演算子
二項ないと成立しないから?
左項が自分
右項が引数
になる。
次の単項演算子の場合は演算対象が自分自身のため
引数は必要なし。
- (マイナス)演算子は単項・二項どっちもあり得る
単項の場合はさっき書いた通り引数なし。
二項の場合(減算演算子)には引数をつける。
this とは
Franction& operator *=(const Franction& rop);
Franction& Franction::operator*=(const Franction& rop) { m_numer *= rop.m_numer; m_denom *= rop.m_denom; return *this;// this はこのメンバ関数を呼び出したオブジェクトのアドレスを持つポインタ }
二項演算子の場合、左項が自分って言ってたけど。
thisがメンバ関数を呼び出したオブジェクトのアドレスってことは自分のアドレスってことでしょ?
そのアドレスを持っているthis を参照するFranction 型が戻ってきて
呼び出したオブジェクト(自分)の元へ・・?
自分で自分を参照って事? え?
インクリメント・デクリメント演算子には
前置き ++n
後置き n++
がある。
h = ++n の場合は ++n が先に計算される
h = n++ の場合は h = n が先に計算される。だったっけ?
オーバーロードでは区別するために後置きにはint 型の引数型を書く決まり。
あとは計算したのを渡すか
計算する前のを渡すか(計算はする)
Franction& operator++(); Franction operator++(int);
Franction& Franction::operator++() { m_numer += m_denom; return *this; } Franction Franction::operator++(int) { Franction ret = *this; m_numer += m_denom; return ret; }
というか、そもそも自分のメンバ変数を弄ってるのになんで返さなきゃならないのだろうか?
テキストで
戻り値なしではこのようなことができなくなってしまいます。
って書いてるけど、このようなことって何だ・・?
あっ、代入される変数の代わりとして使いたいということか・・?
なんでそんなことしたいの?
左項が普通の変数で
右項がクラスオブジェクトなどの場合
左項が自身になるから。
それ自体は問題がないが、
しかしそうなるとクラスのメンバ変数がprivate だった場合に
アクセスできなくなってしまいます。
その際に利用できるのが
friend
オーバーロードの実装はクラス外で行うが
宣言部分はクラス内で行う。
その際宣言部の先頭に friend を記述する。
そうすると演算子オーバーロードはクラス外の認識で、しかしクラス内のprivate にもアクセスできるようになる。
暗黙のキャストの抑制
暗黙的にキャストが行われるのが不都合な場合がある。
そのため、それを暗黙では呼び出さないようにする方法がある。
それが、
explicit (明示的な)
これを宣言の頭につけるとOK
引数が一つでも通るコンストラクタには常につけてもいい感じ。
キャストの種類
C++ では三種類のキャストに分けられている。
static_cast 静的な普通の型変換
reinterpret_cast ポインタや参照がかかわる強引な型変換
const_cast ポインタや参照のconst を外すキャスト
//static_cast の場合 int main() { signed int a = -1; int b; b = (unsigned)a; //こうなる signed int aa = -1; int bb; bb = static_cast<unsigned>(aa); system("pause"); }
//reinterpret_cast の場合 //ポインタや参照の絡む強引なキャスト int main() { int n = 0x12345678; const char* p = reinterpret_cast<const char*>(&n); printf("%02X\n", p[0]); printf("%02X\n", p[1]); printf("%02X\n", p[2]); printf("%02X\n", p[3]); system("pause"); }
//output
78
56
34
12
これは環境依存。リトルエンディアン・ビッグエンディアンで変わってくる。
//const_cast の場合 //constを外すキャスト int main() { const int* p; int* n; n = p;//こっちはpがconstなのでエラー n = const_cast<int*>(p); //constを外すキャスト system("pause"); }
ここまで