이 게시글은
서울대학교 데이터사이언스대학원
조요한 교수님의
데이터사이언스 응용을 위한 컴퓨팅 강의를
학습을 위해 재구성하였습니다.
c++에서의 Pointer와 Reference
마지막 편
Call by Value
Call by Reference와
마지막으로
c++에서의 Reference에 대해
정리해보도록하겠다
Call by Value
위 ppt의 코드를 잘보자
mySwap이란 함수에서
x와 y를 파라미터로 받고
함수 내부에서 x와 y의 값을
서로 바꿔준다
그런 다음 main함수에서
int a와 b를 선언해준뒤
mySwap 함수의
파라미터로 넣어준뒤 a와 b를 출력해본다
mySwap 함수 내부에서는
두 파라미터의 값을 서로 교환해주니
main함수에서 정의된 a와 b를
mySwap에 넣어주면
a와 b의 값이 변할까?
정답은 변하지 않는다
왜냐하면 mySwap 함수에
a와 b를 넘겨줄 때
변수 자체를 넘겨주는게 아니고
그 값만 넘겨주는 것이기 때문이다
a에 있는 값인 10과
b에 있는 값인 20만 넘겨줘서
mySwap 함수 내부에서
x와 y의 값만 바뀐다음
mySwap함수의 실행이 끝나면
x와 y는 scope에서 사라지게된다
바뀌어도 mySwap 내부끼리 바뀌는거지
main함수의 a와 b까지
침범할 수 없다
a와 b 변수 자체를 넘겨준 것이
아니기 때문에 mySwap을 실행해도
a와 b의 값은 변하지 않고
여전히 a=10, b=20이 된다
이런걸 Call by Value
한국어로는
값에 의한 호출이라고 부른다
Call by Reference
그렇다면 위의 ppt를 다시 잘보자
이번에는 mySwap함수의 인자로
포인터변수를 받고있다
그런 다음 포인터변수 내부의 주솟값에 접근해
x와 y의 값을 교환해주고있다
이 mySwap함수를 main함수에서
a와 b를 인자로 넣은다음 실행시키면
a와 b의 값은 변할까?
정답은 변한다
왜냐하면 인자로 값을 넘겨준게아닌
메모리주솟값을 넘겨줬기 때문이다
mySwap 함수는 인자로
메모리주솟값을 받았고
함수내부에서 메모리 주솟값에 접근해서
값을 변경하기 때문에
mySwap함수가 끝나도
메모리 주솟값에 들어있는 값은
변경된 상태로 남게된다
이러한 것을
Call by Reference
한국어로는
참조에 의한 호출이라고 부른다
Reference
이런 Call by Reference의 개념을
잘 이해했다면
c++에서의 reference 변수타입에 대해 알아보자
reference는 c++의 변수타입으로
c에서는 없는 변수타입이다
위 코드를 잘보자
int x = 5;가 선언되었고
그 밑에
int& ref = x;
가 선언되어있다
int&는 int 변수에대한
Reference 변수 선언이고
그렇게 선언된 reference변수 ref에
x의 값이 들어가있다
reference 변수는 말그대로
'참조'할 수 있는 변수인데
일종의 별명같은 개념이다
위에 이미 int x가 선언되었는데
x의 별명(alias)로
ref도 사용할 수 있게하겠다라는 뜻이다
그렇게 선언해주면
ref도 x의 메모리주소에 접근이 가능해지고
ref 값을 변경하면
x도 값이 변경되게된다
reference 변수를 선언할 때는
선언과 동시에 반드시 초기화를 해주어야한다
좀 더 현실적인 사례로 예시를 들어보겠다
서울시 관악구 관악로 1 관악캠퍼스 62동이라는
주소에는 건물이 한 개 있다
이를 주소로 부르기는 너무 힘드니까
건물 이름을 정해서 불러주기로했다
따라서 실제 주소에
어떤 이름을 지정해서
앞으로 이 주소에 있는 것은
이 이름으로 불러주겠다고 약속한 것이다
이것이 메모리주소와 변수명의 관계이다
따라서
서울시 관악구 관악로 1 관악캠퍼스 62동이라는
주소에 있는 건물은
합의 하에
서울대학교 관악캠퍼스 중앙도서관
이라는 변수명으로 불리게된다
하지만 이 이름도 너무 길고
부르기가 힘들기때문에
별명을 하나 지어서
별명으로도 똑같은 주소를
가리킬 수 있도록 지정해준다
따라서 별명으로
서울대 관정도서관
이라는 별명으로 불러주기로 합의했다
이것이 바로 Reference 변수의 개념이다
그렇다면 위 예시에 나왔던
mySwap함수를 reference변수를
사용하는 방식으로 변경해보자
mySwap함수의 인자로
int& x와 int& y를 받도록 해준다
그런 다음 main함수에 있는 x와 y를
mySwap의 인자로 넣어주면
x가 a의 reference 변수가
y가 b의 reference 변수가 되어서
x와 y로
main함수에 정의되어있는
a와 b의 메모리 주솟값에
접근이 가능해진다
따라서 mySwap에서
값을 변경해주어도
메모리주솟값에 접근해서
값을 변경해주는 것이므로
a와 b의 값이 변경된채로 남게된다
아까 위처럼 포인터변수를 사용해도 되는데
왜 굳이 reference 변수를 사용하냐고하면
pointer변수를 사용하게되면
int a와 b가 mySwap함수에 들어가게되면
어쩔 수 없이 value값을 copy해서 들어가게된다
그럼 메모리 공간을 추가로 차지하게되는데
reference 변수는 단순한 별명이기때문에
value값을 copy할 필요가 없어
메모리 사용을 줄일 수 있다
또한 포인터변수보다
문법적으로 간단하게 구현이 가능하다
그렇다면 이런 reference 변수를
아무생각없이 사용하다보면
원하지않게 메모리 주소에 접근해
값을 변경하는 경우가 생길 수 있다
따라서 이러한 경우를 대비해
const로 reference 변수를 정의하면
값의 변경을 막아준다고 한다
따라서 위 함수처럼 const int&, int&로 선언하면
mySwap함수는 컴파일 단계에서 에러가 뜬다고 한다
이런 Reference 변수는
함수의 Return값으로도 사용할 수 있다
위 코드 예시를 잘보자
middleElement 함수는
순서가 있는 데이터타입인 map에서
가운데인 pair를 찾아
그 value값을 반환하는 함수인데
함수의 return type이
int&이므로
반환값은 reference 변수가 된다
따라서 오른쪽 코드를 보면
middleElement(m) = 10;
이라는 코드가 있는데
원래라면 문법적으로 있을 수 없는 구문이지만
middleElement 함수의 반환값이
reference 변수이기 때문에 가능하게된다
따라서 middleElement(m) = 10;
의 뜻은
위에서 정의해준 map인 m의
가운데 pair를 찾아 value값을
10으로 변경해달라는 의미이다
middleElement 함수를 실행한 뒤
m에서 가운데 값인 b의 value를 출력해보면
10으로 변경된 것을 확인할 수 있다
위에서 말했듯이
굳이 reference 변수를 사용하는 이유는
value를 copy 할 필요가 없어져
메모리를 효율적으로 사용할 수 있다
따라서 큰 데이터를 사용할 때
유용하게 사용된다
또한 reference 변수를
return값으로 반환하게하면
chaining operation을 구현할 수 있게된다
위의 코드 예시를 통해
이게 무슨 소리인지 알아보자
위 코드에서
MyClass라는 클래스가 정의되어있고
이 클래스 내부의 method로
increment 함수가 정의되어있고
함수의 리턴값은 MyClass 클래스의
reference 변수이다
increment 함수 내용을 잘 살펴보면
MyClass에 정의되어있는
x의 값을 1개 증가시킨다음
그 값을 출력하고
*this 로 MyClass 자신을 반환한다
그런데 increment 함수의 리턴값이
reference 변수이므로
MyClass의 Reference 변수를 반환하게 되는 것이다
이렇게 정의된 MyClass를
main함수에서 초기화한 후 사용해보자
main함수에서
MyClass obj;로 정의해준다음
obj.increment().increment().increment()
와 같이 작성해준다
위와같은 코드가 어떻게 가능하냐하면
obj.increment()를 실행하면
0이었던 x가 한 개 증가하고
그걸 출력시킨 다음
다시 obj의 reference 값을 반환한다
그래서 바로 다음 연속으로
.increment()라고 해줘도
정상적으로 작동하게 되는 것이다
이러한 방식을
chaining operation이라고한다
따라서 increment()를 3번 실행하게되면
처음에 x: 1
두번째는 x: 2
세번째는 x: 3가 출력되고
멈추게 된다
이러한 reference 변수를 이용해
chaining operation을 사용하는
가장 대표적인 예시가
cin이다
이전에 I/O Stream에서
cin에 대해 배울 때
cin >> value1 >> value2 >> value3;
과 같은 방식으로 프로그래밍하면
value1, value2, value3을
연속으로 할당할 수 있다고 해줬는데
이는
cin >> opeartion이
value값을 reference로 받기때문에
cin >> value1의 반환값은
또 cin이 되고
그 이후로 value2의 값도
연속적으로 할당이 가능해지는 것이다
마지막으로
pointer변수와 reference 변수를
비교해보자
초기화
pointer변수는 코드의 어디에서
초기화를 해도 상관없다
하지만 reference 변수는
선언과 동시에 반드시 초기화가 되어야한다
재할당
포인터는 그냥 일반적인 변수이기때문에
언제든 담고있는 주솟값을 바꿀 수 있다
하지만 reference 변수는
한번 assign이 되면 다른 주소로 reassign이 불가능하다
nullability
pointer변수는 역시 일반 변수이기때문에
null값을 가질 수 있다
하지만 reference 변수는
null값을 가질 수 없다
memory
포인터는 당연히 일반 변수이기때문에
포인터 변수를 저장하기위해
8바이트의 공간을 차지한다
하지만 reference 변수는 공간이 할당되는
변수가 아니다
기존에 있는 변수를 가리키는 닉네임을
만들어주는 개념이기 때문에
새롭게 공간을 할당받지 않는다
주요사용
포인터는 주로 동적 메모리를
할당할 때 사용한다
reference 변수는
주로 함수의 인자나 반환값에 사용한다
'강의 > computer science' 카테고리의 다른 글
[ComputerScience] c++의 Class와 Class Template (2) | 2024.10.06 |
---|---|
[ComputerScience] c++의 function overloading (2) | 2024.10.06 |
[ComputerScience] c++의 pointer와 reference 2편 (Dynamic Array, Static Array, Smart Pointers) (0) | 2024.09.30 |
[ComputerScience] c++의 pointer와 reference 1편 (포인터의 정의, 동적 메모리 할당, 포인터 연산) (0) | 2024.09.29 |
[ComputerScience] c++의 map과 set (1) | 2024.09.23 |