함수를 만들다보면, string을 넘길 일이 많다.
이럴 때, 한번쯤은 고민해도 좋을 것들에 대해 끄적여본다.
먼저.. 함수에 파라미터로 인자를 넘길 때, 아래 두 코드의 차이는 무엇일까?
void CMyClass::ChangeName(CString newName){
//m_myName을 갱신
}
void CMyClass::ChangeName2(const CString& newName2){
//m_myName을 갱신
}
PC성능이 워낙 좋은 요즘에야 잘 모르겠지만.. (그래도 모른다. 벌크로 반복작업이 수십만~수백만 회 일어난다면 다른 이야기다.)
만약 전달받은 newName을 read only로 사용한다면 당연히 아래 const CString&이 더 낫다.
먼저 const를 붙여서 readonly가 된 것도 있지만, 더 중요한 이유는...
외부에서 ChangeName을 호출하면, 어떤일이 일어날까?
ChangeName 함수에 넘어가는 파라미터인 newName변수를 새로 만들어야한다. 그리고 newName 객체가 문자열을 담는 메모리 공간에 문자열을 복사한다. 그만큼 메모리 공간이 필요할 것이다.
하지만 두번째 방법인 ChangeName2의 newName2는 CString객체를 새로 생성하는 것이 아닌 기존에 존재하는 CString문자열의 참조를 넘긴다. 즉, 레퍼런스로 쓰이는 주소를 전달하므로 메모리 공간을 덜 사용하게 된다.
혹시, 문자열이 주소를 나타내는 바이트 수보다 적다면 CString을 그냥 넘기는게 나은거 아닐까? 싶을 수 있다.
하지만, 애석하게도 그렇지는 않다. CString객체는 문자열 공간을 포인터 형태로 가지고있다. 내부에 미리 정의한 고정크기의 배열이 아니라. 필요시 동적으로 공간 할당/재할당 등을 수행해야한다. (문자열 길이가 얼마인지 생성하는 시점에서는 알 수 없으니까.) 즉, CString객체가 문자열을 저장하는 위치는 메모리의 힙 영역에 공간을 할당하고 생성된 힙 영역에 문자열을 저장하고, CString객체는 그 힙 주소를 가지고 있다. 메모리주소를 가지고 있는 것이다.
물론, 별도의 힙 메모리를 추가로 할당해야하는 것이니, const CString&으로 주소 참조 형태로 보내는 것 보다 메모리를 더 사용할 수 밖에 없다. 또한 복제에 시스템 리소스를 할당해야하니 참조로 전달하는 것이 더 빠를거다.
저런 작업이 반복적으로 수천~수만회가 이루어진다고 하면, 생각외로 수행 시간에서 많은 차이를 보일 수 있다.
이 내용은 CString뿐만 아니라, std::string에도 같이 적용된다.
이제 값을 반환받는 경우를 고민해보자. 의외로 아래와 같이 작성하는 경우가 많다.
CString CMyClass::GetNames(){
CString myName = "Hello";
return myName;
}
CString을 반환하는 것은 좋은데, 호출된 함수 내부에서 생성한 객체인 myName에 값을 할당하고, 그대로 반환하는 경우가 많다.
보통 이렇게 많이들 쓰긴 한다. 사실 큰 문제가 있는 것은 아니기도 할 수 있고. 시스템에 메모리는 의외로 충분하니까. 하지만 위와 같이 할 경우. 고려해야 할 사항이 하나 있다.
저 myName은 stack에 저장된 메모리이고, 함수를 빠져 나가는 순간 stack에 저장한 변수들은 살아있다고 장담할 수 없다. 특히, CString이 힙에 할당한 메모리 영역은 더더욱. 물론, 힙이 충분하니까. 크게 와닿지는 않지만, 내가 메모리를 엄청 많이 사용하고 있다면??? 정리당할 수 있는거고, 저 문자열에 접근했을 때, 이상한 값이 있다거나, 주소를 못찾을 가능성이 0은 아니라는 것.
만약 저 문자열을 받아와서 처리해야한다면 함수를 호출한 입장에서는 값이 들어오자마자 복제를 하던가, 아니면 아예 호출할 때, 파라미터로 문자열을 reference로 보내서 해당 메소드에서 쓰게하거나 하면 좀 나을듯?
댓글