2023. 8. 13. 14:18ㆍProgramming Language/C++
- 참조자
C++의 참조자(Reference)는 기존 변수에 대한 별칭(alias)를 만들어내는 개념이다. 참조자는 포인터와 다르게 반드시 초기화되어야 하며, 한 번 초기화되면 다른 변수를 참조하는 데 계속 사용된다. 참조자를 사용하면 변수에 대한 별칭을 만들어 원래 변수를 직접 조작할 수 있다.
참조자의 주요 특징은 다음과 같다
- 초기화 필요: 참조자는 반드시 선언과 동시에 초기화되어야 한다. 참조자를 선언하면서 어떤 변수를 참조할지 명시적으로 지정해야 한다.
- 별칭: 참조자는 변수에 대한 별칭이기 때문에, 변수와 참조자는 메모리상 같은 위치를 가리키며 같은 데이터를 가리킨다.
- 포인터와 유사: 참조자는 포인터와 유사한 개념이지만, 포인터와 달리 널(null)로 초기화되지 않으며, 주소 산술(address arithmetic)을 할 수 없다.
- 간결성과 가독성: 포인터 대신 참조자를 사용하면 코드가 간결해지고 가독성이 좋아진다. 포인터 연산이 필요하지 않기 때문에 코드 작성이 더 편리해진다.
- 함수 호출 시 효율성: 함수 호출 시 참조자를 이용하여 인자를 전달하면, 복사본이 아닌 원래 변수를 함수 내에서 조작할 수 있으므로 효율적인 인자 전달이 가능하다.
- 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 참조자를 사용하여 상수를 참조할 경우, 해당 참조자를 통해 값을 변경하는 것이 불가능하다. 이는 상수의 불변성을 보장하며, 상수의 값이 의도치 않게 변경되는 것을 방지한다.
주의해야 할 중요한 사항:
- 상수에 대한 참조자는 상수로 초기화되어야 한다.
- const 참조자를 통해 값을 변경하는 것은 컴파일 오류를 일으킨다.
- 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 |