Reference

Date:     Updated:

카테고리:

태그:

모두의 코드 씹어먹는 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 앞에 & 붙일 필요가 없음
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;
}

nullptrc++ 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);



맨 위로 이동하기

Cpp 카테고리 내 다른 글 보러가기

댓글 남기기