Array クラスを作る (演算子編)
"Array クラスを作る" シリーズの 4回目です。
今回は、各種演算子の定義について解説します。
過去の内容については、以下の記事を参照してください:
代入演算子
まずは代入演算子から。
代入演算子の動作はコピーコンストラクタと似ていますが、以下の点に留意する必要があります:
- a) 古いデータの破棄
-
代入演算子は、コンストラクタにとは異なり、既にデータを割り当てられたオブジェクトから呼び出されることがあります。
そのため、代入の前にこの古いデータを破棄しなければなりません。 - b) 自己代入の許容
-
「古いデータ」の破棄を「新しいデータの代入」より前に行ってしまうと、自己代入 (ex.
x =x;
) ができなくなります。
これを防ぐには、「古いデータ」を「新しいデータ」の代入が終わるまで保持しておく必要があります。
これらを踏まえて、さっそくコードの記述に入りましょう。
//代入 template <typename T> Array<T>& Array<T>::operator = (const Array<T>& a){ if (a.m_nSize){ int nSize =a.m_nSize; T* lpa =new T [nSize]; int i; for (i=0; i<nSize; ++i){ lpa[i] =a.m_lpa[i]; } //i Destroy(); m_nSize =nSize; m_lpa =lpa; } else { Destroy(); m_nSize =0; m_lpa =NULL; } return *this; } |
Destroy の動作については、こちらの記事 を参照してください。
(単純に m_lpa
を delete[] しているだけです。)
要素参照演算子
次に、配列の個々の要素を参照するための []
演算子を実装しましょう。
const, 非const どちらのオブジェクトからも呼び出せるようにするため、オーバーロードが必要になります。
//要素参照 (非const) template <typename T> T& Array<T>::operator [] (int nIndex){ assert(0 <= nIndex && nIndex < m_nSize); return m_lpa[nIndex]; } //要素参照 (const) template <typename T> const T& Array<T>::operator [] (int nIndex) const { assert(0 <= nIndex && nIndex < m_nSize); return m_lpa[nIndex]; } |
キャスト演算子
最後にキャスト演算子を定義します。
これは、配列データをアドレスとして受け取る関数などとの互換性を取る上で欠かせません。
//キャスト (T*) template <typename T> Array<T>::operator T* () { return m_lpa; } //キャスト (const T*) template <typename T> Array<T>::operator cosnt T* () cosnt { return m_lpa; } |
このキャスト演算子を利用して配列が空 (要素数が 0) かどうかを次のように判定することもできます。
(Array クラスを作る (導入編) で "m_nSize
= 0 ⇔ m_lpa
= NULL
" という制約を設けたのはこのため。)
//平均を計算 double avr(const Array<int> an){ if (a){ //非空判定 double dSum =0.0; int i; for (i=0; i<a.size; ++i){ dSum +=an[i]; } //i return dSum/a.size; } else return 0.0; //NaN } |
次のように !
演算子との併用することも可能です。(個人的にはこちらの書き方の方が好み)
//平均を計算 double avr(const Array<int> an){ //空判定 if (!a) return 0.0; //NaN double dSum =0.0; int i; for (i=0; i<a.size; ++i){ dSum +=an[i]; } //i return dSum/a.size; } |
成田(IOCCCには憧れない)