프로그래머/CPP_메모

c++ 참조 정리

미역국마싯 2022. 3. 20. 11:13

목록

1. 선언과 초기화

2. 관점에 따라 달라지는 참조

  1. 어셈블리 관점에서 참조
  2. c++ 관점에서 참조

3. 포인터 대신 참조를 사용하는 이유

4. 참조의 단점과 해결방안

  1. 쓰기 기능을 막는 방법
  2. 가독성을 높이는 방법

5. 참조에 포인터를 포인터에 참조를

  1. 포인터로 사용하던 걸 참조로 넘겨주는 경우
  2. 참조로 사용하던 걸 포인터로 넘겨주는 경우

 

선언과 초기화

int number = 1;

int& ref = number;

int& ref2;		// error !
ref2 = number;

참조는 선언과 동시에 초기화를 해줘야 한다.

 

참고로 참조는 산술 연산을 제공하지 않는다.

ref++;	// error !

뒤에 말하겠지만 참조는 특정 바구니의 가명과 같다. 즉, 하나의 바구니에 종속되기 때문에 산술 연산을 통해 참조가 가리키는 주소를 변경할 수 없다.


 

관점에 따라 의미가 달라지는 참조

1. 어셈블리 관점에서 참조란

포인터와 같은 방식으로 동작한다. int&은 int*처럼 주소를 담는 방식이다. 

포인터처럼 4byte( 32bit ) 또는 8byte( 64bit ) 고정 크기를 가진다.

 

2. c++ 관점에서 참조란

number라는 바구니에 또 다른 이름( ref )을 부여한 것이다. ref 바구니에 데이터 조작을 하면 number 바구니에 하는 것과 같다.

 

즉, 참조는 포인터와 동작하는 방식은 같지만 사용하는 문법이 다르다.


 

포인터 대신 참조를 사용하는 이유

참조 전달 방식은 값 전달 방식처럼 편리하고 주소 전달처럼 효율적이기 때문이다.

struct Stat
{
	hp = 10;
	damage = 1;
};

// struct의 크기가 클 경우 비효율적이고 stat에 영향을 미치지 않는다.
void ValueTest(Stat stat)
{
	stat.hp = 100;
	stat.damage = 10;
}

// struct의 크기가 아무리 크더라도 고정 크기를 가진다.
// stat에 영향을 미친다.
void PtrTest(Stat* stat)
{
	stat->hp = 100;
	stat->damage = 10;
}

// 고정 크기를 가지고 stat에 영향을 미친다.
// 값 전달 방식으로 사용할 수 있다.
void RefTest(Stat& stat)
{
	stat.hp = 100;
	stat.damage = 10;
}

int main()
{
	Stat stat;

	ValueTest(stat);
	PtrTest(&stat);
	RefTest(stat);
}

 

참조의 단점과 해결방안

위 코드에서 ValueTest, RefTest는 값 전달인지 참조 전달인지 함수에 명시했기 때문에 구분할 수 있다. 하지만 Test라고만 한다면 구분할 수 없다. 따라서 구분할 수 없는 함수는 f12를 통해 참조인지 값인지 확인하고 사용해야 할 수 있다. 왜냐하면 값 전달인줄 알고 사용하다가 알고 보니 참조 전달이라 원본 데이터가 수정될 수 있기 때문이다.

 

1. 쓰기 기능을 막는 방법

함수 내부에서 쓰기 기능을 막는 방법이 있다. 

// 읽기만 가능하도록 상수화 한다.
void Test(const Stat& stat)
{
	stat.hp = 100;		// error!
	std::cout << stat.hp << '\n';
}

 

2. 가독성을 높이는 방법

OUT을 이용하는 방법이다.

OUT통해 사람들에게 해당 함수는 원본 데이터를 수정하는 기능이 있다는 것을 알려준다.

#define OUT
void Test(OUT Stat& stat)
{
	stat.hp = 100;
	stat.damage = 10;
}

int main()
{
	Stat stat;
	Test(OUT stat);	// 참조 전달 + 값 수정을 알 수 있다
}

#define OUT에는 아무런 값을 넣지 않는다. 값을 넣지 않으면 c++ 컴파일에서 OUT은 그냥 넘어가기 때문이다.

// 우리가 보는 코드
void Test(OUT Stat& stat)

// 컴파일러가 보는 코드
void Test(Stat& stat)

즉, OUT을 통해 사람들이 참조 전달 방식을 사용하는 함수임을 알 수 있고, 해당 함수에서 원본 데이터에 접근해서 값을 변경한다는 사실도 알 수 있다.


 

참조에 포인터를 포인터에 참조를

1. 포인터로 사용하던걸 참조로 넘겨주는 경우

Stat stat;
Stat* ptr = &stat;

Stat& ref = *ptr;	// ref, stat [ value ]

ptr은 stat의 주소를 가지고 있다. ptr이 가리키는 주소로 이동하면 stat이 존재한다.

따라서 ref는 stat이 존재하는 주소의 또 다른 이름으로 사용할 수 있게 된다.

 

2. 참조로 사용하던걸 포인터로 넘겨주는 경우

Stat stat;
Stat& ref = stat;

Stat* ptr = &ref;	// == &stat

ref의 주소를 넘겨주면 된다.

'프로그래머 > CPP_메모' 카테고리의 다른 글

c++ 연산자 오버로딩 정리  (0) 2022.04.03
c++ 배열 정리  (0) 2022.03.21
c++ 포인터 정리  (0) 2022.03.20
c++의 다양한 input 방법과 속도 비교  (0) 2022.03.17
header  (0) 2022.03.16