728x90

황기태 저자의 명품 C++ Programming 개정판을 읽고 학습한 내용을 정리한 포스트입니다!

https://search.shopping.naver.com/book/catalog/32436115747

 

C++ Programming : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

참조에 의한 호출, 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;
}

 

 

참조에 의한 호출의 장점
  • 주소에 의한 호출은 호출하는 쪽에서는 주소 전달 위해 &, 전달받는 함수에서는 * 연산자를 반복적 사용함으로써, 실수의 가능성과 가독성이 떨어진다.
  • 참조 매개 변수를 사용하면 간단히 변수를 넘겨주면 되고, 함수 내에서도 참조 매개 변수를 보통 변수처럼 사용하기 때문에 작성하기 쉽고 보기 좋은 코드가 된다.

참조에 의한 호출로 객체 전달

값에 의한 호출로 전달 시 아래 두 가지 사항에 유의해야 한다.

  1. 함수 내에서 매개 변수 객체를 변경하여도, 원본 객체를 변경시키지 않는다.
  2. 매개 변수 객체의 생성자가 실행되지 않고 소멸자만 실행되는 비대칭 구조로 작동한다.

하지만, call by reference는 완전 다르게 작동한다.

  1. 참조 매개 변수로 이루어진 모든 연산은 원본 객체에 대한 연산이 된다.
  2. 참조 매개 변수는 이름만 생성되므로, 생성자와 소멸자는 아예 실행되지 않는다.

참조 리턴

  • 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