Language/C++

📌 Chapter 10 - 첨자 연산자 오버로딩 (Subscript Operator)

e-cko 2025. 9. 9. 09:50
반응형
반응형

1. 첨자 연산자란?

1) 기본 배열의 문제점

  • int arr[5];라고 하면, 숫자 5개를 담을 있는 배열이 생긴다.
  • 배열을 사용할 경계를 검사하지 않는다.

⚠️ 예시:

int arr[5];

cout << arr[-1] << endl;  // 이상한 출력, 프로그램이 위험해질 있음

  • arr[-1]처럼 존재하지 않는 칸을 참조해도 컴파일은 되고 실행도 된다.
  • 하지만 컴퓨터 메모리에서 엉뚱한 값을 읽거나 프로그램이 터질 있다.

2) operator[]?

  • [ ] 사용자가 만든 함수로 바꿔서 예방할 있다. → 첨자 연산자 오버로딩
  • , arrEx[2]라고 쓰면, 사실 내부적으로는 arrEx 객체의 operator[] 함수가 호출되어 2번째 칸의 위치(참조값) 가져오는 과정이다.

핵심:

  • operator 뒤에는 반드시 대괄호 [] 붙여야 한다.
  • 잘못된 표기: arrEx.operator = 10; → ❌ 컴파일 오류
  • 올바른 표기: arrEx[2] = 10; → 3번째 참조 10 저장

3) 참조형 반환

  • operator[] 참조형(int&)으로 반환되면,
  • arrEx[2] = 10;
  • 일반 int형으로 반환하면 읽기만 가능, 쓰기는 불가능

4) 배열 클래스 예시

class ExArr {

private:

    int* arr;       // 실제 숫자들을 저장하는 상자

    int arrlen;     // 배열 길이

public:

    ExArr(int len);           // 배열을 만들 크기 지정

    int& operator[](int idx); // 첨자 연산자

    ~ExArr();                 // 배열 메모리 해제

};

  • ExArr arr(5); → 길이 5짜리 배열 생성
  • arr[3] = 10; → operator[] 4번째 참조를 반환 10 저장

⚠️ 추가 설명:

  • 경계 검사: 잘못된 인덱스 접근 막기
  • 데이터 유일성: 복사 생성자/대입 연산자를 private으로 두면 안전

2. const 오버로딩

  • const 객체는 수정이 불가하다. , 읽기만 가능
  • const 객체에서 operator[] 쓰려면 const 버전 필요

int& operator[](int idx);       // 일반 객체

int& operator[](int idx) const; // const 객체

  • const 함수에서는 내부 데이터를 변경할 없다
  • 초보자 실수:

const ExArr c_arr(5);

c_arr[0] = 10;  // ❌ 컴파일 에러

  • 해결: const 버전 operator[] 정의 사용

3. 객체를 저장하는 배열

1) 객체 대입 기반

  • 배열 안에 기본 자료형 대신 객체를 넣음
  • 예시:

arr[0] = PTR(3,4);

  • 과정:
    1️⃣ arr[0] → operator[]
    호출참조 반환
    2️⃣ PTR&
    반환내부 좌표 접근
    3️⃣
    대입 연산자 = → 복사
  • 장점: 참조형 반환 덕분에 읽기/쓰기 모두 가능
  • ⚠️ 주의: 객체가 크면 복사 비용 발생

2) 포인터 기반 배열

  • 배열 안에 객체 포인터를 저장
  • 포인터로 동적 생성 → new/delete 사용 가능

typedef PTR* P_ptr;

P_ptr* arr; // PTR* 배열 (이중 포인터)

  • 과정:
  • 사용 delete arr[i]; 필수 ⚠️
  • 장점:

💻예제 코드

#include <iostream>

#include <cstdlib>

using namespace std;

 

class PTR {

private:

    int xpos, ypos;

public:

    PTR(int x=0, int y=0);

    friend ostream& operator<<(ostream& os, const PTR& pos);

};

PTR::PTR(int x,int y):xpos(x),ypos(y){}

ostream& operator<<(ostream& os, const PTR& pos){

    os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;

    return os;

}

 

typedef PTR* P_ptr;

 

class ExPTRArray{

private:

    P_ptr* arr;

    int arrlen;

    ExPTRArray(const ExPTRArray& arr);

    ExPTRArray& operator=(const ExPTRArray& arr);

public:

    ExPTRArray(int len) : arrlen(len) { arr = new P_ptr[len]; }

    P_ptr& operator[](int idx){

        if(idx<0||idx>=arrlen){ cout<<"index 범위 초과"<<endl; exit(1); }

        return arr[idx];

    }

    P_ptr& operator[](int idx) const{

        if(idx<0||idx>=arrlen){ cout<<"index 범위 초과"<<endl; exit(1); }

        return arr[idx];

    }

    int GetArrLen() const { return arrlen; }

    ~ExPTRArray() { delete[] arr; }

};

 

int main(){

    ExPTRArray arr(3);

    for(int i=0;i<3;i++)

        arr[i] = new PTR(i+i+3,i+i+4);

    for(int i=0;i<arr.GetArrLen();i++)

        cout<<*(arr[i]);

    for(int i=0;i<3;i++)

        delete arr[i];

    return 0;

}

실행 결과

[3,4]

[5,6]

[7,8]


마무리 정리

1️⃣ operator[] 참조 반환읽기/쓰기 모두 가능
2️⃣
경계 검사잘못된 인덱스 접근 방지
3️⃣ const
버전 operator[] → const 객체에서도 안전
4️⃣
객체 배열 대입내부 데이터 안전하게 복사 가능
5️⃣
포인터 배열 관리 → new/delete 안전한 메모리 관리
6️⃣
복사 생성자/대입 연산 차단포인터 안전 보장
7️⃣
참조형 반환읽기, 쓰기, 대입 연산 모두 안전
8️⃣
동적 객체 생성배열 크기 자유, 메모리 관리 가능

반응형