Object Oriented Programming 2 - Pointer & Reference
C/C++을 공부하면서 가장 어려운 것은 단연 포인터를 사용하는 방법이다. 여기에 더해 C++에는 reference type 이라는 것도 존재한다.
포인터는 분명 익숙해지면 사용하기 쉽지만, 필연적인 실수에 의한 많은 오류들이 포인터를 사용함으로써 기인하기에, C++, java 등은 포인터를 사용하는 것을 상대적으로 권장하지 않는다.
C++에서 사용되는 reference type과 pointer type의 사용법에 대해 간단히 정리해보자.
Reference Type(&) : c/java는 없는 타입. Pointer와 reference는 서로 연관 & c의 가장 중요한 개념.
Reference is alias(=nickname) of a variable. 즉, 같은 변수에 새로운 이름을 붙이는 것.
Call by reference & call by value : void a (double &x) / void a (double x).
함수가 실행될 때 내부의 지역변수와 매개변수들이 생성된다. 그래서 value로 전달될 경우, 매개변수가 생성되고, argument(실인자-호출하는 실제 변수)의 값이 parameter(매개변수-함수 내부의 지역변수)에 복사된다.
즉 복사에 의한 값의 전달이 이루어지는 것이 call by value이다. 이 때 함수의 리턴되고 난 후에 내부의 지역변수와 매개변수는 모두 삭제된다. (lifetime over) 그래서 함수를 호출하는 실인자의 값이 변하지 않는다.
이와 다르게 call by reference를 할 경우 전과 달리 reference type으로 매개변수를 선언하므로 함수가 호출될 때 생성되는 매개변수가 입력되는 argument의 새로운 nickname이 된다. (같은 주소를 공유하게 된다)
따라서 내부에서 매개변수를 가지고 계산을 한 후에 리턴하게 되면, 실인자로 입력된 변수의 값도 같이 바뀌게 된다. (왜냐하면 매개변수로 전달된 지역변수가 입력된 실인자의 다른 이름으로 주소를 공유하고 있었기 때문) 이 때 함수가 리턴할 때 지역변수는 삭제되므로 매개변수로 입력된 다른 이름은 삭제된다.
Call by value : 함수 내부에서 어떤 짓을 하던 지 실인자의 값이 변하지 않는다는 장점이 있다. 즉, safety & readability가 보장된다. (value of argument variable이 불변한다는 것이 gurantee되어 safe하다)
그러나 함수가 호출될 때 매개변수가 생성되고 값이 복사가 되므로 오버헤드가 발생하게 된다. 그래서 큰 인자를 전달할 때는 이 오버헤드가 매우 커져서 중요하게 된다. (user define type이나 구조체 등. 크기가 큰 변수의 생성과 복사 둘 다 cost가 엄청 높다. 또한 user define type에 대해서는 copy constructor와 destructor도 호출되므로 많은 오버헤드가 발생한다)
일반적으로 built in type은 오버헤드가 그리 크지 않다. 다만 user define type에 대해서는 앞서 언급한 이유들 때문에 오버헤드가 매우 커지고, call by reference를 사용하는 것이 바람직하다. (or call by pointer)
Call by reference : 함수 내부의 변경이 실제로 변수에 가해지게 된다. (modification of the variable may be dangerous) 다만 이것은 상황에 따라 필요할 수도 있기 때문에 장점이 될 수도 있다. 또한 함수가 호출될 때 parameter의 생성이나 복사가 실행되지 않기 때문에 오버헤드가 적다는 장점이 있다.
일반적으로 함수에 매개변수를 전달할 때는 해당 객체의 정보를 읽게 하는 것이 목적이 되는 경우가 많다. (새로 쓰는 경우도 있지만 상대적으로 읽는 경우가 더 많음). 따라서 copy를 만들고, 그것을 읽는 행동은 쓸데없고 낭비가 심하게 된다. 따라서 original argument를 사용하는 것이 낫다.
Call by pointer : void a (double *x) -> informal name.
Parameter variable에 argument의 주소가 담김 -> parameter가 삭제되어도 원본 주소를 직접 수정하므로 값이 변경됨. 즉, effect(장단점)는 call by reference와 동일. 다만 reference를 사용하면 해당 변수에 새로운 이름을 부여하는 것이고, pointer를 사용하면 pointer 변수를 생성하여 그 주소를 저장하는 것.
결과적으로 reference는 반드시 실제 오브젝트를 할당해야 하지만 (해당 오브젝트의 닉네임) 포인터는 다른 것들도 가리킬 수 있다. (주소만 있으면 되므로) 그래서 reference가 more restrictive, more safer, less ambiguous 하므로 이를 사용하는 것이 더 좋다. Pointer는 null이 될 수 있지만 null reference는 없다. 따라서 object를 전달할 때 reference를 쓰는 것이 더 좋다.
*와 &는 declaration, unary operator, binary operator의 세가지 사용법이 있다. 같은 표기지만 의미가 완전히 다르므로 이들을 구별할 줄 알아야 한다.
*의 경우
1. declaration : pointer type variable declaration.
2. unary : the value of this address (1 operand)
3. binary : used as multiplication operator (2 operand)
따라서 *에 대해 declaration인지 식별하고, 이것이 곱하기 인지 아니면 값을 가져오는 operator인지 확인해야 한다. (operand가 두 개면 곱하기, 한 개면 unary)
&의 경우
1. declaration : reference type variable’s declaration.
2. unary : return the address of the operand (only 1 operator)
3. binary : bitwise AND. (2 operand) (bitwise = doing the operation to each bit)
//Example --------------------------------------------------------------------------------
int main()
{
int x=5;
int* y;
int& z=x;
y=&x;
int w=*y;
x=9;
z=*y*x;
x++;
z=x&*y;
cout << x << " " << *y << " " << z << " " << w << endl; // print the values
return 0;
} // Result = 82 82 82 5 ------------------------------------------------------------------
Reference return to function : c에서는 함수에 값을 집어넣는 것이 불가능하지만 C++에서는 f()=7;처럼 사용할 수 있다. 만약 함수의 return type이 reference 타입일 때 가능하게 된다.
Int & f(){return y;}, f()=7; -> 이렇게 될 경우, 함수가 실행되고 y를 리턴하는데, 해당 함수의 타입이 reference이므로 y와 함수이름 f는 서로 같은 주소의 다른 이름이 되게 된다.
즉, f()=7;은 y=7;과 동일하게 되는 것이다. 따라서 함수의 return type으로 reference type이 될 수도 있다.
단, 이 때 y가 함수 내부의 지역변수라면 불가능하다. (함수가 끝나면 지역변수는 삭제되는데, 함수에다가 대입을 하면 해당 변수가 없어져 있기 때문)
그래서 리턴 타입이 reference type인 함수의 경우, 리턴 값이 비지역 변수여야 한다. (함수가 끝나도 해당 함수의 alias가 남아있게 하기 위해)
Advantage of call by Reference&pointer
1. can modify the value of argument variable. (그러나 변화의 가능성이 있으므로 위험하다는 단점이 되기도 함)
2. reduce the overhead for creating parameter variables and copying that are necessary in call-by-value(especially when the size is big – use defined types)
따라서 이 위험하다는 단점을 없애고, overhead를 줄인다는 장점만 가져오려면 (const int & argu)로 reference type에 const를 붙여서 사용하면 된다. 이러면 해당 argument를 수정할 수 없는 동시에 overhead를 줄일 수 있다. 따라서 수정하고 싶을 때를 제외하면 const를 붙여서 사용하는 것이 바람직하다.
댓글
댓글 쓰기