c++ 프로그래밍을 하려면 포인터와 참조자에 대한 차이점에 대해서 확실히 이해를 하고 있어야 합니다. 우선 포인터와 참조자가 무엇인지 코드로 살펴봅시다.
int * pNum; // 포인터
int & rNum; // 참조자
int & rNum; // 참조자
위와 같이 객체(변수)를 선언할 때 * 를 사용하면 포인터 & 를 사용하면 참조자입니다.
포인터와 참조자의 차이점에 대해서 말하기 전에 공통점에 대해서 먼저 짚고 넘어가겠습니다.
# 공통점 : 포인터와 참조자는 다른 객체(변수)를 간접적으로 참조하는 역할을 한다.
즉, 원본 대상을 포인터와 참조자를 이용하여 대신 이용할 수 있도록 한다는 겁니다. 그럼 같은 역할을 하는게 왜 2개나 있을까요? 물론 사용하는 용도에 차이점이 있기때문에 그렇습니다. 그럼 어떤 차이점이 있는지 살펴봅시다.
# 차이점 : 포인터는 널 값을 가질 수 있지만 참조자는 널 값을 가질 수 없다. 즉, 널 참조자(null reference)는 없다.
위의 말은 포인터는 자신이 참조하는 대상을 원하는 아무 때나 참조할 수 있지만 참조자는 자신이 선언되는 순간에 참조하려는 대상을 알고 있어야만 한다는 뜻입니다. 코드로 살펴보면 다음과 같습니다.
포인터와 참조자의 차이점에 대해서 말하기 전에 공통점에 대해서 먼저 짚고 넘어가겠습니다.
# 공통점 : 포인터와 참조자는 다른 객체(변수)를 간접적으로 참조하는 역할을 한다.
즉, 원본 대상을 포인터와 참조자를 이용하여 대신 이용할 수 있도록 한다는 겁니다. 그럼 같은 역할을 하는게 왜 2개나 있을까요? 물론 사용하는 용도에 차이점이 있기때문에 그렇습니다. 그럼 어떤 차이점이 있는지 살펴봅시다.
# 차이점 : 포인터는 널 값을 가질 수 있지만 참조자는 널 값을 가질 수 없다. 즉, 널 참조자(null reference)는 없다.
위의 말은 포인터는 자신이 참조하는 대상을 원하는 아무 때나 참조할 수 있지만 참조자는 자신이 선언되는 순간에 참조하려는 대상을 알고 있어야만 한다는 뜻입니다. 코드로 살펴보면 다음과 같습니다.
int * pNum = NULL; // 통과.
int & rNum = NULL; // 에러.
int num = 100;
int & rNum = num // 통과. (참조자는 선언 시 반드시 초기화를 해야한다.)
int & rNum = NULL; // 에러.
int num = 100;
int & rNum = num // 통과. (참조자는 선언 시 반드시 초기화를 해야한다.)
이런 차이점을 이용하여 2가지 용도로 구분할 수 있습니다. 즉, 어떤 대상을 참조하는 변수를 만들때, 그 대상이 언제나 존재해야만 한다면 참조자를 써야하고 그 대상이 존재하지 않을 수도 있을 경우에는 포인터를 써야하는 것입니다.
위의 말은 이런 의미도 가지게 됩니다. 참조자는 그 대상이 언제나 존재하기 때문에 유효성 검사를 할 필요가 없지만, 포인터는 그 대상이 존재하지 않을 수도 있기때문에 사용전에 언제나 유효성 검사를 해야 합니다. 즉, 언제나 아래와 같은 형식으로 사용해야 합니다.
위의 말은 이런 의미도 가지게 됩니다. 참조자는 그 대상이 언제나 존재하기 때문에 유효성 검사를 할 필요가 없지만, 포인터는 그 대상이 존재하지 않을 수도 있기때문에 사용전에 언제나 유효성 검사를 해야 합니다. 즉, 언제나 아래와 같은 형식으로 사용해야 합니다.
string name("whitesnake");
string * ps = &name;
if(ps) cout << ps; // 유효성 검사 : ps가 존재하는지 확인 후 사용해야 한다.
그럼 다음 차이점에 대해서 알아봅시다.
# 차이점 : 초기화 시, 참조자는 객체(변수)를 직접 입력받고, 포인터는 객체(변수)의 주소값을 입력받는다.
이 부분은 초보자에게는 좀 헷갈리는 부분이긴 하지만 자꾸 사용하다보면 그리 헷갈리는 부분은 아닙니다. 코드를 봅시다.
참조자 초기화 시,
# 차이점 : 초기화 시, 참조자는 객체(변수)를 직접 입력받고, 포인터는 객체(변수)의 주소값을 입력받는다.
이 부분은 초보자에게는 좀 헷갈리는 부분이긴 하지만 자꾸 사용하다보면 그리 헷갈리는 부분은 아닙니다. 코드를 봅시다.
참조자 초기화 시,
string name("whitesnake");
string & rs = name; // name을 직접 대입한다.
string & rs = name; // name을 직접 대입한다.
포인터 초기화 시,
string name("whitesnake");
string * ps = &name; // name의 주소값을 대입한다.
코드를 보시면 무슨 말인지 이해가 가실겁니다. ^^
# 차이점 : 참조자는 한번 가리킨 대상을 변경할 수 없지만, 포인터는 자신이 가리키는 대상을 언제든지 변경할 수 있다.
# 차이점 : 참조자는 한번 가리킨 대상을 변경할 수 없지만, 포인터는 자신이 가리키는 대상을 언제든지 변경할 수 있다.
string name("whitesnake");
string nickname("developerT");
string & rs = name; // 초기화.
rs = nickname; // 이 코드는 참조하는 대상을 변경하는 게 아니라 원래 참조하던
// name객체의 값을 nickname의 값으로 변경하는 결과를
// 가져오게 됩니다.
string * ps = &name; // 초기화.
ps = &nickname; // ps는 이제 nickname을 가리키게 됩니다.
string nickname("developerT");
string & rs = name; // 초기화.
rs = nickname; // 이 코드는 참조하는 대상을 변경하는 게 아니라 원래 참조하던
// name객체의 값을 nickname의 값으로 변경하는 결과를
// 가져오게 됩니다.
string * ps = &name; // 초기화.
ps = &nickname; // ps는 이제 nickname을 가리키게 됩니다.
# 차이점 : 클래스 멤버 접근 시, 참조자는 "."을 사용하고 포인터는 "->"를 사용한다.
위 차이점 역시 기본적으로 알고 계셔야 하는 부분입니다.
위 차이점 역시 기본적으로 알고 계셔야 하는 부분입니다.
class Snake
{
{
void Attack();
}
Snake cobra;
Snake & bigSnake = cobra;
bigSnake.Attack(); // 참조자에서 Attack()함수 호출.
Snake * hugeSnake = &cobra;
hugeSnake->Attack(); // 포인터에서 Attack()함수 호출.
Snake cobra;
Snake & bigSnake = cobra;
bigSnake.Attack(); // 참조자에서 Attack()함수 호출.
Snake * hugeSnake = &cobra;
hugeSnake->Attack(); // 포인터에서 Attack()함수 호출.
코드를 보시면 잘 이해가 되시리라고 믿습니다.
C++ 코딩하실 때 포인터와 참조자에 대한 위의 차이점들을 잘 생각하고 사용하시면 많은 도움이 되실 겁니다.