[C++] 참조자

2023. 8. 13. 14:18Programming Language/C++

728x90
  • 참조자

C++의 참조자(Reference)는 기존 변수에 대한 별칭(alias)를 만들어내는 개념이다. 참조자는 포인터와 다르게 반드시 초기화되어야 하며, 한 번 초기화되면 다른 변수를 참조하는 데 계속 사용된다. 참조자를 사용하면 변수에 대한 별칭을 만들어 원래 변수를 직접 조작할 수 있다.

참조자의 주요 특징은 다음과 같다

 

  1. 초기화 필요: 참조자는 반드시 선언과 동시에 초기화되어야 한다. 참조자를 선언하면서 어떤 변수를 참조할지 명시적으로 지정해야 한다.

  2. 별칭: 참조자는 변수에 대한 별칭이기 때문에, 변수와 참조자는 메모리상 같은 위치를 가리키며 같은 데이터를 가리킨다.

  3. 포인터와 유사: 참조자는 포인터와 유사한 개념이지만, 포인터와 달리 널(null)로 초기화되지 않으며, 주소 산술(address arithmetic)을 할 수 없다.

  4. 간결성과 가독성: 포인터 대신 참조자를 사용하면 코드가 간결해지고 가독성이 좋아진다. 포인터 연산이 필요하지 않기 때문에 코드 작성이 더 편리해진다.

  5. 함수 호출 시 효율성: 함수 호출 시 참조자를 이용하여 인자를 전달하면, 복사본이 아닌 원래 변수를 함수 내에서 조작할 수 있으므로 효율적인 인자 전달이 가능하다.

 

 

  • c++에서의 &변수와 c언어에서의 *변수의 차이점

 C++의 &변수는 참조자(Reference)를 나타내며, C 언어의 *변수는 포인터(Pointer)를 나타낸다. 이 두 가지 개념은 다음과 같은 차이점을 가지고 있다

 

C++의 &변수(참조자) 

#include <iostream>

int main() {
    int original = 10;
    int& ref = original; // 참조자 생성

    // 참조자는 반드시 초기화되어야 함
    // 아래 주석을 해제하면 컴파일 오류 발생
    // int& ref2; 

    // 참조자는 주소 연산을 할 수 없음
    // 아래 주석을 해제하면 컴파일 오류 발생
    // int& ref3 = &original; 

    return 0;
}

위 코드에서 주석을 해제하면 int& ref2;와 int& ref3 = &original; 줄에서 컴파일 오류가 발생한다. 이는 참조자가 반드시 선언과 동시에 초기화되어야 하며, 주소 연산(&)을 통한 주소 값을 할당할 수 없다는 것을 보여준다.

 

 

C언어의 *변수(포인터)

#include <stdio.h>

int main() {
    int original = 10;
    int *ptr = &original; // 포인터 생성

    // 포인터는 초기화하지 않아도 됨
    int *ptr2;

    // 포인터는 주소 연산을 통해 주소 값을 할당할 수 있음
    int *ptr3 = &original; 

    return 0;
}

C 언어의 포인터에서는 초기화하지 않은 포인터를 사용하거나, 주소 연산(&)을 통해 주소 값을 할당하는 것이 가능하다.

 

 

  • 참조자는 메모리 상에 존재하지 않을 수 있다

일반적으로 C++의 레퍼런스(참조자)는 변수를 참조하는 별칭으로, 메모리 상에서 원본 변수와 같은 위치를 가리키지만, 특별한 경우에 레퍼런스가 메모리 상에 존재하지 않을 수 있다. 이러한 상황은 주로 컴파일러의 최적화 과정에서 발생한다.

#include <iostream>

int main() {
    int original = 42;
    int& ref = original;

    std::cout << "original: " << original << std::endl;
    std::cout << "ref: " << ref << std::endl;

    original = 56;

    std::cout << "original after change: " << original << std::endl;
    std::cout << "ref after change: " << ref << std::endl;

    return 0;
}

위 코드에서 int& ref = original; 라인에서 ref는 original 변수의 레퍼런스로 초기화된다. 이 경우 ref는 original 변수와 동일한 메모리 위치를 가리키게 된다. 따라서 ref를 통해 값을 수정하면 original 변수의 값도 함께 수정된다.

그러나 컴파일러가 최적화 과정에서, 코드가 레퍼런스를 실제로 사용하지 않을 때 레퍼런스를 메모리 상에 생성하지 않을 수 있다.

 

#include <iostream>

int main() {
    int original = 42;
    int& ref = original;

    if (false) {
        std::cout << "ref: " << ref << std::endl;
    }

    return 0;
}

위 코드에서 if (false) 조건문은 항상 거짓이므로 ref를 사용하는 코드가 실행되지 않는. 이 경우, 컴파일러는 최적화를 통해 ref를 메모리 상에 생성하지 않을 수 있습니다. 그 결과, ref가 메모리 상에 실제로 존재하지 않게 된다.

이러한 최적화는 레퍼런스가 사용되는 컨텍스트와 코드 구조에 따라 달라질 수 있다. 최적화를 통해 레퍼런스가 메모리 상에 존재하지 않는 경우, 일반적으로 성능 개선을 가져오지만, 이러한 동작은 컴파일러에 의해 결정되므로 예측하기 어려울 수 있다.

 

 

  • 상수에 대한 참조자

C++에서 상수에 대한 참조자를 사용하는 것은 몇 가지 주의 사항과 규칙이 있다. 

 

1. 일반적인 참조자
일반적인 참조자는 변수에 대한 별칭으로서, 값을 변경할 수 있다.

int main() {
    int a = 42;
    int &ref = a;  // 일반적인 참조자
    ref = 10;      // a의 값이 변경됨

    return 0;
}

 

 

2. const 참조자
const 참조자는 변수에 대한 별칭으로서, 값을 변경하지 않겠다는 의미로 사용된다. 이는 상수에 대한 참조로서 사용된다.

int main() {
    const int b = 42;
    const int &const_ref = b;  // const 참조자

    // 아래 주석을 해제하면 컴파일 오류 발생
    // const_ref = 10;  // 값을 변경할 수 없음

    return 0;
}

const 참조자를 사용하여 상수를 참조할 경우, 해당 참조자를 통해 값을 변경하는 것이 불가능하다. 이는 상수의 불변성을 보장하며, 상수의 값이 의도치 않게 변경되는 것을 방지한다.

주의해야 할 중요한 사항:

 

  1. 상수에 대한 참조자는 상수로 초기화되어야 한다.
  2. const 참조자를 통해 값을 변경하는 것은 컴파일 오류를 일으킨다.
  3. const 참조자를 사용하면 상수 값을 변경할 수 없으므로, 상수의 불변성을 유지하는 데 도움이 된다.

 

'Programming Language > C++' 카테고리의 다른 글

[C++] 쓰레드 (thread)  (1) 2023.08.13
[C++] 객체, 함수 오버로딩  (0) 2023.08.13
[C++] std, namespcae  (0) 2023.08.12
[C++] STL  (0) 2023.08.11