Reference
참조
- 미리 정의된 어떤 변수의 실제 이름 대신 쓸 수 있는 대용 이름을 제공하는 기능이다. (포인터와 비슷한 면모가 있다.)
- 참조의 주된 용도로는 함수의 Parameter(형식 매개변수)에 사용하는 것이다.
- 매개변수로써 참조 변수를 이용하면, 해당 함수는 매개변수의 복사본이 아닌 원본을 갖고 작업하게 된다.
(이러한 점에서는 포인터 개념과 동일하다.)
- 또한, 참조 개념은 \(\texttt{class}\)를 설계할 때 필수적으로 사용되며, 일반적으로 C++에서는 클래스 객체를 함수에 전달할 때 참조로 전달한다.
- 참조 변수와 원본 변수는 모두 같은 값과 같은 메모리 위치를 공유한다.
- 참조 변수는 데이터형 바로 뒤에 \(\texttt{&}\) 연산자를 이용해서 선언한다.
dataType& variableName = value;
// 실 사용 예시
double Rnum = 12.34;
double& num = Rnum;
- 참조 변수는 선언 이후에 값을 지정할 수 없고, 선언함과 동시에 값이 지정되어야 한다.
(이러한 점에서, 포인터와 차이가 보인다. 포인터는 선언 이후에 주소를 할당할 수 있다.)
char Alpha = 'a';
char& R_pha; // 선언되자마자 초기화 되어야 한다!
R_pha = Alpha; // 에러가 발생한다!
- 참조 변수는 선언될 때 연결된 변수를 끝까지 고수해야 한다.
(이러한 점에서 \(\texttt{const}\) 포인터와 비슷하다.)
int num = 123;
int& Rnum = num;
...
int val = 379;
Rnum = val; // 에러가 발생한다!
int& rodents = rats;
// 위 구문과 아래 구문은 논리적으로 상당히 비슷하다.
int* const ptr = &rats;
- 그러나, 우회적으로 참조하는 대상을 변경할 수 있는 방법은 있다.
int rats = 101;
int* pt = &rats;
int& rodents = *pt;
int bunnies = 50;
pt = &bunnies; // rodents가 *pt를 참조하고 있음에는 변함이 없다!
Reference Argument (참조 매개변수)
* 참조 매개변수를 사용하는 주된 이유
1. 호출 함수에 있는 원본 데이터 객체의 수정을 허용하기 위해
2. 전체 데이터 객체 대신에 참조를 전달하여 프로그램의 실행속도를 개선하기 위해 (특히, 구조체, 클래스 객체에서)
* 참조 매개변수를 함수가 수정하지 않고 이용만 하는 경우의 가이드라인 (Read Only Case)
1. 기본 데이터형, 작은 규모의 구조체의 경우, 값으로 전달한다.
2. 배열의 경우. 포인터가 유일한 선택지이며, 포인터를 \(\texttt{const}\)를 지시하는 포인터로 선언한다.
3. 큰 규모의 데이터 객체의 경우, \(\texttt{const}\) 포인터나 \(\texttt{const}\) 참조를 이용하여 실행속도가 저해되는 것을 방지한다.
4. 클래스 객체의 경우, \(\texttt{const}\) 참조를 사용한다. 클래스 객체 매개변수의 전달은 참조로 전달하는 것이 C++ 표준이다.
* 함수가 참조 매개변수를 수정하는 작업을 수행할 경우의 가이드라인 (Read-Write Case)
1. 기본 데이터형의 경우, 포인터를 이용한다.
2. 배열의 경우, 포인터가 유일한 선택지이다.
3. 구조체의 경우 포인터 또는 참조를 이용한다.
4. 클래스 객체의 경우 참조를 이용한다.
함수가 참조를 리턴하는 것의 의미
- 참조를 리턴하고자 하는 경우, 함수 원형 부분이나 함수 정의 부분에서 리턴 데이터형 바로 뒤에 \(\texttt{&}\) 연산자를 붙여서 참조를 리턴함을 표시한다.
- \(\texttt{return}\) 구문을 통해서는 참조를 리턴할 방법이 없다.
- 일반적인 \(\texttt{return variables;}\) 구문에서는 \(\texttt{variables}\)의 복사본을 리턴하게 되는데,
참조를 리턴할 경우 해당 변수의 참조형을 리턴하게 된다.
- 위와 같은 이유로, 참조를 리턴하는 함수 호출 구문의 경우, 경우에 따라 대입구문에서 좌변에 위치할 수 있게 된다.
(값으로 리턴하는 경우에는 이러한 구문이 컴파일되지 않는다.)
Ex. 참조를 리턴하는 함수 예시
double& accumulate (double& target, const double source) {
// 참조로 입력받고 참조를 리턴한다.
target += source
return target;
}
...
double num1 = 12.34;
double num2 = 5.5;
accumulate(num1, num2);
std::cout << num1 << std::endl;
// 17.84가 출력된다.
accumulate(num1, num2) = 4.0; // 참조를 리턴하므로, 함수 호출 구문이 좌변에 위치할 수 있다.
std::cout << num1 << std::endl;
// num1와 num2의 가산값에 관련없이 4.0이 출력된다.
* 참조를 리턴할 때, 함수가 종료되면서 수명이 함께 끝나는 메모리 위치에 대한 참조를 리턴하지 않도록 주의해야 한다.
const float& clone (float ft) {
float newguy;
newguy = ft;
return newguy;
}
- 함수가 종료될 때 사라질 예정인 변수 \(\texttt{newguy}\)를 리턴하게 되면 문제가 발생하게 된다.
- 마찬가지로, 이러한 임시적인 변수의 포인터를 리턴하는 구문도 피해야 한다.
- 이 문제의 해결방안으로 크게 두 가지 방법이 있다.
1. 함수에 매개변수로 참조를 전달하고 리턴하는 방법
2. \(\texttt{new}\) 키워드를 이용해서 새로운 메모리 공간을 만드는 방법
- 함수 안에서 \(\texttt{new}\) 키워드를 사용할 경우, \(\texttt{delete}\)를 이용한 해제를 미이행할 수 있는데,
\(\texttt{auto_ptr}\) Templete이나 \(\texttt{unique_ptr}\)을 이용해서 예방할 수 있다.
\(\texttt{const}\) Reference (\(\texttt{const}\) 참조형)
- \(\texttt{const}\)를 사용하면, 의도하지 않은 데이터 변경으로 인한 프로그래밍 에러를 막을 수 있다.
- Prototype 부분에서 \(\texttt{const}\)를사용하면,함수가 \(\texttt{const}\)로 선언되거나 그렇지 않은 매개변수들을 모두 처리할 수 있지만, Prototype 부분에서 \(\texttt{const}\) 키워드가 생략된 경우에는 \(\texttt{const}\)로 선언되지 않은 매개변수만 처리할 수 있게된다.
- 위 원리와 마찬가지로, \(\texttt{const}\) 참조 형식 매개변수를 사용하면, 객체의 원본 데이터가 보존된다.
- \(\texttt{const}\) 참조를 통한 매개변수 전달은 데이터를 따로 복사하지(복사 생성자를 호출하지) 않아서 실행시간과 메모리 효율성이 개선된다.
- \(\texttt{const}\) 참조를 리턴하기 위해선, 입력받는 매개변수 또한 \(\texttt{const}\) 참조형이어야 한다.
- 객체의 복사본을 전달하는 것이 아니기 때문에, 매개변수의 Duration(존속기간)에 유의하여야 한다.
(함수가 실행중일 때에도 메모리에 존재하는 객체가 매개변수이어야 한다.)
Ex. 참조 매개변수에 \(\texttt{const}\) 키워드를 선언한 함수 Prototype 예시
const double& refcube (const double& ra);
// 위와 같은 함수 내에서 컴파일러가 상수 참조 변수 ra의 수정을 시도하는 코드를 발견하게 되면
// 에러 메세지를 내보내게 된다.