포인터 vs 참조
1. 편의성 관련
편의성이 좋다는게 꼭 장점만은 아니다
포인터는 주소를 넘기니 확실하게 원본을 넘긴다는 힌트를 줄 수 있는데,
참조는 자연스럽게 모르고 지나칠 수도 있음
StatInfo info;
// 포인터
PrintInfo(&info);
// 참조
PrintInfo(info);
만약 신입이 와서 info.hp = 10000; 이런식으로 조져버릴수도 있다. -> const 사용해서 방지
const 사용
별(*)기준, 앞에다 붙이는것과 뒤에 붙이는게 다름
- 별 뒤에 붙인다면?
- StatInfo* const info
- info = &globalInfo; 가 막힘
- info라는 바구니의 내용물[주소]을 바꾸지 못하게
- info 주소값을 갖는 바구니 -> 주소값이 고정이다.
- 별 이전에 붙인다면?
- const StatInfo* info => 일반적으로 사용하는 방법
- StatInfo const* info
- info->hp = 10000; 가 막힘
- info가 가리키고 있는 바구니의 내용물을 바꿀 수 없음
2. 초기화 여부
참조 타입은 바구니의 2번째 이름(별칭)
-> 참조하는 대상이 없으면 안됨!
반면 포인터는 그냥 어떤 주소라는 의미
-> 대상이 실존하지 않을 수도 있음
// 포인터
StatInfo* pointer; // 문제없음
pointer = &info;
PrintInfo(pointer);
StatInfo* pointer = &info; // 이렇게 줄여도 됨.
PrintInfo(pointer);
// 참조
StatInfo& reference; // -> 에러발생
reference = info;
PrintInfo(referecne);
StatInfo& reference = info; // 대상이 있어야 함
PrintInfo(reference);
포인터에서 '없다'는 의미?
NULL
StatInfo* pointer = nullptr; // -> 더욱 권장하는 방식
StatInfo* pointer = NULL;
실제 게임을 만들때 게임이 뻗어버리는 이유의 80% 이상은 null crash!!
널 포인터에 접근하려 하면 뻗어버림
참조타입은 이런 nullptr라는게 없음.
그럼 무조건 없는게 좋은건가?
-> 널 있는게 좋을때도 있음
-> 만약 특정 조건을 만족하는 몬스터를 찾는 함수 FindMonster()함수가 있다고 해 보자.
// 특정 조건을 만족하는 몬스터를 찾는 함수
StatInfo* FindMonster()
{
// Heap 영역에서 뭔가를 찾아봄
// 찾았다!!
// return monster~~;
// 만약 못찾으면?
retun nullptr
}
참조에서는 없다라는 것을 표시할 방법이 없음.
그래서 포인터를 사용하기 전에는 항상 긴장해라
포인터값 받아와서 사용 전에는 항상 널 체크를 해라.
if (info == nullptr)
return;
3. 그래서 결론은? 포인터 vs 참조 뭘 써야 하는데?
사실 Team by Team 이다.
정해진 답은 없다.
ex) 구글에서 만든 오픈소스를 보면 거의 무조건 포인터 사용
ex) 언리얼 엔진에선 reference도 애용
Rookiss 선호 스타일)
- '없다'는 경우도 고려해야 한다면 포인터 사용
- 바뀌지 않고 읽는 용도(readonly)만 사용하면 const ref&
- 그 외 일반적으로는 ref 선호 (명시적으로 호출할때 OUT을 붙인다)
- const를 붙여놓은 경우면 상관없는데 아니라면
- 앞에 OUT을 붙여줌 (C#에서는 있는 기능, C++에서는 선언해서 사용, 언리얼등에서 많이 사용됨)
- 단, 다른 사람이 pointer 를 만들어놓은걸 이어서 만든다면, 계속 pointer 사용. (즉, 섞어서 쓰지는 않음)
- 어찌됐든 팀에서 사용하는 스타일을 잘 따라라
// 아무 define도 하지 않음. 오직 코드상의 힌트로 적기 위한 용도
#define OUT
void ChangeInfo(OUT StatInfo& info)
{
info.hp = 1000;
{
4. 보너스
포인터로 사용하던걸 참조로 넘겨주려면?
참조로 사용하던걸 포인터로 넘겨주려면?
#include <iostream>
using namespace std;
struct StatInfo
{
int hp;
int attack;
int defence;
};
void CreateMonster(StatInfo& stat)
{
stat.hp = 100;
stat.attack = 8;
stat.defence = 2;
}
void PrintInfoByRef(StatInfo& stat)
{
cout << "HP: " << stat.hp << endl;
cout << "Attack: " << stat.attack << endl;
cout << "Defence: " << stat.defence << endl;
}
void PrintInfoByptr(StatInfo* stat)
{
cout << "HP: " << stat->hp << endl;
cout << "Attack: " << stat->attack << endl;
cout << "Defence: " << stat->defence << endl;
}
int main()
{
StatInfo stat;
CreateMonster(stat);
StatInfo* pointer = &stat; // 포인터
StatInfo& reference = stat; // 참조
// 포인터로 사용하던걸 참조로 넘겨주려면?
PrintInfoByRef(*pointer);
// 참조로 사용하던걸 포인터로 넘겨주려면?
PrintInfoByptr(&reference);
return 0;
}