참조와 함수
1. 참조 개념
C++에서는 C 언어에 없는 참조(reference)개념을 도입하였다. 포인터 변수를 선언하기 위해 * 기호를 사용하지만, 참조 변수를 선언하기 위해서는 & 기호를 사용한다. &를 참조자라고 부른다.
C++에서 참조는 다음과 같이 활용된다.
1) 참조 변수
2) 참조에 의한 호출
3) 함수의 참조 리턴
2. 참조 변수
1) 참조 변수 선언
참조 변수는 이미 선언된 변수(원본 변수로 지칭)에 대한 별명으로서, 참조자(&)를 이용하여 선언하며, 선언 시 반드시 원본 변수로 초기화하여야 한다. 다음은 두개의 참조 변수 refn과 refc를 선언하는 코드이다.
i.e.
int n = 2;
int &refn = n; //참조 변수 refn 선언. refn은 n에 대한 별명으로, 둘은 동일한 변수이다.
Circle circle;
Circle &refc = circle; //참조 변수 refc 선언. refc는 circle에 대한 별명. refc와 circle은 동일한 객체를 가리키는 동일한 변수이다.
참조 변수가 선언되면, 참조 변수 이름만 생성되며, 별도의 공간이 할당되지 않는다. 대신, 참조 변수는 초기화로 지정된 원본 변수의 공간을 공유한다.
2) 참조 변수 사용
참조 변수를 사용하는 방법은 보통 변수와 동일하며, 참조 변수에 대한 사용은 바로 원본 변수의 사용이다. 다음 예를 보자.
i.e.
int n = 2;
int &refn = n; //refn은 n의 참조 변수
refn = 3; //refn = 3 이므로, n의 값도 3이 된다.
n = 5; //n=5 이므로, refn의 값도 5가 된다.
참조 변수는 포인터가 아니므로, 다음과 같이 사용하지 않도록 주의하라.
i.e.
refc->setRadius(30); //컴파일 오류. refc.setRadius(30)로 해야 한다. (참조 변수는 포인터가 아니라 일반 변수이기 때문이다.)
3) 참조 변수 선언 시 주의 사항
- 초기화가 없다면 컴파일 오류가 발생한다.
i.e.
int &refn; //컴파일 오류. 참조 변수는 선언 시에 초기화되지 않으면 컴파일 오류 발생.
- 참조자 &이 타입명과 변수명 사이에 있으면 된다.
i.e.
int &refn = n;
int & refn = n;
int& refn = n;
위의 3개의 참조 변수 선언은 모두 동일하다.
- 참조자 &의 사용에 유의해야 한다. 다음은 문법적으로 잘못된 참조 변수 선언이다.
i.e.
& int refn = n; //컴파일 오류
int refn & = n; //컴파일 오류
- 참조 변수의 배열을 만들 수 없다.
i.e.
char &n[10]; //컴파일 오류. 참조의 배열을 만들 수 없다.
- 참조 변수에 대한 참조 선언이 가능하다.
i.e.
int&r = refn; //참조 변수 refn에 대한 참조 변수 r 선언 가능
3. 참조에 의한 호출 (call by reference)
원본 변수와 참조 변수를 함께 사용하면 변수 사용이 혼란스러운 것은 사실이다. 그래서 일반적으로 C++ 프로그래머들도 참조 변수를 잘 사용하지 않는다. 하지만, 잘 사용하지 않는다고 해서 아예 사용하지 않는 것은 아니다. 그렇다면, 참조는 어디에 가장 많이 사용될까?
참조는 C++의 새로운 인자 전달 방식인 '참조에 의한 호출'에 많이 사용된다.
참조에 의한 호출은 함수의 매개 변수를 참조 타입으로 선언하여, 매개 변수가 함수를 호출 하는 쪽의 실인자를 참조하여 실인자와 공간을 공유하도록 하는 인자 전달 방식이다. 참조 타입으로 선언된 함수의 매개 변수를 참조 매개 변수라고 부른다.
- 참조 매개 변수가 필요한 경우
다음과 같이 평균을 구하여 리턴하는 함수가 있다고 하자.
i.e.
int average (int a[], int size) {
if (size <= 0) return 0; //size는 음수가 될 수 없기에..
int sum = 0;
for (int i = 0; i < size; i++)
sum += a[i];
return sum / size;
}
위의 함수에는 별 문제가 없어 보인다. 그러나 만일 다음과 같이 함수를 호출하면 어떻게 되겠는가?
i.e.
int x[] = {1,2,3,4};
int avg = average(x, -1); //avg에 0이 리턴된다.
위의 경우와 같이 avg에 0이 리턴되면 평균이 0인지 아니면 2번째 인자의 값이 음수여서 0을 리턴한 것인지 알 수가 없기 때문이다. 이러한 문제를 해결하기 위해서 참조 매개 변수를 사용한다. 다음 예제를 통해서 참조 매개 변수의 적절한 사용 방법에 대해서 알아보자.
i.e.
#include <iostream>
using namespace std;
bool average(int a[], int size, int& avg) {
if (size <= 0)
return false;
int sum = 0;
for (int i = 0; i < size; i++)
sum += a[i];
avg = sum / size;
return true;
}
int main() {
int x[] = {0,1,2,3,4,5};
int avg;
if (average(x, 6, avg))
cout << "평균: " << avg << endl;
}
- 참조에 의한 호출의 장점
주소에 의한 호출은 포인터 타입으로 매개 변수를 선언하므로, 호출하는 쪽에서 주소를 전달해야 한다. 그러 인해서 * 기호 등을 반복적으로 사용하게 되면서 실수의 가능성과 코드 작성의 긴장감 등이 배가 된다. 또한 주소에 의한 호출에서 * 기호를 남용하게 되면 가독성이 떨어지게 된다.
그러나 참조 매개 변수를 사용하면 간단히 변수를 넘겨주기만 하면 되고, 함수 내에서도 참조 매개 변수를 보통 변수처럼 사용하기 때문에 작성하기 쉽고 가독성이 올라가기도 한다.
4. 참조에 의한 호출로 객체 전달
값에 의한 호출로 객체를 매개 변수에 전달하면 다음 두 가지 사항에 유의해야 한다.
1) 함수 내에서 매개 변수 객체를 변경하여도, 원본 객체를 변경시키지 않는다.
2) 매개 변수 객체의 생성자가 실행되지 않고 소멸자만 실행되는 비대칭 구조로 작동한다.
그러나 참조에 의한 호출은 이 두 가지 사항에 완전히 다르게 작동한다.
1) 참조 매개 변수로 이루어진 모든 연산은 원본 객체에 대한 연산이 된다.
2) 참조 매개 변수는 이름만 생성되므로, 생성자와 소멸자는 아예 실행되지 않는다.
5. 참조 리턴
C 언어에서 함수가 리턴하도록 허용된 것은 오직 값뿐이다. 값에는 void를 포함하여 정수, 문자, 실수 등의 기본 타입의 값과 주소(포인터)가 있다. 이와 달리 C++에서는 함수가 참조를 리턴할 수 있다. 참조 리턴이란 변수 등과 같이 현존하는 공간에 대한 참조의 리턴이다.
다음 예시를 보도록 하자.
i.e.
char c = 'a';
char get() {
return c; //변수 c의 값을 리턴
}
char& find() {
return c; //변수 c에 대한 참조 리턴
}
char a = find(); //a = 'a'가 됨
char b = get(); //b = 'a'가 됨
char &ref = find(); //ref는 c에 대한 참조
find() = 'b'; //c = 'b'가 됨
- 참조 리턴에 대한 치환문
다음과 같이 find()가 치환문(=)의 오른쪽에 온다면 변수 c의 값 'a'가 변수 a에 치환된다.
i.e.
char a = find(); //a = 'a'
그러나 다음과 같이 참조 변수로 참조를 리턴 받을 수 있다.
i.e.
char& ref = find();
이 코드의 실행 결과 ref는 find()가 리턴한 변수 c의 참조가 된다. 그러므로 다음과 같이 ref에 대한 연산은 모두 변수 c에 대해 이루어지는 연산이 된다.
i.e.
ref = 'M'; //c = 'M'
'C++ > C++ 기본' 카테고리의 다른 글
묵시적 복사 생성 (0) | 2018.07.28 |
---|---|
복사 생성자 (0) | 2018.07.27 |
객체 치환 및 객체 리턴 (0) | 2018.07.26 |
함수 호출 시 객체 전달 (0) | 2018.07.26 |
cin.getline()과 getline()의 차이점 (0) | 2018.07.24 |