#include <iostream>
int change_val(int *p) {
*p = 3;
return 0;
}
int main() {
int number = 5;
std::cout << number << std::endl;
change_val(&number);
std::cout << number << std::endl;
}
5
3
함수의 인자 p에 number의 주소값을 전달하여, *p를 통해 number를 참조하여 number의 값을 3으로 바꾸었다.
C언어에서는 &키를 계속 눌러줘야했지만 C++에는 새로운 개념이 생겼다.
바로 '래퍼런스'
#include <iostream>
int change_val(int &p) {
p = 3;
return 0;
}
int main() {
int number = 5;
std::cout << number << std::endl;
change_val(number);
std::cout << number << std::endl;
}
5
3
위와 같은 결과 때문에 흔히 C++에서 래퍼런스를 참조자라고도 한다.
p가 number변수의 다른 이름이 되는 것이다. 따라서 p=3; 이란 명령은 number=3;과 정확히 일치하는 명령이다.
래퍼런스를 정의하는 방법은 아래와 같다.
int& ref = number;
즉, int 타입의 변수의 레퍼런스를 만들기 위해서는 int& 로 하면 되고, 그 오른쪽에 참고하고 싶은 것을 써주면 된다.
참조자의 가장 중요한 특성은 반드시 정의시 초기화되어야 한다는 것이다. 아래와 같이 쓸 수 없다.
//int &ref;
JVM의 garbage Collector가 해당 변수의 reference-count가 0이 되면 지우는 알고리즘과 같다.
여기서 그러면 "포인터도 다른 어떤 변수를 가리켜야 하는데 왜 초기화하지 않고 정의 할 수 있냐?"라고 물을 수 있다.
하지만 포인터 자체는 '메모리값을 보관하는 변수'자체로 활용될 수 있지만 참조자는 그렇지 않다.
참조자는 포인터처럼 어떠한 메모리공간에 할당되어서 자신을 참조하는 주소값을 보관하는 것이 아니다.
컴파일시에 원래 가리키던 변수의 주소값으로 다 치환되버린다.( *(주소값) 으로)
래퍼런스는 한 번 초기화되면 다른 변수의 별명이 될 수 없다.
int a = 10;
int &ref = a;
int b = 3;
ref = b;
참조대상이 바뀌는 것이 아니라 ref는 a를 참조하니까 a=b라는 결과가 나온다.
//&ref = b;
와 같은 문장은 &a = b; 즉, "a 의 주소값을 3 으로 변경한다?" 라는 말이 안되는 문장이다.
ref &= b;
는 ref = ref &b;, 즉 a = a & b; 와 같은 문장으로 역시 전혀 의미가 다르다.
아무튼 레퍼런스는 포인터로 치면 int* const 와 같은 형태라 말할 수 있다.
즉 한 번 별명이 된다면 영원히 바뀔 수 없다. (물론 포인터와 레퍼런스는 엄연히 다른 것이다)
int number = 10;
int& ref = number;
int* p = &number;
ref++;
p++;
p ++ 의 경우 C 언어를 배운 사람이라면 p 의 주소값이 4 만큼 증가되어서 (int 의 크기가 4 이니까) 아마 이상한 것을
가리키고 있겠고. 반면에 ref++ 의 경우 ref 가 아까 'number 의 다른 이름' 이라고 했으므로 number++ 과 동일하다.
즉 number 가 11 이다.
이렇게 참조자를 사용한다면 귀찮았던 포인터 관련 연산들을 모두 생략할 수 있게 된다.
원래코드를 다시 보자.
change_val(number);
인자로 number를 전달하였다. 따라서
int change_val(int &p) {
p = 3;
return 0;
}
int &p = number; 와 같으므로 p가 number의 별명이 된다.
따라서 p = 3; 은 number = 3; 과 동일한 작업이다.
// 참조자 이해하기
#include <iostream>
int main() {
int x;
int& y = x;
int& z = y;
x = 1;
std::cout << "x : " << x << " y : " << y << " z : " << z << std::endl;
y = 2;
std::cout << "x : " << x << " y : " << y << " z : " << z << std::endl;
z = 3;
std::cout << "x : " << x << " y : " << y << " z : " << z << std::endl;
}
x : 1 y : 1 z : 1
x : 2 y : 2 z : 2
x : 3 y : 3 z : 3
C언어의 포인터와 대응시키지말자. 포인터와 래퍼런스는 다르다.
int x;
int& y = x;
여기서 포인터 개념으로 보면 의문이 생길 수 있다.
int& y = x;
int& z = y;
y가 int&이므로 z는 int&&가 되야하는데 왜 int&이지?
이것은 포인터였다면,
int x;
int* y = &x;
int** z = &y;
와 같이 소스를 작성했어야 맞다. 이로써 포인터와 래퍼런스는 다르다.
정리하자면 래퍼런스의 경우 x, y, z가 모두 같은 것이여야 한다. x의 다른 이름인 y와 z를 만든 것 뿐이다.
case1:
std::cin >> user_input;
case2:
scanf("%d", &user_input);
case2에서는 어떤 변수의 값을 다른 함수에서 바꾸기 위해서는 항상 포인터로 전달했다.
하지만 여기서 cin에서는 래퍼런스로 전달하였기 때문에 앞에 &를 붙일 필요가 없게 된다.
'개발공부 > C++' 카테고리의 다른 글
포인터 복습 (0) | 2019.10.10 |
---|---|
상수에 대한 참조자 (0) | 2019.10.10 |
std::cin (0) | 2019.10.10 |
c++공부를 시작합니다. (0) | 2019.10.10 |
Namespace (0) | 2019.10.10 |