티스토리 뷰

프로그래밍/C++

Const Reference

국윤창 2017. 4. 20. 05:26

충분한 크기의 class, struct reference는 함수의 매개변수 또는 리턴값으로 쓸 때 그냥 객체 자체를 만들어서 넘겨주는 것보다 효과적이다.

그러면 어떨 때 써야하고 어떨 때 쓰지 말아야할까?

정확히 알고 써야할 것 같아서 몇가지 테스트를 해봤다.


잘 생각해보면 당연한 거지만 머리가 나빠 정리해둔다.

typedef struct TEST_STRUCT {
	double m1[5];
	float m2[5];
	int m3[5];
}TSTRUCT, *LPTSTRUCT;
위 코드처럼 테스트할 구조체를 만들어놨다.
그리고 이 구조체를 테스트할 함수 3개를 아래와 같이 만들었다.
// 객체를 만들고 value로 리턴
TSTRUCT GetTestObj();
// 객체를 만들고 const reference로 리턴
const TSTRUCT& GetTestObj2();
// const reference 객체 두개를 받아서 계산한 뒤 value로 리턴
TSTRUCT AddTest(__in const TSTRUCT& t1, __in const TSTRUCT& t2);

TSTRUCT GetTestObj()
{
	TSTRUCT temp;
	memset((void*)&temp, 0, sizeof(temp));
	for (int i = 0; i < 5; i++)
	{
		temp.m1[i] = static_cast<double>(i + 1);
		temp.m2[i] = static_cast<float>(i + 1);
		temp.m3[i] = i + 1;
	}

	return temp;
}

const TSTRUCT& GetTestObj2()
{
	TSTRUCT temp;
	memset((void*)&temp, 0, sizeof(temp));
	for (int i = 0; i < 5; i++)
	{
		temp.m1[i] = static_cast<double>(i + 1);
		temp.m2[i] = static_cast<float>(i + 1);
		temp.m3[i] = i + 1;
	}

	return temp;
}

TSTRUCT AddTest(__in const TSTRUCT& t1, __in const TSTRUCT& t2)
{
	TSTRUCT temp;
	for (int i = 0; i < 5; i++)
	{
		temp.m1[i] = t1.m1[i] + t2.m1[i];
		temp.m2[i] = t1.m2[i] + t2.m2[i];
		temp.m3[i] = t1.m3[i] + t2.m3[i];
	}

	return temp;
}


보통 위 코드의 두번째 함수 

const TSTRUCT& GetTestObj2();

같은 형태의 함수는 클래스 내의 멤버변수를 가져오는 멤버함수에서나 사용한다.

처음에는 함수의 리턴값이 지역변수라 리턴과 동시에 메모리에서 삭제가 된다고 해도, 아래 코드와 같이 리턴과 동시에 대입이 바로 이루어지면 메모리에서 삭제되기 전에 대입이 되기 때문에 상관없다고 생각했다. (사실 아래 코드는 C++ 표준에 어긋나는 내용이다. 값이 제대로 복사되는게 이상하다.)

TSTRUCT temp2 = GetTestObj2();
// 결과 제대로 나옴
for (int i = 0; i < 5; i++)
{
	cout << temp2.m1[i] << endl;
	cout << temp2.m2[i] << endl;
	cout << temp2.m3[i] << endl;
}

그러나 위 코드의 AddTest 함수는 어떨까?
아래 코드와 같이 테스트를 해보았다. 결과는 당연히 이상하게 나왔다. 

왜냐하면 C++ 표준에 따르면 함수 리턴값은 temporary(임시변수)로 저장되는데, const TSTRUCT&를 리턴하면 const TSTRUCT&가 가리키는 메모리는 삭제가 되고 const TSTRUCT& 자체만 temporary에 남기때문에 결국 이상한 값이 들어있는 메모리를 temp2가 가리키게 되는 것이다.

따라서 함수의 리턴값이 temporary에 저장되게 하려면 reference가 아닌 value 자체를 리턴해야한다.
이 코드로 나온 결과는 아래 그림 1이다.

const TSTRUCT& temp3 = GetTestObj2();
// 완전 이상한 값이 나온다.
for (int i = 0; i < 5; i++)
	{
		cout << temp3.m1[i] << endl;
		cout << temp3.m2[i] << endl;
		cout << temp3.m3[i] << endl;
	}

[그림 1] 쓰레기 값


함수의 리턴값으로 reference 대신 value로 리턴하는 것은 결국 아래 코드와 같은 경우 두 번 복사가 이루어지기 때문에 비효율적이라는 생각을 했으나, 위와 같은 문제를 피하기 위해서는 지역변수를 리턴할 때에는 reference로 하지 않아야한다.

// GetTestObj가 리턴될 때 값 복사가 한 번, 그 값이 temp에 복사되면서 복사가 한 번 일어남
TSTRUCT temp = GetTestObj();

또한 C++ 표준에 따르면 temporary는 const reference가 참조하게 되면 수명이 연장되게 된다. 
따라서 AddTest함수를 아래와 같이 써도 AddTest함수의 매개변수가 const reference이기 때문에 정상적으로 값을 사용할 수 있는 것이다.

// 마찬가지로 temp는 const reference이므로, AddTest함수의 리턴 temporary의 수명이 연장된다.
const TSTRUCT& temp = AddTest(GetTestObj(), GetTestObj());
// 2초가 지나도 값이 제대로 나온다
Sleep(2000);
for (int i = 0; i < 5; i++)
{
	cout << temp.m1[i] << endl;
	cout << temp.m2[i] << endl;
	cout << temp.m3[i] << endl;
}



결론은 매개변수 또는 함수의 리턴이 클래스 또는 구조체인 경우...

클래스 멤버변수 Get 함수는 const reference (const TSTRUCT&)

지역변수 리턴은 value (TSTRUCT)

함수의 매개변수는 input일 때 const reference (const TSTRUCT&), output일 때 reference(TSTRUCT&)


위와 같이 사용하는 것이 바람직하겠다.

'프로그래밍 > C++' 카테고리의 다른 글

Union-Find  (0) 2018.05.23
Sort  (0) 2018.05.14
Windows C++ RTP Streaming from Cam using FFMPEG and OpenCV  (22) 2017.03.16
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/02   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29
글 보관함