C언어와 다르게 C++에서는 참조형 변수를 사용할 수 있습니다.
C++에서 사용할 수 있는 변수의 종류는 총 3가지입니다.
int a = 2; // 일반 변수
int *b = &a; // 포인터 변수
int &c = a; // 참조형 변수
1. 일반 변수: 직접 값을 보유
2. 포인터 변수: 다른 값의 주소(또는 null)를 보유
3. 참조형 변수: 다른 객체 또는 값의 별칭으로 사용
참조형 변수는 주소와 동일하게 &기호를 사용하여 선언할 수 있습니다.
주소를 나타내는 &와 구분되는 점은 int와 같은 자료형 바로 뒤에서 사용된다는 점입니다.
(int *a와 비슷하게 int &a와 같이 사용됩니다.)
참조형 변수를 사용할 때는 몇 가지를 주의해야 합니다.
- 선언과 동시에 초기화해야 합니다.
- 초기화된 후에는 다른 변수를 참조하도록 변경할 수 없습니다.
- non-const값에 대한 참조는 const값 또는 r-value로 초기화할 수 없습니다.
non-const값에 대한 참조? r-value? 와 같은 낯선 용어들이 등장했습니다.
이에 대해서 더 자세히 살펴보겠습니다.
L-value와 R-value
각 단어의 정의를 살펴보면 다음과 같습니다.
- L-value: 객체를 참조하는 표현식 [메모리 위치를 가지고 있다]
- R-value: Lvalue 가 아닌 모든 것
R-value가 사용돼야 하는 곳에 L-value를 사용할 수 있지만
L-value가 필요한 곳에는 R-value가 사용할 수는 없습니다.
자세한 예시는 아래 사이트에 잘 정리되어 있습니다.
C++ 참조형의 다양한 형태
어떤 값을 참조했는가에 따라 참조형 변수는 각각의 특징을 가집니다.
참조형 변수의 유형과 특징을 살펴보겠습니다.
1. non-const값 참조
int a = 1;
int &b = a;
cout << b << '\n'; // 1
b = 3;
cout << a << '\n'; // 3
상수가 아닌 값을 참조할 경우는 참조형 변수는 선언과 동시에 일반 변수와 동일한 형태로 초기화됩니다.
단 참조한 변수와 참조형 변수 모두 같은 주소를 가리키게 됩니다.
2.const값 참조
const int a = 1;
int &b = a; // Error
const int &c = a; // Ok
상수 값을 참조하는 경우에 const int &형식으로 참조해야 합니다.
(그렇지 않은 경우 에러가 발생합니다.)
참조한 변수와 참조형 변수 모두 const 값이기에 변경이 불가능합니다.
3. r-value 참조
class A {
int data;
public:
A(A&& rhs) {
// ...
}
}
우측값 참조는 값을 이동할 때 필요 없는 복사가 발생하지 않도록 해 메모리를 효율적으로 사용할 수 있도록 도와줍니다.
간단한 형태의 연산에서는 컴파일러가 자동으로 쓸모없는 복사를 진행하지 않도록 도와주는 복사 생략이 진행됩니다.
하지만 복잡한 형태에서는 복사 생략이 가능하도록 r-value참조가 가능한 생성자를 추가해주어야 합니다.
이와 관련된 내용은 해당 문서에서 다루기에는 너무 복잡하여 추후에 보다 자세히 다루도록 하겠습니다.
참조형의 특징
참조형은 주로 함수의 인자로 값을 전달할 때 사용됩니다.
void fun1(int x)
{
x = 2;
}
void fun2(int& x)
{
x = 3;
}
int main()
{
int a = 0;
fun1(a);
cout << a; // 0
fun2(a);
cout << a; // 3
return 0;
}
참조형으로 인자를 전달할 경우 call by reference와 같이
전달된 함수 내에서 변수 값을 변경하면 외부에서도 동일하게 적용됩니다.
참조형으로 인자를 전달할 경우 값을 가상의 공간에서 새로 생성하지 않고 전달하게 됩니다.
그렇기에 훨씬 효율적으로 시간과 공간을 사용할 수 있습니다.
주의사항
1. 배열의 참조를 인자로 전달
배열의 참조를 전달할 때는 배열의 크기를 명시해주어야 합니다.
void func1(int (&a)[])
{
cout << a[0];
}
void func2(int (&a)[4])
{
cout << a[0];
}
int main() {
int a[4] = {};
func1(a); // Error
func2(a); // Ok
return 0;
}
만약 예제 코드 내 func1과 같이 배열의 크기를 명시하지 않는다면
int [4]를 int (&)[]로의 변환이 불가능하여 컴파일 단계에서 에러를 발생시킵니다.
std::vector를 사용한다면 이러한 상황을 사전에 예방할 수 있습니다.
2. 참조형 변수에 상수 값 전달
아래 예시 코드와 같이 참조형 변수에 상수를 전달할 때 오류가 발생하는 경우가 있습니다.
void foo1(const int &x)
{
cout << x;
}
void foo2(int &x)
{
cout << x;
}
int main()
{
foo1(6); //Ok
foo2(6); //Error
}
참조형 변수를 전달할 경우 함수 내부에서 값을 변경하게 될 경우 외부에도 동일하게 적용되어야 합니다.
하지만 상수는 변경이 불가능하므로 참조형 변수에 상수를 전달할 경우 에러가 발생합니다.
인자에 &를 제거해 일반형 변수로 만들거나 const를 앞에 붙여 상수형 참조 값으로 만들어 주면 에러를 해결할 수 있습니다.
지금까지 참조연산자에 대해 알아보았습니다.
참조연사자를 사용한다면 포인터 연산자보다 직관적인 코드를 만들 수 있으며
직접 값에 접근할 수 있어 배열의 크기를 따로 전달해주지 않아도 크기를 구할 수 있는 장점등이 있습니다.
참조 연산자를 통해 직관적이면서 메모리를 효율적으로 사용하는 코드를 작성해 보시길 바랍니다.
'Language & FrameWork > C++' 카테고리의 다른 글
[C++] strtok() 함수를 활용하여 문자열 토크나이징하기 (0) | 2020.10.02 |
---|---|
[C++] std::advance(),std::distance() 임의 접근 생성자 (0) | 2020.05.04 |
[C++11]emplace 함수 (0) | 2020.03.17 |
[C++] 메모리 관리 방법 정리 (0) | 2020.03.17 |