개발/C, C++

[C++] 배열 인덱스 연산자 오버로딩 []

huiyu 2019. 1. 12. 18:11

배열 인덱스 연산자 오버로딩

 - 배열요소에 접근할 때 사용하는 [ ] 연산자 오버로딩


C, C++의 기본 배열은 다음의 단점을 갖고 있다.

 " 경계 검사를 하지 않는다."

때문에 다음과 같은 코드가 만들어지고 컴파일 되며 실행 될 수 있다.

1
2
3
4
5
6
7
8
int main(void)
{
  int arr[3= {123};
  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 배열의 인덱스 연산자 오버로딩

728x90
반응형