배열 인덱스 연산자 오버로딩
- 배열요소에 접근할 때 사용하는 [ ] 연산자 오버로딩
C, C++의 기본 배열은 다음의 단점을 갖고 있다.
" 경계 검사를 하지 않는다."
때문에 다음과 같은 코드가 만들어지고 컴파일 되며 실행 될 수 있다.
1 2 3 4 5 6 7 8 | int main(void) { int arr[3] = {1, 2, 3}; cout<<arr[-1]<<endl; cout<<arr[-2]<<endl; cout<<arr[3]<<endl; cout<<arr[4]<<endl; } | cs |
이러한 단점을 해결하기 위한 배열 클래스 디자인
먼저, [ ] 연산자 오버로딩에 대해 알아보자.
arrObject[ 2 ];
arrObject가 객체의 이름이라고 한다면, 아래와 같이 유추해볼 수 있다.
- 객체 arrObject의 멤버함수를 호출한다.
- 연산자가 [ ] 이므로 멤버함수의 이름은 'operator []' 이다.
- 함수호출 시 전달되는 인자의 값은 정수 2이다.
아래와 같이 함수형태를 파악할 수 있다.(반환형 int는 임의로 결정)
int operator[ ] (int idx) { .... }
그래서
arrObject[ 2 ];
는 아래와 같이 해석될 수 있다.
arrObject.operator[ ] (2);
이를 기반으로 배열클래스를 디자인하면 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <iostream> #include <cstdlib> using namespace std; class BoundCheckIntArray { private: int * arr; int arrlen; public: BoundCheckIntArray(int len) : arrlen(len) { arr = new int[len]; } int& operator[] (int idx) { if(idx<0 || idx>=arrlen) { cout<<"Array index out of bound exception"<<endl; exit(1); } return arr[idx]; } ~BoundCheckIntArray() { delete []arr; } }; int main(void) { BoundCheckIntArray arr(5); for(int i=0; i<5; i++) { arr[i] = (i+1) * 11; } for(int i=0; i<6; i++) { cout<<arr[i]<<endl; } return 0; } | cs |
[출력결과]
11 22 33 44 55 Array index out of bound exception
* 안전성을 더 높이기 위해 아래와 같은 코드의 실행을 막는다면,
1 2 3 4 5 6 7 8 9 10 11 12 13 | int main(void) { BoundCheckIntArray arr(5); for(int i=0; i<5; i++) { arr[i] = (i+1) * 11; } BoundCheckIntArray cpy1(5); cpy1 = arr; // 안전하지 않은 코드 BoundCheckIntArray copy = arr; // 안전하지 않은 코드 return 0; } | cs |
다음과 같이 복사 생성자와 대입여산자를 private으로 선언해서 막는다.
1 2 3 4 5 6 7 8 9 10 11 | class BoundCheckIntArray { private: int * arr; int arrlen; BoundCheckIntArray(const BoundCheckIntArray& arr) { } BoundCheckIntArray& operator=(const BoundCheckIntArray& arr) { } public: ... }; | cs |
** 배열은 저장소의 일종이고, 저장소에 저장된 데이터는 '유일성'이 보장되어야 하기 때문에, 대부분의 경우 저장소의 복사는 불필요하거나 잘못된 일로 간주된다. 위의 코드에서 보이듯이 빈 상태로 정의된 복사 생성자와 대입 연산자를 private 멤버로 둠으로써 복사와 대입을 원천적으로 막는 것이 좋은 선택이 되기도 한다.
const 함수를 이용한 오버로딩의 활용
위 예제에서 배열의 저장된 모든 요소르르 출력하는 ShowAllData 함수를 추가한다.
1 2 3 4 5 6 | void ShowAllData(const BoundCheckIntArray& ref) { int len = ref.GetArrLen(); //BoundCheckIntArray클래스에서 배열의 길이를 반환하는 함수 추가 for(int idx=0; idx<len; idx++) cout<<ref[idx]<<endl; //컴파일 에러 발생 } | cs |
위 함수의 매개변수 ref는 아래와 같이 선언되어 있다.
const BoundCheckIntArray& ref
함수 내에서 배열에 저장된 데이터를 변경하지 못하도록 매개변수 형이 const로 선언되어있다.
그리고 이는 올바른 선언이라고 할 수 있다. 그런데 이 선언으로 인해 위 5행이 컴파일 에러가 발생한다.
위 5행은 아래와 같이 해석된다.
ref.operator[ ] (idx) ;
그런데 이 때 호출되는 BoundCheckIntArray클래스의 operator[]함수는 const가 아니기 때문에 에러가 발생한다.
* const형 변수는 const함수만 호출 가능하다.(-> const 키워드)
operator[] 함수에 const를 붙인다면?
이는 적절한 해결방법이 아니다. 이렇게 된다면 배열을 멤버로 선언하는 경우에 저장 자체가 불가능해진다.
-> 해결방법 : const의 선언유무도 함수오버로딩 조건에 해당하므로, operator[]함수를 const를 이용해 오버로딩한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <iostream> #include <cstdlib> using namespace std; class BoundCheckIntArray { private: int * arr; int arrlen; BoundCheckIntArray(const BoundCheckIntArray& arr) { } BoundCheckIntArray& operator=(const BoundCheckIntArray& arr) { } public: BoundCheckIntArray(int len) : arrlen(len) { arr = new int[len]; } int& operator[] (int idx) { if(idx<0 || idx>=arrlen) { cout<<"Array index out of bound exception"<<endl; exit(1); } return arr[idx]; } int& operator[] (int idx) const { if(idx<0 || idx>=arrlen) { cout<<"Array index out of bound exception"<<endl; exit(1); } return arr[idx]; } ~BoundCheckIntArray() { delete []arr; } }; void ShowAllData(const BoundCheckIntArray& ref) { int len = ref.GetArrLen(); //BoundCheckIntArray클래스에서 배열의 길이를 반환하는 함수 추가 for(int idx=0; idx<len; idx++) cout<<ref[idx]<<endl; //컴파일 에러 발생 } | cs |
[참고자료]
윤성우 열혈 C++ 프로그래밍 11-2 배열의 인덱스 연산자 오버로딩
'Software Development > C, C++' 카테고리의 다른 글
[C++] 템플릿(Template)에 대한 이해와 함수 템플릿 (0) | 2019.03.10 |
---|---|
[C++]new, delete 연산자 오버로딩 (0) | 2019.01.13 |
[C++] 연산자 오버로딩 - 디폴트 대입 연산자 (0) | 2019.01.12 |
[C++] 단항 연산자 오버로딩 (0) | 2019.01.03 |
[C++] 연산자 오버로딩 (0) | 2019.01.03 |