一次元配列を二次元配列っぽく扱うクラスの実装

前回の記事では以下のようにして一次元配列を扱っていました。

一次元配列としてnewで確保した配列を二次元配列っぽく扱う。

#include <iostream>
using std::cout; using std::endl;

const int ARRAY_X = 10;
const int ARRAY_Y = 5;

int main () {
    int* array = new int[ARRAY_X * ARRAY_Y];
    
    for(int y=0;y<ARRAY_Y;++y) {
        for(int x=0;x<ARRAY_X;++x) {
            v = (y+1)*(x+1);
        }
    }
    
    for(int y=0;y<ARRAY_Y;++y) {
        for(int x=0;x<ARRAY_X;++x) {
            cout << array[y*ARRAY_X + x] << ' ';
        }
        cout << "\n";
    }
    cout << endl;
    
    delete[] array;
    
    return 0;
}
2次元配列の動的確保 - (void*)Pないと

しかし「array[y*ARRAY_X + x]」の部分が分かりにくいのでやはりここは「array[y][x]」でアクセスできたら嬉しいなと思い、それを実現するクラスを勉強がてら作ってみました。

template<class T>
class arrays {
    class array {
    public:
        T* m_array;
        int m_x;
        int m_index;
        
        operator T&() {
            return m_array[m_index];
        }
        
        array& operator=(T val) {
            m_array[m_index] = val;
            return *this;
        }
        
        T& operator[](int index) {
            return m_array[m_index*m_x+index];
        }
    };
    
    T* m_arrays;
    array m_array;
    int m_x;
    int m_y;
    
public:
    arrays(int y,int x) {
        m_x = x;
        m_y = y;
        m_arrays = new T[x*y];
    }
    
    arrays(const arrays& obj) {
        m_x = obj.m_x;
        m_y = obj.m_y;
        m_arrays = new T[m_x*m_y];
        for(int y=0;y<m_y;++y) {
            for(int x=0;x<m_x;++x) {
                m_arrays[y*m_x+x] = obj.m_arrays[y*m_x+x];
            }
        }
    }
    
    array& operator[](int index) {
        m_array.m_array = m_arrays;
        m_array.m_x = m_x;
        m_array.m_index = index;
        return m_array;
    }
    
    int x () const { return m_x; }
    int y () const { return m_y; }
    
    // 一次元配列の添え字からXY座標へ変換する。
    int index_x (int index) const { return index % m_x; }
    int index_y (int index) const { return index / m_x; }
    
    ~arrays() {
        delete[] m_arrays;
    }
};

arraysクラスです。

使い方は以下のようになります。

#include <iostream>
#include "arrays.h"
using std::cout; using std::endl;

const int ARRAY_X = 10;
const int ARRAY_Y = 5;

int main () {
    // arraysクラスを使う。
    arrays<int> array(ARRAY_X,ARRAY_Y);
    
    for(int y=0;y<ARRAY_Y;++y) {
        for(int x=0;x<ARRAY_X;++x) {
            // 二次元配列の書き方で代入できる
            array[y][x] = (y+1)*(x+1);
        }
    }
    
    for(int y=0;y<ARRAY_Y;++y) {
        for(int x=0;x<ARRAY_X;++x) {
            // 二次元配列の書き方で参照できる
            cout << array[y][x] << ' ';
        }
        cout << "\n";
    }
    cout << endl;
    
    return 0;
}

無事「array[y][x]」でアクセスできるようになりました。

最初、

class CClass {
public:
    int operator[][] (int index1,int index2) {
    }
}

みたいな感じで書けたりするのかなーとか思ったんですが当然の如く無理でした。

なので演算子のオーバーロードではarrays::arrayクラスを戻り値とし、arrays::arrayクラス内で演算子オーバーロードを行うことで二次元配列を実現しました。

さらにarrays::arrayクラス内で型変換演算子と代入演算子オーバーロードすることによって、一次元配列としてアクセスした場合でもうまく動くように実装してみました。

const int ARRAY_X = 10;
const int ARRAY_Y = 5;

int main () {
    // arraysクラスを使う。
    arrays<int> array(ARRAY_Y,ARRAY_X);
    
    for(int y=0;y<ARRAY_Y;++y) {
        for(int x=0;x<ARRAY_X;++x) {
            // 一次元配列のままでも代入することが出来る。
            array[y*ARRAY_X + x] = (y+1)*(x+1);
        }
    }
    
    for(int y=0;y<ARRAY_Y;++y) {
        for(int x=0;x<ARRAY_X;++x) {
            // 一次元配列のままでも参照することが出来る。
            cout << array[y*ARRAY_X + x] << ' ';
        }
        cout << "\n";
    }
    cout << endl;
    
    return 0;
}


実際作ってみると、色々と知識が抜けてててんやわんやになりますね。もっと手を動かさないとダメだ。