C++におけるクラス内配列

クラス内配列を添字演算子[](index operator, subscript operator)のオーバーロードを利用して記述します。


1.クラス内で配列を静的に確保する

C/C++では配列そのものを関数の引数や戻り値にすることはできませんが、以下のArrayクラスではそれが可能です。これを使えば複数の値の値渡しや値戻しができます。関数の引数や戻り値がベクトルである場合に使うと便利です。もちろん、配列長が大きい場合は参照渡しや参照戻しを使うこともできます。

array1.cpp

#include <cassert>
#include <iostream>

template <int LENGTH> class Array {
  double array[LENGTH];
public:
  const int length = LENGTH;
  Array() {
    if (length > 0) {
      for (int i = 0; i < length; i++) {
        array[i] = 0;
      }
    }
  }
  Array& operator=(const Array& object) {
    if (length > 0) {
      for (int i = 0; i < length; i++) {
        array[i] = object.array[i];
      }
    }
    return *this;
  }
  double& operator[](int index) {
    assert(0 <= index && index < length);
    return array[index];
  }
  const double& operator[](int index) const {
    assert(0 <= index && index < length);
    return array[index];
  }
};

Array<10> echo1(Array<10> array) {
  return array;
}

const Array<10>& echo2(const Array<10>& array) {
  return array;
}

int main() {
  const int length = 10;
  //Array<length> array;
  auto array = Array<length>();
  for (int i = 0; i < array.length; i++) {
    array[i] = i;
  }
  auto array1 = Array<length>();
  array1 = array;
  for (int i = 0; i < array1.length; i++) {
    std::cout << array1[i] << std::endl;
  }
  for (int i = 0; i < echo1(array).length; i++) {
    std::cout << echo1(array)[i] << std::endl;
  }
  for (int i = 0; i < echo2(array).length; i++) {
    std::cout << echo2(array)[i] << std::endl;
  }
}

array2.cpp

#include <cassert>
#include <iostream>

template <int LENGTH> class Array {
  double array[LENGTH];
public:
  const int length = LENGTH;
  Array();
  Array& operator=(const Array&);
  double& operator[](int);
  const double& operator[](int) const;
};

template <int LENGTH> Array<LENGTH>::Array() {
  if (length > 0) {
    for (int i = 0; i < length; i++) {
      array[i] = 0;
    }
  }
}

template <int LENGTH> Array<LENGTH>& Array<LENGTH>::operator=(const Array& object) {
  if (length > 0) {
    for (int i = 0; i < length; i++) {
      array[i] = object.array[i];
    }
  }
  return *this;
}

template <int LENGTH> double& Array<LENGTH>::operator[](int index) {
  assert(0 <= index && index < length);
  return array[index];
}

template <int LENGTH> const double& Array<LENGTH>::operator[](int index) const {
  assert(0 <= index && index < length);
  return array[index];
}

Array<10> echo1(Array<10> array) {
  return array;
}

const Array<10>& echo2(const Array<10>& array) {
  return array;
}

int main() {
  const int length = 10;
  //Array<length> array;
  auto array = Array<length>();
  for (int i = 0; i < array.length; i++) {
    array[i] = i;
  }
  auto array1 = Array<length>();
  array1 = array;
  for (int i = 0; i < array1.length; i++) {
    std::cout << array1[i] << std::endl;
  }
  for (int i = 0; i < echo1(array).length; i++) {
    std::cout << echo1(array)[i] << std::endl;
  }
  for (int i = 0; i < echo2(array).length; i++) {
    std::cout << echo2(array)[i] << std::endl;
  }
}


2.クラス内で配列を動的に確保する

スタックに格納できないサイズの配列を扱う際に使うと便利です。また、ディストラクタが自動で動的に確保した配列の後始末をしてくれます。

注)オブジェクトの値渡しでは、値渡しの際にコピーコンストラクタが呼び出され、関数を抜ける時にディストラクタが呼び出されます。

注)メンバ変数にポインタがあるオブジェクトの値渡しでは、関数を抜ける時に呼び出されるディストラクタがコピー元の実体を解放するので、値渡しの際に実体がコピーされるようにコピーコンストラクタを定義しておきます。

array3.cpp

#include <cassert>
#include <iostream>

