C++: 포인터 vs. 참조자

C++ 프로그래밍 언어에서는 객체를 간접적으로 접근하게 해주는 수단으로 크게 2가지를 제공해준다. 한가지가 포인터; pointer고 나머지 하나가 – C 언어에는 없는 – 참조자; reference 다.

사실 이 둘은 거의 비슷한 용도로 쓰인다.

  • 크기가 커서 직접 전달하는게 힘든 함수 인자 혹은 멤버 변수 전달용
  • 값에 의한 전달; pass by value 로 할 수 없는 일의 처리 (예: swap 함수)
  • 다형성; polymorphism 이 사용되어야 하는 상황

주로 이런 용도로 사용하게 되는데, C 언어를 오래 사용하거나 한 경우, 참조자 보다 포인터를 좀 더 편안하게 느끼고 더 많이 쓰는 경우가 있다. 그렇지만 C++ faq-lite 에서 설파하고 있듯이 – 혹은 스캇 마이어스; Scott Meyers의 More Effective C++† 에서 설명하고 있듯이,

사용할 수 있다면 참조자를, 어쩔 수 없다면 포인터를 써라;

Use references when you can, and pointers when you have to

즉 가능하면 참조자를 사용하는 것이 좋다. 언제가 어쩔 수 없는 경우에 해당할까? 바로 대상이 NULL일 수 있는 경우만이다. 이런 경우에는 어쩔 수 없이 포인터를 쓴다.


우선 참조자를 써야하는 대표적인 사례 는 이런 것 같다(위에서 언급한 첫번째 케이스). 매우 거대한 객체를 유지하고 있는 클래스가 있는데, 이 거대한 객체를 “외부에서 접근하게 해주려면” 무엇을 써야할까?

우선 포인터를 사용하면 다음과 같은 코드가 될 것이다.

SomeHeavyObject* somClass::GetHeavy() { return m_HeavyObjectPointer; }

반면에 참조자를 사용하면 다음과 같은 코드가 될 것이다.

SomeHeavyObject& someClass::GetHeavy() { return *m_HeavyObjectPointer; }‡

둘의 차이는 무엇일까? 가장 중요한 차이는 저 반환값을 가지고 NULL 검사를 해야하는지 여부일 것이다. 즉, 앞에서 설명한 원칙에 따라 포인터를 반환한다는 것은 “해당하는 객체가 없을 수; NULL일 수 있다”라는 의미를 부여하게 된다. 그래서 저 멤버 함수를 호출할 사람은 당연히 NULL검사를 해야한다. (왜냐하면 NULL이 아닌게 보장이 되면 당연히 참조자를 썼을 것이기 때문에)

*

Moral한 C++ 프로그래머라면 꼭 읽어야하는 Effective C++ 씨리즈의 두번째 책이다. 이 책의 항목에서 이 글의 주제를 상세히 다루고 있으니 읽어보자.

‡ 여기에서는 m_HeavyObjectPointer가 NULL이 아니라는 보장이 내부적으로 되어 있어야 한다. 그렇지 않다면 NULL 참조자라는 C++ 최악의 악마 중 하나가 나타나게 된다. (보통 저런 코드에는 assert( m_HeavyObjectPointer ); 같은 코드를 넣어서 미리 검사를 하는 경우가 많다)

ps. 근데 왜 내가 지금 보고있는 코드는 거대(…) 멤버 객체를 반환하는데 포인터인걸까 Orz (위의 경우와는 더더욱 다르게 내부적으로도 그냥 객체가 바로 저장되도록 설계되어 있다; 그래서 절대 NULL일 수가 없다).

Jinuk Kim
Jinuk Kim

SW Engineer / gamer / bookworm / atheist / feminist

Articles: 935

2 Comments

Leave a Reply