어떤 변수의 타입 뒤에 &를 붙이면 그 변수는 참조 변수가 된다.
개발자가 코드에서 다루는 방법은 일반 변수와 같지만 코드 내부적으로는 변수에 대한 포인터로 취급된다.
int x = 32;
int& x_refer = x;
이게 왜있나 싶을 수도 있는데, 주로 함수에 매개변수로 전달하는 과정에서 유용하게 사용된다.
레퍼런스 전달 방식의 원본값 변경에 대한 이점
만약 원본 변수의 값을 변경하는 함수를 만들고 싶다면 C언어에서는 복잡한 포인터 연산을 수행할 수 밖에 없었다.
하지만 C++에서는 포인터 연산 대신 레퍼런스 전달 방식을 활용하면 편리하다. 아래는 예시
#include <istream>
#include <iostream>
using namespace std;
class Me {
public:
int number;
Me(int num) : number(num) {}
};
int add(int i) {
return i + 1;
}
void add_refer(int& i) {
i++;
}
void add(Me* me) {
me->number ++;
}
int main() {
Me me = Me(3);
// 레퍼런스 전달
add_refer(me.number);
cout << me.number;
// 포인터 전달
add(&me);
cout << me.number;
// r-value 전달 후의 리턴값을 다시 대입
me.number = add(me.number);
cout << me.number;
// r-value 전달
add(me.number);
cout << me.number;
return 0;
}
r-value, 즉 리터럴 상수를 전달하면 변수의 원본값이 변경되지 않는다.
레퍼런스 전달 방식의 메모리 상 이점
일반적으로 함수에 매개변수로 전달한 변수는 값 전달 방식(pass by value)로 처리된다.
그러므로 함수에 전달되는 과정에서 해당 함수가 실행되면 변수의 복사가 일어나게된다.
int Sum(int num1, int num2){
return num1+num2;
}
int a = 5;
int b = 10;
int main(){
Sum(a,b);
}
main에서 실행한 Sum 함수는 내부적으로는
int Sum(int num1, int num2){
int infunction_1 = num1;
int infunction_2 = num2;
return num1+num2;
}
이런 불필요한 복사를 진행 후에야 개발자가 의도한 행위를 하고있다!
물론 매개변수가 차지하는 메모리의 크기가 크지않고, 많이 반복되지 않는다면 크게 문제가 되진 않겠지만,
vector<vector<int>> DoComplexThing(myclass c1, myclass c2, vector<vector<vector<int>>> weights){
//복잡한 무언가
vector<vector<int>> result;
return result;
}
대충 이런 상황이라면 함수 내 파라미터 복사가 부담스러울 수 있다.
위 코드에 레퍼런스 전달 방식을 적용하면
vector<vector<int>> DoComplexThing(myclass& c1, myclass& c2, vector<vector<vector<int>>>& weights){
//복잡한 무언가
vector<vector<int>> result;
return result;
}
이런 형태가 될텐데, 함수에 전달한 c1, c2 인스턴스와 3차원 벡터 weigths 변수의 실제 값들이 변경되기를 의도했다면 여기서 끝이지만, 전달한 변수의 원본 값이 변경되지 않기를 원하지만, 변수의 복사로 인한 메모리 사용이 걱정된다면 추가로 적용해야 할 것이 있다. 바로 const 키워드!
const reference 전달 방식
- 원본 변수는 변경하지 않으면서 함수에 매개변수가 전달될 때 함수내에서 변수가 복사되는것이 우려될 경우에 사용
💡 레퍼런스 전달 방식은 말 그대로 참조 변수이기때문에 리터럴 상수를 전달할 수 없었지만,
const reference 전달 방식은 리터럴 상수와 r-value를 전달할수 있다.
int mul(const int& a, const int& b){
return a * b;
}
int seven = 7;
int eight = 8;
// 둘다 가능
int result = mul(seven, eight);
int result2 =mul(7,8);
왜냐면은 const 키워드가 붙은 참조 변수에 경우 rvalue 값을 할당하면 표현식인 rvalue값이 해당 line을 벗어나면 원래는 다시 참조할 수 없도록 소멸되지만 const reference 변수에 할당하게되면 현재 스코프 범위내로 수명이 연장된다고한다.
아래는 본문
This is a C++ feature… the code is valid and does exactly what it appears to do.
Normally, a temporary object lasts only until the end of the full expression in which it appears. However, C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error. In the example above, the temporary returned by f() lives until the closing curly brace. (Note this only applies to stack-based references. It doesn’t work for references that are members of objects.)
https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/