오버로딩 원칙 상 동일한 인자를 받는 함수에 대해서는 한 가지 리턴 타입만이 가능. int의 Wrapper 클래스 객체를 리턴에서 실제로 int 값에 접근할 때에는 int 변수 처럼 행동하고, 원소에 접근해 나가는 중간 단계의 산물일 경우, 그 중간 단계의 정보를 포함하는 것으로 사용하자.
friend 키워드를 사용할때는, 컴파일러가 해당 자료형이 무엇인지 알 수 있도록 클래스를 미리 선언해줘야할 때가 있다.
#include <iostream>
namespace MyArray {
class Array;
class Int;
class Array {
friend Int;
const int dim; // 몇 차원 배열 인지
int* size; // size[0] * size[1] * ... * size[dim - 1] 짜리 배열이다.
struct Address {
int level;
// 맨 마지막 레벨(dim - 1 레벨) 은 데이터 배열을 가리키고, 그 위 상위
// 레벨에서는 다음 Address 배열을 가리킨다.
void* next;
};
Address* top;
public:
class Iterator {
int* location;
Array* arr;
friend Int;
public:
Iterator(Array* arr, int* loc = NULL) : arr(arr) {
location = new int[arr->dim];
for (int i = 0; i != arr->dim; i++)
location[i] = (loc != NULL ? loc[i] : 0);
}
Iterator(const Iterator& itr) : arr(itr.arr) {
location = new int[arr->dim];
for (int i = 0; i != arr->dim; i++) location[i] = itr.location[i];
}
~Iterator() { delete[] location; }
// 다음 원소를 가리키게 된다.
Iterator& operator++() {
if (location[0] >= arr->size[0]) return (*this);
bool carry = false; // 받아 올림이 있는지
int i = arr->dim - 1;
do {
// 어차피 다시 돌아온다는 것은 carry 가 true
// 라는 의미 이므로 ++ 을 해야 한다.
location[i]++;
if (location[i] >= arr->size[i] && i >= 1) {
// i 가 0 일 경우 0 으로 만들지 않는다 (이러면 begin 과 중복됨)
location[i] -= arr->size[i];
carry = true;
i--;
}
else
carry = false;
} while (i >= 0 && carry);
return (*this);
}
Iterator& operator=(const Iterator& itr) {
arr = itr.arr;
location = new int[itr.arr->dim];
for (int i = 0; i != arr->dim; i++) location[i] = itr.location[i];
return (*this);
}
Iterator operator++(int) {
Iterator itr(*this);
++(*this);
return itr;
}
bool operator!=(const Iterator& itr) {
if (itr.arr->dim != arr->dim) return true;
for (int i = 0; i != arr->dim; i++) {
if (itr.location[i] != location[i]) return true;
}
return false;
}
Int operator*();
};
friend Iterator;
Array(int dim, int* array_size) : dim(dim) {
size = new int[dim];
for (int i = 0; i < dim; i++) size[i] = array_size[i];
top = new Address;
top->level = 0;
initialize_address(top);
}
Array(const Array& arr) : dim(arr.dim) {
size = new int[dim];
for (int i = 0; i < dim; i++) size[i] = arr.size[i];
top = new Address;
top->level = 0;
initialize_address(top);
}
// address 를 초기화 하는 함수이다. 재귀 호출로 구성되어 있다.
void initialize_address(Address* current) {
if (!current) return;
if (current->level == dim - 1) {
current->next = new int[size[current->level]];
return;
}
current->next = new Address[size[current->level]];
for (int i = 0; i != size[current->level]; i++) {
(static_cast<Address*>(current->next) + i)->level = current->level + 1;
initialize_address(static_cast<Address*>(current->next) + i);
}
}
void delete_address(Address* current) {
if (!current) return;
for (int i = 0; current->level < dim - 1 && i < size[current->level]; i++) {
delete_address(static_cast<Address*>(current->next) + i);
}
delete[] current->next;
}
Int operator[](const int index);
~Array() {
delete_address(top);
delete[] size;
}
Iterator begin() {
int* arr = new int[dim];
for (int i = 0; i != dim; i++) arr[i] = 0;
Iterator temp(this, arr);
delete[] arr;
return temp;
}
Iterator end() {
int* arr = new int[dim];
arr[0] = size[0];
for (int i = 1; i < dim; i++) arr[i] = 0;
Iterator temp(this, arr);
delete[] arr;
return temp;
}
};
class Int {
void* data;
int level;
Array* array;
public:
Int(int index, int _level = 0, void* _data = NULL, Array* _array = NULL)
: level(_level), data(_data), array(_array) {
if (_level < 1 || index >= array->size[_level - 1]) {
data = NULL;
return;
}
if (level == array->dim) {
// 이제 data 에 우리의 int 자료형을 저장하도록 해야 한다.
data = static_cast<void*>((
static_cast<int*>(static_cast<Array::Address*>(data)->next) + index));
}
else {
// 그렇지 않을 경우 data 에 그냥 다음 addr 을 넣어준다.
data = static_cast<void*>(static_cast<Array::Address*>(
static_cast<Array::Address*>(data)->next) +
index);
}
};
Int(const Int& i) : data(i.data), level(i.level), array(i.array) {}
operator int() {
if (data) return *static_cast<int*>(data);
return 0;
}
Int& operator=(const int& a) {
if (data) *static_cast<int*>(data) = a;
return *this;
}
Int operator[](const int index) {
if (!data) return 0;
return Int(index, level + 1, data, array);
}
};
Int Array::operator[](const int index) {
return Int(index, 1, static_cast<void*>(top), this);
}
Int Array::Iterator::operator*() {
Int start = arr->operator[](location[0]);
for (int i = 1; i <= arr->dim - 1; i++) {
start = start.operator[](location[i]);
}
return start;
}
}
int main() {
int size[] = { 2, 3, 4 };
MyArray::Array arr(3, size);
MyArray::Array::Iterator itr = arr.begin();
for (int i = 0; itr != arr.end(); itr++, i++) (*itr) = i;
for (itr = arr.begin(); itr != arr.end(); itr++)
std::cout << *itr << std::endl;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 4; k++) {
arr[i][j][k] = (i + 1) * (j + 1) * (k + 1) + arr[i][j][k];
}
}
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 4; k++) {
std::cout << i << " " << j << " " << k << " " << arr[i][j][k]
<< std::endl;
}
}
}
return 0;
}