3 minute read

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 연산을 써야 하지만 복사에 대한 문제를 신경 쓸 필요 없기 때문에 더 많이 사용된다고 한다

Tags: ,

Categories:

Updated: