★添字演算子オーバーロード★


続いては添字演算子のオーバーロードについて説明します。

添字演算子とは「[ ]」です。

配列の添え字として使いますが、これをオーバーロードするとクラスが配列のように扱えます。

C#ではインデクサという名前になっています。

どのような使い方になるか、まずはベースとなるクラスを作ってみます。

<sample program cpp068-01>

#include <iostream>
#include <vector>

#include <cstdlib>
#include <ctime>

class Sample {
public:

    Sample();

    void Show() const;

private:

    std::vector<int> m_vecInt;
};

int main()
{
    Sample sample;

    sample.Show();

    return 0;
}

Sample::Sample()
{
    srand((unsigned int)time(NULL));

    int count = rand() % 6 + 5;

    for (int i = 0; i < count; i++) {
        m_vecInt.push_back((i + 1) * 10);
    }
}

void Sample::Show() const
{
    for (unsigned int i = 0; i < m_vecInt.size(); i++) {
        std::cout << m_vecInt[i] << " ";
    }
    std::cout << std::endl;
}

<実行結果>

10 20 30 40 50 60 70 80
続行するには何かキーを押してください・・・

クラスのメンバとして「ベクタ」を用意しました。

乱数を使って、コンストラクタ関数で5から10個のデータをpushしています。

Show関数で「ベクタ」の中身を全て表示しました。

※小さいプログラムなので、直接値を書いているのは見逃してください・・・

当然、クラスの外からは「ベクタ」にアクセスする事は出来ませんので、「ベクタ」の要素を取り出そうと思うと↓のような関数を追加する必要があります。

<sample program cpp068-02>

#include <iostream>
#include <vector>

#include <cstdlib>
#include <ctime>

class Sample {
public:

    Sample();

    void Show() const;

    int GetElement(const unsigned int index) const;

private:

    std::vector<int> m_vecInt;
};

int main()
{
    Sample sample;

    sample.Show();

    std::cout << sample.GetElement(0) << std::endl;

    return 0;
}

Sample::Sample()
{
    srand((unsigned int)time(NULL));

    int count = rand() % 6 + 5;

    for (int i = 0; i < count; i++) {
        m_vecInt.push_back((i + 1) * 10);
    }
}

void Sample::Show() const
{
    for (unsigned int i = 0; i < m_vecInt.size(); i++) {
        std::cout << m_vecInt[i] << " ";
    }
    std::cout << std::endl;
}

int Sample::GetElement(const unsigned int index) const
{
    return m_vecInt[index];
}

<実行結果>

10 20 30 40 50 60 70 80 90
10
続行するには何かキーを押してください・・・

GetElement関数を作る事で「ベクタ」の添え字(インデックス)を指定し、中身を取得する事が出来ます。

しかし、添字演算子をオーバーロードする事で、もっと直感的にアクセス出来るようになります。


[]演算子のオーバーロード


まずは「読み取り専用」パターンから作ってみましょう。

上のプログラムからGetElement関数を削除し、添字演算子のオーバーロードを追加します。

<sample program cpp068-03>

#include <iostream>
#include <vector>

#include <cstdlib>
#include <ctime>

class Sample {
public:

    Sample();

    void Show() const;

    int operator[](const unsigned int index) const;

private:

    std::vector<int> m_vecInt;
};

int main()
{
    Sample sample;

    sample.Show();

    std::cout << sample[0] << std::endl;

    return 0;
}

Sample::Sample()
{
    srand((unsigned int)time(NULL));

    int count = rand() % 6 + 5;

    for (int i = 0; i < count; i++) {
        m_vecInt.push_back((i + 1) * 10);
    }
}

void Sample::Show() const
{
    for (unsigned int i = 0; i < m_vecInt.size(); i++) {
        std::cout << m_vecInt[i] << " ";
    }
    std::cout << std::endl;
}

int Sample::operator[](const unsigned int index) const
{
    return m_vecInt[index];
}

<実行結果>

10 20 30 40 50 60 70 80 90 100
10
続行するには何かキーを押してください・・・

まずはプロトタイプ宣言を見てみましょう。

int operator[](const unsigned int index) const;

引数として、添え字(インデックス)を渡しています。

「読み取り専用」ですから、中身の書き換えをさせないために後ろに「const」を付つけています。

戻り値も「ベクタ」の該当要素のコピーを返しているだけですので、外から書き換えられることもありません。

注目してほしいのはmain関数です。

std::cout << sample[0] << std::endl;

あたかもクラスが配列のように扱えています。

※添え字の範囲チェックはしていませんので、オーバーロード本体で範囲チェックを入れる事も重要な事です。

今回は書きませんので、皆さんでassertなどを使って作ってみてください。


では続いて「読み書き両用」パターンのオーバーロードを追加してみましょう。

<sample program cpp068-04>

#include <iostream>
#include <vector>

#include <cstdlib>
#include <ctime>

class Sample {
public:

    Sample();

    void Show() const;

    int operator[](const unsigned int index) const;

    int& operator[](const unsigned int index);

private:

    std::vector<int> m_vecInt;
};

int main()
{
    Sample sample;

    sample.Show();

    sample[0] = 12;

    std::cout << sample[0] << std::endl;

    return 0;
}

Sample::Sample()
{
    srand((unsigned int)time(NULL));

    int count = rand() % 6 + 5;

    for (int i = 0; i < count; i++) {
        m_vecInt.push_back((i + 1) * 10);
    }
}

void Sample::Show() const
{
    for (unsigned int i = 0; i < m_vecInt.size(); i++) {
        std::cout << m_vecInt[i] << " ";
    }
    std::cout << std::endl;
}

int Sample::operator[](const unsigned int index) const
{
    return m_vecInt[index];
}

int& Sample::operator[](const unsigned int index)
{
    return m_vecInt[index];
}

<実行結果>

10 20 30 40 50 60 70
12
続行するには何かキーを押してください・・・

これもプロトタイプ宣言から見てみましょう。

int& operator[](const unsigned int index);

引数は添え字(インデックス)ですが、戻り値はint型の参照です。

本体を見ても、配列の指定した要素の参照を返しています。

main関数を見てみると、

sample[0] = 12;

あたかも、配列の要素に数値を代入しているように見えます。

しかし、=演算子の左辺は「配列の指定した要素の参照」です。

右辺の数値が「配列の指定した要素」へ代入されている事が分かります。


次へ

戻る

目次へ