Reference
카테고리: Cpp
태그: Programming
모두의 코드 씹어먹는 c++ 자료를 보고 정리한 내용입니다.
C 언어에서는 어떠한 변수를 가리키고 싶을 때 반드시 포인터를 사용했다. 그런데 c++에서는 다른 변수나 상수를 가리키는 방법으로 또 다른 방식을 제공하는데, 이를 바로 참조자(reference) 라고 한다.
구조체의 생성 & 복사
StatInfo player;
player.hp = 0xbbbbbbbb;
player.attack = 0xbbbbbbbb;
player.defence = 0xbbbbbbbb;
player = CreatePlayer();
// 실제 작동 방식
// StatInfo temp = CreatePlayer();
// player = temp;
- 실제 작동 방식
- StatInfo temp 주소 할당
- temp의 주소가 CreatePlayer()로 넘어감
- temp에 값들을 저장하고 return temp
- return된 temp의 값들을 하나하나 player로 복사
- 굉장히 비효율적…
- 구조체를 함수의 파라미터나 리턴값으로 사용할 경우에도 마찬가지
- 포인터나 레퍼런스를 사용하면 매우 깔끔하고 효율적
특징
[1] 한 번 어떤 변수의 참조자가 되어버리면, 더이상 다른 변수를 참조할 수 없음
int a = 10;
int &another_a = a;
int b = 3;
another_a = b; // a = 3
[2] 레퍼런스는 반드시 초기화가 필요하다.
int* p; // ok
int& anoter_a; // error
[3] 레퍼런스는 메모리 상에 존재하지 않을 수도 있다.
int a = 10;
int* p = &a; // p는 메모리 상에 8바이트를 차지하며 존재
int b = 10;
int &another_b = b; // another_b가 반드시 메모리를 차지해야 할까? → b로 바꿔치기 하면 됨
[4] 레퍼런스가 반드시 메모리에 존재하는 경우도 있다.
int change_val(int &p)
{
p = 3;
return 0;
}
int number = 5;
change_val(number);
std::cout << number; // 3
- 함수 인자로 레퍼런스를 받는 경우 [파라미터] 자리로 할당된 메모리 사용
- p가 정의되는 순간은
change_val(number)
이 호출될 때 - p 에게 너는 앞으로 number의 별명이야 라고 선언
- 따라서 포인터와 다르게 number 앞에 & 붙일 필요가 없음
- p가 정의되는 순간은
int &ref = 5; // error
const int &ref = 5; // ok
- 일반적으로 리터럴 값을 참조할 수는 없지만 const reference의 경우 선언이 가능
- 이 경우 메모리 할당
[5] 레퍼런스의 레퍼런스?
int x = 1;
int& y = x;
int& z = y;
std::cout << z; // ??
- z는
int&&
가 아닌int&
- 즉 y, z 모두 x의 참조자
[6] 레퍼런스의 배열
int a, b;
int& arr[2] = {a, b}; // error
c++ 규정 : 레퍼런스의 레퍼런스,레퍼런스의 배열, 레퍼런스의 포인터는 존재할 수 없다.
배열의 작동 원리를 생각해보면 arr
은 첫 번째 원소의 주소값이 반환되어야 한다. 하지만 레퍼런스는 특별한 경우가 아닌 이상 메모리 상에서 공간을 차지 하지 않는다. 따라서 이런 경우를 방지하고자 언어 차원에서 금지시킨 것이다.
참고) 배열의 레퍼런스는 가능하다.
[7] 레퍼런스를 리턴하는 함수
int& function()
{
int a = 2;
return a;
}
int b = function(); // error
- 리턴과 동시에 a 변수가 메모리 스택에서 pop 됨 (포인터도 마찬가지)
- 런타임 에러 발생
- Dangling Reference : 원래 참조하던 것이 사라진 레퍼런스
int& function(int& a)
{
a = 5;
return a;
}
int b = 2;
int c = function(b); // ok
- 외부 변수의 레퍼런스 리턴은 가능
int& function()
{
int a = 2;
return a;
}
cont int& c = function(); // ok
- const reference로 리턴값을 받아 오면 리턴값의 생명이 연장됨
Pointer vs Reference
어셈블리 관점에서 작동 방식은 동일하다.
편의성 : 참조 승
- 간접 참조 (→)쓰지 않고 . 사용 가능
- 매개변수로 넘겨줄 때 아주 편함 (& 붙일 필요 없음)
- 하지만 편의성이 좋다는 것이 꼭 장점만은 아님
- 포인터의 경우 원본을 넘긴다는 티가 나지만, 참조는 모르고 지나칠 위험이 높음
- Const를 잘 이용하면 개선 가능
범용성 : 포인터 승
- 래퍼런스는 반드시 초기화를 해주어야 함
- 포인터는 초기화 없이 선언 가능 (nullptr)
- 아무 것도 가르키지 않는 상태가 가능
- 반대로 포인터가 넘어오면 항상 nullptr 체크하기
StatInfo* FindMoster()
{
// TODO : Heap 영역에서 Monster 객체를 찾아보고
// 찾으면
// return monster;
// 만약 없다면
// return nullptr;
}
nullptr 은 c++ 11에 새로 추가된 키워드로 기존의 NULL 을 대체.
C 에서의 NULL 은 단순히 #define 으로 정의되어 있는 상수값 0이기 떄문에, 이 값이 그냥 0을 의미하는지, 아니면 포인터 주소값 0을 의미하는지 구분할 수 없었음.
정리
- 없는 경우도 고려해야 한다면 pointer
- read only 용도로만 사용하면 const ref&
- 그 외 일반적인 경우에는 ref 사용하되 (OUT 명시)
// 코드에 아무 영향을 미치지 않지만, 원본이 바뀔 수 있다는 표기
#define OUT
void ChangeInfo(OUT StatInfo& info) {}
ChangeInfo(OUT info);
댓글 남기기