난 게임을 만들고 싶은 사람이다.
예전부터 컴퓨터 게임을 할 때마다 '내가 이런 게임을 만들어 보면 어떨까' 하고 생각했었다.
나름대로 수많은 pc게임을 접해오면서 느낀건
"버그 없이, 빠르게 잘 돌아가야 한다" 는 것이었다.
게임은 아무리 잘만들어도 느리면 소용없다. 하고 싶은 맘이 싹 달아달게 분명하다.
이런 이야기를 하는 이유는 오늘 이야기 할 것이 바로
"참조자로 매개변수에 객체 전달" 이기 때문이다.
이런 말들은 정말 많이 들어보셨으리라 믿는다.
Call by reference
Call by value
설명하자면...
하나는 값으로 전달하는 방법이고, 하나는 참조로 전달하는 방법이다.
값으로 전달하는 놈은 복사를 행하고,
참조로 전달하는 놈은 단순히 원래 객체에 이름표만 붙인다는 점이 다른것이다.
아직 무슨말인지 모르시겠다면 다음을 보면서 이야기 하겠다.
위에서 CData형 클래스는 멤버변수로 int형 배열을 가지고 있다.
그리고 생성과 동시에 복사가 일어났을 경우에 콘솔창에서 직접 확인하기 위해
복사생성자를 재정의 해주었다
이 클래스로 객체를 생성했을 경우 그 객체의 사이즈는 4byte(int) * 10 = 40byte인 것이다.
자 이제 이 클래스형을 매개변수로 받는 함수 FuncA를 작성해보자.
지금 현재 이 함수 FuncA에서는 그다지 하는 일이 없지만,
우리는 이 함수가 1프레임에 100번이상 호출되는 중요한 함수라고 가정하자.
메인에서 이런식으로 FuncA 함수를 사용하였다. 콘솔창에 출력되는 결과값은 어떻게 될까.
한번 예상해보고 아래쪽을 보시기 바란다.
생성과 동시에 복사가 이루어질 경우만 호출되는 복사생성자가 함수 한번 실행에
무려 3번!이나 호출되었다.
한번은 당연히 CData형 객체 temp가 생성됨과 동시에 같은 타입의 _data를 복사하기 때문에
복사생성자가 호출되었다.
그렇다면 나머지 두번은 어디에서 호출되는것일까.
답은
하나는 매개변수에서 호출되고
다른 하나는 리턴할 때 호출된다는 것이다.
위와같은 코드가 있을 경우 컴파일러는 이런 코드를 생성해낸다.
(FuncA함수의) CData _data((main함수의)oData); -> 매개변수에서의 코드
...
CData abcd(_data) 을 main함수내에 생성 -> 리턴에서의 코드
(여기서 abcd는 임의의 변수 이름)
이렇듯 FuncA 함수내의 temp객체가 생성되는 과정 이외에도 2번의 복사가 더 일어나는 것이다.
자 그럼 이것이 뭐 어쨌다는 것인가.
생각해보자. 40byte짜리 객체를 3번 복사하는건 120byte정도의 복사비용이 든다.
뭐 이정도도 컴퓨터가 워낙빠르기 때문에 감당할 수 있다고 하자.
그런데 우리가 이 함수를 사용하려는 게임에서는 프레임당 이 함수를 100번이상 호출한다고 해보자.
그러면 상황은 달라진다. 1프레임당 12,000byte의 복사가 계속해서 이루어지는 것이다.
그렇게 되면 게임은 우리가 의도한것과는 다르게 느려지게 되는 것이다.
그럼 우짜란 말인가.
아 일단 이 글을 읽으시는 분이 참조자나 포인터에 대해서
어느정도 잘 알고 계신다고 믿고 설명하는것이다.
이제 값에 의한 전달이 아닌 참조에 의한 전달을 해야 할 순간이 온 것이다.
자 이제 복사가 일어나려는 부분을 수정해보자.
일단 매개변수에서의 복사를 없애야 할 것이다.
이제 위의 값에 의한 전달에서 행하여 지는 일과 비교를 해보자.
(FuncA함수의) CData& _data = (main함수의)oData; -> 매개변수에서의 코드
CData의 참조자 _data를 만들어 줌과 동시에 oData를 참조하고 있다.
참조자는 생성과 동시에 반드시 초기화 되어야 하는데,
매개변수는 생성과 동시에 모든일을 진행하므로 참조자로 넘겨주는 것이 가능하다는 이야기다.
그렇다면 이제 다시한번 결과를 보자
이제 40byte의 복사 비용 하나를 줄였다.
그리고 리턴에도 참조자를 적용해보자.
결과는 또 아래와 같다.
오오...이제 80byte의 복사비용을 절감한 것이다.
복사를 하지 않고 참조자를 이용해서 이름표를 붙여줌으로써 정말 확실한 효과를 봤다.
그러나 한가지 걱정되는건, 매개변수로 넘겨온 값을 사용은 하되, 그 참조되는 실제 객체의
값 자체는 바꾸고 싶지 않을 때다. 그것도 문제가 될 것이 없다. 넘겨오는 매개변수의 참조자를
상수화시키면 된다.
이제는 함수내에서 그 넘겨온 참조자 객체의 값을 모르고 변경할 위험에서도 벗어나게 되었다.
하지만 이코드는 오류를 하나 뿜어낼 것이다.
왜냐하면 매개변수에서 상수화를 시켜서 값 변경이 불가능하게 했는데
리턴할때는 값이 변경 가능한 참조자로 바꾸려고 해서다.
그래서 리턴값도 상수화가 필요하니 리턴 타입 앞에 const를 붙이면 땡이다.
이렇게 매개변수에서의 참조자는 간단하지만 정말 초강력한 기능을 발휘한다는 점이 참 멋지다.
리턴에서는 또 리턴 값 최적화라는 컴파일러에서 많이 지원되고 있으므로 그 부분이 궁금한
사람들은 또 전문서적이나 인터넷을 뒤져서 읽어보는것도 많이 도움이 될 듯하다.
그럼 모두모두 즐코딩!
작성자 : kaltznnnyyy