class Array {
  double* array;
public:
  int length;
  Array(int length) : array(NULL), length(length) {
    if (length > 0) {
      array = new double[length];
      for (int i = 0; i < length; i++) {
        array[i] = 0;
      }
    }
  }
  Array(const Array& object) : array(NULL), length(object.length) {
    if (length > 0) {
      array = new double[length];
      for (int i = 0; i < length; i++) {
        array[i] = object.array[i];
      }
    }
  }
  Array& operator=(const Array& object) {
    array = NULL;
    length = object.length;
    if (length > 0) {
      array = new double[length];
      for (int i = 0; i < length; i++) {
        array[i] = object.array[i];
      }
    }
    return *this;
  }
  ~Array() {
    if (array != NULL) {
      delete[] array;
      array = NULL;
    }
  }
  double& operator[](int index) {
    assert(0 <= index && index < length);
    return array[index];
  }
  const double& operator[](int index) const {
    assert(0 <= index && index < length);
    return array[index];
  }
};

Array echo1(Array array) {
  return array;
}

const Array& echo2(const Array& array) {
  return array;
}

int main() {
  int length = 10;
  //Array array(length);
  auto array = Array(length);
  for (int i = 0; i < array.length; i++) {
    array[i] = i;
  }
  auto array1 = Array(length);
  array1 = array;
  for (int i = 0; i < array1.length; i++) {
    std::cout << array1[i] << std::endl;
  }
  for (int i = 0; i < echo1(array).length; i++) {
    std::cout << echo1(array)[i] << std::endl;
  }
  for (int i = 0; i < echo2(array).length; i++) {
    std::cout << echo2(array)[i] << std::endl;
  }
}

array4.cpp

#include <cassert>
#include <iostream>

class Array {
  double* array;
public:
  int length;
  Array(int);
  Array(const Array&);
  Array& operator=(const Array&);
  ~Array();
  double& operator[](int);
  const double& operator[](int) const;
};

Array::Array(int length) : array(NULL), length(length) {
  if (length > 0) {
    array = new double[length];
    for (int i = 0; i < length; i++) {
      array[i] = 0;
    }
  }
}

Array::Array(const Array& object) : array(NULL), length(object.length) {
  if (length > 0) {
    array = new double[length];
    for (int i = 0; i < length; i++) {
      array[i] = object.array[i];
    }
  }
}

Array& Array::operator=(const Array& object) {
  array = NULL;
  length = object.length;
  if (length > 0) {
    array = new double[length];
    for (int i = 0; i < length; i++) {
      array[i] = object.array[i];
    }
  }
  return *this;
}

Array::~Array() {
  if (array != NULL) {
    delete[] array;
    array = NULL;
  }
}

double& Array::operator[](int index) {
  assert(0 <= index && index < length);
  return array[index];
}

const double& Array::operator[](int index) const {
  assert(0 <= index && index < length);
  return array[index];
}

Array echo1(Array array) {
  return array;
}

const Array& echo2(const Array& array) {
  return array;
}

int main() {
  int length = 10;
  //Array array(length);
  auto array = Array(length);
  for (int i = 0; i < array.length; i++) {
    array[i] = i;
  }
  auto array1 = Array(length);
  array1 = array;
  for (int i = 0; i < array1.length; i++) {
    std::cout << array1[i] << std::endl;
  }
  for (int i = 0; i < echo1(array).length; i++) {
    std::cout << echo1(array)[i] << std::endl;
  }
  for (int i = 0; i < echo2(array).length; i++) {
    std::cout << echo2(array)[i] << std::endl;
  }
}

注)テンプレートクラスのメンバー関数の実装はヘッダーファイルに記述してください。


参考サイト

1.演算子のオーバーロード
2.subscript operator overloading
3.Overloading the C++ indexing subscript operator []
4.Overloading the Subscript Operator [] the Right Way
5.クラス内での配列の動的確保
6.関数とオブジェクト
7.オブジェクト利用時の注意点
8.迷信:new に失敗すると NULL が返る?
9.コピーコンストラクタ、代入演算子、デストラクタ(The Law of The Big Three)
10.コピー操作と参照
11.ロベールのC++教室 – クラステンプレート
12.テンプレートの実装をヘッダに書かなければならない理由

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中