참조자

김민수·2025년 1월 8일

C++

목록 보기
19/68

참조자는 포인터와 유사하지만, 기존 변수에 대한 별칭(Alias)을 제공하는 개념으로, 포인터와는 몇 가지 중요한 차이가 있다.



1. 기본 개념

int val = 7;
int& ref = val;
ref = 20;
cout << val << endl; // 20 출력
  • refval이 저장된 메모리 공간을 참조한다.
  • ref에 값을 대입하면 원본 변수 val도 변경된다.
  • 참조자는 포인터처럼 메모리 주소를 직접 들고 있지는 않지만, 컴파일러가 내부적으로 참조 대상을 가리키도록 처리한다.


2. 특징

  1. 초기화 필수: 선언과 동시에 반드시 유효한 대상을 참조해야 한다.
  2. 참조 대상 변경 불가: 참조자로 지정된 대상을 변경할 수 없다.
  3. NULL 값 불가: 참조자는 항상 유효한 대상을 가리켜야 하므로, 포인터와 달리 NULL 상태가 될 수 없다.
  4. 문법의 단순함: 별도의 간접 참조 연산 없이 참조자를 통해 바로 값에 접근할 수 있다.
    • 포인터: *p_val
    • 참조자: ref


3. 함수 매개변수 활용

void increase(int& ref) {
    ref += 10;
}

void increase2(int val) {
    val += 10;
}

int main() {
    int num = 5;
    increase(num);
    cout << num << endl; // 15 출력

    increase2(num);
    cout << num << endl; // 15 출력 (num은 변하지 않음)
}
  • Call by Reference: 참조자를 매개변수로 사용하면, 함수 내부에서 값 변경 시 원본 변수에 바로 적용된다.
  • Call by Value: 값 복사로 전달된 경우, 함수 내에서의 변경은 지역 변수에만 영향을 미치며 원본 변수는 변화하지 않는다.


4. 간접 참조와의 비교

void increase3(int* p_val) {
    *p_val += 10;
}
  • 포인터를 사용하면 간접 참조 문법이 필요하므로 코드가 복잡해질 수 있다.
  • 참조자는 이러한 간접 참조 문법을 생략하여 가독성을 높인다.


5. const 참조자

void print(const int& ref) {
    cout << ref << endl;
}
  • const를 사용하여 참조자가 읽기 전용으로 동작하도록 설정할 수 있다.
  • 함수 내부에서 참조값 변경이 불가능하므로 원본 데이터의 손상을 방지할 수 있다.


6. 주의 사항

소멸된 객체를 참조하지 않기

int& get_ref() {
    int x = 42; // 지역 변수
    return x;
}

int main() {
    int& ref = get_ref();
    cout << ref; // 비정상 동작 가능
}
  • 지역 변수의 참조를 반환하면, 함수 종료 후 스택 메모리가 소멸하면서 참조자는 유효하지 않게 된다.
  • 이런 경우 undefined behavior(정의되지 않은 동작)를 초래할 수 있다.


7. 포인터와 참조자 비교

특징참조자포인터
초기화반드시 초기화 필요초기화 없이 선언 가능
NULL 가능 여부불가능가능 (nullptr 사용)
참조 대상 변경 가능 여부불가능가능
메모리 관리자동 (대상 객체 생명 주기와 동일)개발자가 직접 관리
문법의 단순성간단함복잡함 (*, & 연산 필요)


8. 동적 메모리 할당 시 포인터 사용

  • 참조자는 가리키는 객체의 생존 주기에 종속된다.
  • 반면, 포인터는 동적 메모리를 할당하거나 해제하는 작업에 적합하다.
  • 동적 메모리 할당 필요 시 포인터 사용:
    • 크기를 컴파일 시점에 알 수 없는 경우
    • 함수 종료 후에도 데이터를 유지해야 하는 경우
int* ptr = new int(10); // 동적 메모리 할당
*ptr = 20;
delete ptr; // 메모리 해제


9. 요약

  1. 참조자의 장점:
    • 가독성과 편의성 향상
    • Call by Reference를 통해 원본 데이터를 안전하고 직관적으로 처리 가능
  2. 참조자의 제약:
    • 항상 유효한 대상을 참조해야 하고, 참조 대상 변경 불가
    • 동적 메모리 관리에는 부적합
  3. 포인터와의 구분:
    • 동적 메모리 관리가 필요하지 않은 경우, 참조자를 우선적으로 고려
    • 동적 메모리를 명시적으로 할당/해제해야 하는 경우, 포인터 사용

참조자는 코드의 안정성을 높이고 가독성을 향상시키는 데 유용하지만, 특정 상황에서는 포인터의 유연함이 더 필요할 수 있다. 상황에 맞게 적절한 방식을 선택하는 것이 중요하다.

profile
안녕하세요

0개의 댓글