참조자 (Reference)
변수의 별명
참조자는 (새로운) 변수가 아님
선언과 동시에 초기화가 되어야 함(Null일 수 없다)
한번 초기화되면, 다른 변수의 참조자가 될 수 없다.
const pointer이면서, 사용시 자동으로 역참조를 수행하는 개념
→ 포인터의 간단하고 편리한 버전으로 생각
함수의 매개변수로 자주 사용
int&, float&등 변수 정의시에 &를 붙인 타입을 사용
포인터와 마찬가지로, 동일한 타입에 대해서만 참조자 생성 가능
참조자를 사용할 때는 마침 a인 것처럼 그냥 사용(*, & 없음)
int main()
{
int a = 10;
int& b = a;
b = 20; // 사용할 때는 그냥 사용
cout << a << endl; // 20
cout << b << endl; // 20
int& c; // error! 초기화가 반드시 필요
}
void Increment(int val){
val++;
}
void IncrementByReference(int& val){
val++;
}
int main(){
int a = 5;
Increment(a);
cout << a << endl; // 5
IncrementByReference(a);
cout << a << endl; // 6
return 0;
}
copy가 필요한 경우가 아니라면, const &를 쓰는 것이 기본
→ const & rvalue 전달 가능
void PrintConstRef(const int& val){
cout << val << endl;
}
void PrintAddress(int* valPtr){
cout << *valPtr << endl;
}
void PrintRef(int& val){
cout << val << endl;
}
void PrintVal(int val){
cout << val << endl;
}
int main(){
int a = 5;
PrintVal(a);
PrintRef(a);
PrintConstRef(a);
PrintAddress(&a);
return 0;
} //전부 5 출력
참조자가 새로운 변수가 아니라는 뜻의 의미
메모리 디버깅을 통해 a의 주소와 b의 주소를 살펴보면 동일하다.
즉, b가 (포인터와 달리)메모리 공간을 차지하는 새로운 변수는 아니라는 의미
(컴파일 시에 참조자는 가리키는 원본의 메모리 주소로 대채되는 방식)
int main(){
int a = 2;
int& b = a;
int* c = &b;
cout << (c == &a) << endl; // true
return 0;
}
l-values and r-values
l-values
이름을 가지며, 주소를 갖는 값
const가 아니라면 수정이 가능한 값
int x = 100; // x is l-value
string name = "Kimm"; //name is l-value
r-value
주소를 갖지 않고, 대입의 대상이 될 수 없음
L-value가 아닌 것들
→ 대입식의 오른쪽에 위치
→ 리터럴 등
int x = 100; // 100 is r-values
string naem = "Kimmmm" // "Kimmmm" is r-value
int max_num = max(20, 30); // max(20, 30) is r-value
기본적으로는 l-value인 경우에만 참조자를 만들 수 있다.
const reference인 경우
r-value reference에 대한 문법 또한 존재
int x = 100;
int& ref1 = x;
ref1 = 200;
int& ref2 = 100; // error 100 is not l-value
const int& ref3 = 100; // allowed
int&& ref4 = 100; //allowed, r-value reference
Pointers vs References
*의 사용
변수를 정의할 때 붙는다 → 포인터의 정의(포인터 변수의 생성)
int* a
변수를 사용할 때 붙는다 → 포인터의 역참조
*a
&의 사용
변수를 정의할때 붙는다 → 참조자의 정의(참조자의 생성)
int& b
변수를 사용할때 붙는다 → 변수의 주소값 반환
&a
Pass-by-value
void func(int a)
함수가 실제 매개변수를 수정하지 않는 경우 사용
int, char, double과 같은 크기가 작은 기본 자료형의 전달
기본 자료형이 아닌 자료형 → string, vector, class
→ 복사 비용이 높기 때문
포인터를 사용한 Pass-by-address
void func(int* a)
함수가 실제 매개변수를 수정해야 하는 경우
→ 즉, 지역 범위 밖에 메모리에 저장된 값을 수정해야 하는 경우
복사의 오버헤드가 클때
포인터가 nullptr이 되어도 상관 없을 때
→참조자는 null이 될 수 없다
const의 포인터를 사용한 pass-by-address
void func(const int* a)
함수가 실제 매개변수를 수정하지 않는 경우
복사의 오버헤드가 클때
포인터가 nullptr이 되어도 상관 없을 때
→ 참조자는 null이 될 수없다
const의 const인 포인터를 사용한 pass-by-address
void func(const int* const a)
함수가 실제 매개변수를 수정하지 않는 경우
복사의 오버헤드가 클때
포인터가 nullptr이 되어도 상관 없을 때
→ 참조자는 null이 될 수 없다.
포인터가 바뀌지 않아야 할때
참조자를 사용한 Pass-by-reference
void func(int& a)
함수가 실제 매개변수를 수정하는 경우
복사의 오버헤드가 클 때
매개변수가 null이 되지 않는 것이 보장될 때
const 참조자를 사용한 Pass-by-const-reference
void func(const int& a)
함수가 실제 매개변수를 수정하지 않는 경우
복사의 오버헤드가 클때
매개변수가 null이 되지 않는 것이 보장될 때
'Language > C++' 카테고리의 다른 글
08.생성자와 소멸자 (0) | 2023.12.22 |
---|---|
07.OOP (0) | 2023.12.22 |
05.포인터 (0) | 2023.12.22 |
04.Function (1) | 2023.12.22 |
03.basic-syntax (1) | 2023.12.22 |
댓글