C++: 포인터 vs. 참조자
C++ 프로그래밍 언어에서는 객체를 간접적으로 접근하게 해주는 수단으로 크게 2가지를 제공해준다. 한가지가 포인터; pointer고 나머지 하나가 – C 언어에는 없는 – 참조자; reference 다.
사실 이 둘은 거의 비슷한 용도로 쓰인다.
- 크기가 커서 직접 전달하는게 힘든 함수 인자 혹은 멤버 변수 전달용
- 값에 의한 전달; pass by value 로 할 수 없는 일의 처리 (예: swap 함수)
- 다형성; polymorphism 이 사용되어야 하는 상황
C++ faq-lite 에서 설파하고 있듯이 – 혹은 스캇 마이어스; Scott Meyers의 More Effective C++1 에서 설명하고 있듯이,
사용할 수 있다면 참조자를, 어쩔 수 없다면 포인터를 써라;
Use references when you can, and pointers when you have to
즉 가능하면 참조자를 사용하는 것이 좋다. 언제가 _어쩔 수 없는 경우_에 해당할까? 바로 대상이 NULL일 수 있는 경우만이다. 이런 경우에는 어쩔 수 없이 포인터를 쓴다.
우선 참조자를 써야하는 대표적인 사례 는 이런 것 같다(위에서 언급한 첫번째 케이스). 매우 거대한 객체를 유지하고 있는 클래스가 있는데, 이 거대한 객체를 “외부에서 접근하게 해주려면” 무엇을 써야할까?
우선 포인터를 사용하면 다음과 같은 코드가 될 것이다.
SomeHeavyObject* somClass::GetHeavy() { return m_HeavyObjectPointer; }
반면에 참조자를 사용하면 다음과 같은 코드가 될 것이다.2
SomeHeavyObject& someClass::GetHeavy() { return *m_HeavyObjectPointer; }
둘의 차이는 무엇일까? 가장 중요한 차이는 저 반환값을 가지고 NULL 검사를 해야하는지 여부일 것이다. 즉, 앞에서 설명한 원칙에 따라 포인터를 반환한다는 것은 “해당하는 객체가 없을 수; NULL일 수 있다” 라는 의미를 부여하게 된다. 그래서 저 멤버 함수를 호출할 사람은 당연히 NULL검사를 해야한다. (왜냐하면 NULL이 아닌게 보장이 되면 당연히 참조자를 썼을 것이기 때문에)
ps. 근데 왜 내가 지금 보고있는 코드는 거대 멤버 객체를 반환하는데 포인터인걸까 Orz (위의 경우와는 더더욱 다르게 내부적으로도 그냥 객체가 바로 저장되도록 설계되어 있다; 그래서 절대 NULL일 수가 없다).