728x90
황기태 저자의 명품 C++ Programming 개정판을 읽고 학습한 내용을 정리한 포스트입니다!
https://search.shopping.naver.com/book/catalog/32436115747
참조에 의한 호출, call by reference
- 이전 게시물에서 참조&에 대해 알아보았는데 원본 변수와 참조 변수를 함께 사용하면 변수 사용이 혼란스러워지는 것은 사실이다.
- 참조는 C++의 새로운 인자 전달 방식인 '참조에 의한 호출'에 많이 사용된다.
- 함수의 매개 변수를 참조 타입으로 선언, 매개 변수가 함수를 호출 하는 쪽의 실인자를 참조(reference)하여 실인자와 공간을 공유
- 참조 타입으로 선언된 함수의 매개 변수를 참조 매개 변수(reference parameter)라고 부른다.
void swap(int &a, int &b);
int m = 2, n = 9;
swap(m, n); // 참조에 의한 호출
- swap(m, n); 함수 호출은 '값에 의한 호출'과 모양이 동일하여 헷갈릴 수 있는데, 함수의 원형에 의해 구분
#include <iostream>
using namespace std;
void swap(int &a, int &b) { // 참조 매개 변수 a, b
int tmp;
tmp = a;
a = b; // 참조 매개 변수를 보통 변수처럼 사용
b = tmp; // a, b는 m, n의 별명으로서 이름만 생성되고 변수 공간 생기지 않음
}
int main() {
int m=2, n=9;
swap(m, n);
cout << m << ' ' << n;
}
참조 매개 변수가 필요한 사례
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;
}
위 함수는 별 문제가 없어 보이지만 아래와 같이 함수를 호출하면 어떻게 될까?
int x[]={1,2,3,4};
int avg = average(x, -1); // avg에 0이 리턴된다.
- avg에 0이 리턴되기 때문에 평균이 0이라고 생각하지만, 사실 매개 변수에 잘못된 값이 전달되었음을 알리는 0이다.
- 따라서, average()의 리턴 값이 0인 경우, 계산된 평균이 0인지 잘못된 매개 변수를 알리기 위한 0인지 알 수 없기에, 아래와 같이 average() 함수를 변경해준다.
#include <iostream>
using namespace std;
bool average(int a[], int size, int& avg) {
if(size <= 0)
return false; // return 타입을 bool로 하고 평균값을 전달하기 위해 참조 매개 변수를 추가함
int sum = 0;
for(int i=0; i<size; i++)
sum += a[i];
avg = sum/size; // 참조 매개 변수 avg에 평균값 전달
return true;
}
int main() {
int x[] = {0,1,2,3,4,5};
int avg; // 참조 매개변수로 넘기는 경우 초기화가 되어있을 필요가 없다 => 주소를 공유하고 있기 때문에
if(average(x, 6, avg)) cout << "평균은 " << avg << endl; // avg에는 평균이 넘어오고, average()는 true 리턴
else cout << "매개 변수 오류" << endl;
if(average(x, -2, avg)) cout << "평균은 " << avg << endl; // avg의 값은 의미 없고, average()는 false 리턴
else cout << "매개 변수 오류 " << endl;
}
참조에 의한 호출의 장점
- 주소에 의한 호출은 호출하는 쪽에서는 주소 전달 위해 &, 전달받는 함수에서는 * 연산자를 반복적 사용함으로써, 실수의 가능성과 가독성이 떨어진다.
- 참조 매개 변수를 사용하면 간단히 변수를 넘겨주면 되고, 함수 내에서도 참조 매개 변수를 보통 변수처럼 사용하기 때문에 작성하기 쉽고 보기 좋은 코드가 된다.
참조에 의한 호출로 객체 전달
값에 의한 호출로 전달 시 아래 두 가지 사항에 유의해야 한다.
- 함수 내에서 매개 변수 객체를 변경하여도, 원본 객체를 변경시키지 않는다.
- 매개 변수 객체의 생성자가 실행되지 않고 소멸자만 실행되는 비대칭 구조로 작동한다.
하지만, call by reference는 완전 다르게 작동한다.
- 참조 매개 변수로 이루어진 모든 연산은 원본 객체에 대한 연산이 된다.
- 참조 매개 변수는 이름만 생성되므로, 생성자와 소멸자는 아예 실행되지 않는다.
참조 리턴
- C언어와 같이 void, 정수, 문자, 실수 등 기본 타입의 값 + 주소(포인터) 뿐만 아닌 함수가 참조를 리턴할 수 있다.
- 참조 리턴이란 변수 등과 같이 현존하는 공간에 대한 참조의 리턴이다.
char c = 'a';
char get() { // char 값 리턴
return c;
}
char a = get(); // a = 'a'가 됨
get() = 'b'; // 컴파일 오류
// 위는 문자에 대한 리턴
// =============================================
// 아래는 char 타입의 공간에 대한 참조리턴
// 따라서, find()가 리턴한 공간에 'b' 문자 저장
char c = 'a';
char& find() { // char 타입의 참조 리턴
return c; // 변수 c에 대한 참조 리턴
}
char a = find(); // a = 'a'가 됨
char& ref = find(); // ref는 c에 대한 참조
ref = 'M'; // c = 'M'
find() = 'b'; // c = 'b'가 됨
- 위의 return 문은 변수 c의 값 'a'가 아닌 변수 c에 대한 참조를 리턴하므로, find() = 'b';의 경우 c의 공간에 'b'를 삽입한다.
- 아래는 참조를 리턴하는 사례의 예시이다.
#include <iostream>
using namespace std;
// 배열 s의 index 원소 공간에 대한 참조 리턴하는 함수
char& find(char s[], int index) {
return s[index]; // 참조 리턴
}
int main() {
char name[] = "Mike";
cout << name << endl;
find(name, 0) = 'S'; // name[0]='S'로 변경
cout << name << endl;
char& ref = find(name, 2);
ref = 't'; // name = "Site"
cout << name << endl;
}
Tip!!!
참조 리턴의 중요성
참조 리턴은 c++에서 많이 사용되는데 특히, string 클래스의 멤버 함수들이나 C++ 표준 라이브러리에는 참조를 리턴하는 많은 함수들이 존재한다. 심지어, 참조를 리턴하지 않고는 작성하기 어려운 연산자 함수같은 것도 있다.
char&, char*, char 사용에 대한 비교
char c = 'a'; // 변수 c 생성. 'a'로 초기화
char* p = &c; // 포인터 변수 p 생성. p는 변수 c의 주소를 가짐
char& s = c; // 변수 s는 이름만 생성. s는 c에 대한 별명
*p = 'b'; // c = 'b'
s = 'c' ; // c = 'c'
- 참조를 리턴하는 함수와 포인터를 리턴하는 함수가 서로 어떻게 다르지 코드로 보자.
char c = 'a';
char& r() { // 참조 리턴
return c; // c의 참조 리턴. 변수 c의 참조(공간) 리턴
}
r() = 'c'; // 변수 c = 'c'로 변경
char* p() { // 주소 리턴. 주소는 값이다.
return &c; // 변수 c의 주소 리턴
}
char* s = p(); // s는 c의 주소 저장
*s = 'b'; // s가 가리키는 변수 c = 'b'로 변경
p() = 'b'; // 컴파일 오류
- 포인터를 리턴하는 함수를 참조로 리턴하는 함수처럼 사용하면 컴파일 오류가 발생한다.(마지막 코드)
- Why?
- L-value에는 값이 올 수 없기 때문
- 공간 = 값이 와야 = 문이 성립된다.
- p() 함수가 리턴한 것은 값(주소도 하나의 값)이기 때문에 오류 발생
728x90
'Programming Language > C++' 카테고리의 다른 글
[C++] 함수 중복 (0) | 2023.12.04 |
---|---|
[C++] 복사 생성자 (1) | 2023.12.04 |
[C++] 함수와 참조 (0) | 2023.12.03 |
[C++] string 클래스를 이용한 문자열 사용 (1) | 2023.12.01 |
[C++] this 포인터 (1) | 2023.12.01 |