[열혈 C++] Chap11: 연산자의 오버로딩2 - 2) 인덱스 연산자 오버로딩
2. 배열의 인덱스 연산자 오버로딩
✏️배열 클래스
-
기본 배열은 ‘경계검사’를 하지 않기 때문에,
int arr[3] = {1,2,3};
arr[-1]
과 같은 엉뚱한 코드가 실행될 수도 있다. - 배열 클래스란 배열 역할을 하는 클래스이다.
-
예제
#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; } /*결과 11 22 33 44 55 Array index out of bound exception */
- 이와 같은 배열 클래스의 정의를 통해 배열 접근의 안전성을 보장받을 수 있다.
- 그러나, 저장소의 일종인 배열은 ‘유일성’이 보장되어야하기 때문에, 복사와 대입은 막는 것이 좋다.
✏️const 함수를 이용한 오버로딩
-
예제
#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]; } int GetArrLen() const { return arrlen; } ~BoundCheckIntArray() { delete []arr; } }; void ShowAllData(const BoundCheckIntArray& ref) { int len=ref.GetArrLen(); for(int idx=0; idx<len; idx++) cout<<ref[idx]<<endl; } int main(void) { BoundCheckIntArray arr(5); for(int i=0; i<5; i++) arr[i]=(i+1)*11; ShowAllData(arr); return 0; }
ShowAllData()
함수에서 매개변수가 배열의 데이터를 변경하지 못하도록 const로 선언되어있다. 그러나 문제는ref[idx]
가ref.operator[](idx);
라고 할 수 있고, 이 함수는 const함수가 아니므로 여기서 컴파일 에러가 난다는 것이다.- 따라서 const 선언을 해준 operator[]함수를 다시 정의한 것이다.
✏️객체 저장을 위한 배열 클래스 정의
-
클래스 객체를 저장하는 배열 기반 클래스
#include <iostream> #include <cstdlib> using namespace std; class Point { private: int xpos, ypos; public: Point(int x=0, int y=0) : xpos(x), ypos(y) { } friend ostream& operator<<(ostream& os, const Point& pos); }; ostream& operator<<(ostream& os, const Point& pos) { os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } class BoundCheckPointArray { private: Point * arr; int arrlen; BoundCheckPointArray(const BoundCheckPointArray& arr) { } BoundCheckPointArray& operator=(const BoundCheckPointArray& arr) { } public: BoundCheckPointArray(int len) :arrlen(len) { arr=new Point[len]; // Point 객체로 이뤄진 배열을 생성한다 // 생성자에 설정된 디폴트 값에 의해 모든 멤버가 0으로 초기화된다 } Point& operator[] (int idx) { if(idx<0 || idx>=arrlen) { cout<<"Array index out of bound exception"<<endl; exit(1); } return arr[idx]; } Point operator[] (int idx) const { if(idx<0 || idx>=arrlen) { cout<<"Array index out of bound exception"<<endl; exit(1); } return arr[idx]; } int GetArrLen() const { return arrlen; } ~BoundCheckPointArray() { delete []arr; } }; int main(void) { BoundCheckPointArray arr(3); arr[0]=Point(3, 4); // 임시 객체를 생성하여 배열을 초기화하고 있다. 디폴트 대입 연산이 호출된다 arr[1]=Point(5, 6); arr[2]=Point(7, 8); for(int i=0; i<arr.GetArrLen(); i++) cout<<arr[i]; return 0; }
- 객체의 저장은 객체간의 대입 연산을 기반으로 한다
-
클래스 객체의 주소 값을 저장하는 배열 기반 클래스
#include <iostream> #include <cstdlib> using namespace std; class Point { private: int xpos, ypos; public: Point(int x=0, int y=0) : xpos(x), ypos(y) { } friend ostream& operator<<(ostream& os, const Point& pos); }; ostream& operator<<(ostream& os, const Point& pos) { os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } typedef Point * POINT_PTR; // 저장,연산의 대상이 포인터인 경우 별도의 자료형을 정의하는 것이 좋다 class BoundCheckPointPtrArray { private: POINT_PTR * arr; int arrlen; BoundCheckPointPtrArray(const BoundCheckPointPtrArray& arr) { } BoundCheckPointPtrArray& operator=(const BoundCheckPointPtrArray& arr) { } public: BoundCheckPointPtrArray(int len) :arrlen(len) { arr=new POINT_PTR[len]; // Point객체의 주소값을 저장하는 배열이다 } POINT_PTR& operator[] (int idx) { if(idx<0 || idx>=arrlen) { cout<<"Array index out of bound exception"<<endl; exit(1); } return arr[idx]; } POINT_PTR operator[] (int idx) const { if(idx<0 || idx>=arrlen) { cout<<"Array index out of bound exception"<<endl; exit(1); } return arr[idx]; } int GetArrLen() const { return arrlen; } ~BoundCheckPointPtrArray() { delete []arr; } }; int main(void) { BoundCheckPointPtrArray arr(3); arr[0]=new Point(3, 4); arr[1]=new Point(5, 6); arr[2]=new Point(7, 8); for(int i=0; i<arr.GetArrLen(); i++) cout<<*(arr[i]); delete arr[0]; delete arr[1]; delete arr[2]; return 0; }
- 주소 값을 저장하는 경우 객체 생성과 소멸을 위해 new, delete 연산을 써야 하지만 복사에 대한 문제를 신경 쓸 필요 없기 때문에 더 많이 사용된다고 한다