B. C++ 함수
1. 참조자(reference)
a. 참조자란?
특정 변수의 실제 이름 대신 참조자라는 기능이 추가
b. 참조자의 선언
📘 문법
int 변수이름; // 변수의 선언
int& 참조자이름 = 변수이름; // 참조자 선언
🤍 문법 설명
& 연산자 : 주소 연산자가 아닌 타입을 식별하기 위해 사용하는 식별자로 사용
즉, int형 변수에 대한 참조를 의미하며 참조자는 대상 변수와 같은 메모리 위치를 참조하게 됨
🧡 주의사항
참조자의 타입 = 대상이 되는 변수
참조자는 선언과 동시에 초기화
한번 초기화되면 참조하는 대상을 변경할 수 없음
❔ 예제 : 참조자의 증가연산에 의한 대상 변수의 변경
#include <iostream>
using namespace std;
int main(void)
{
int x = 10; // 변수의 선언
int& y = x; // 참조자 선언
cout << "x : " << x << ", y : " << y << endl;
y++; // 참조자를 이용한 증가 연산
cout << "x : " << x << ", y : " << y << endl;
cout << "x의 주소값 : " << &x << ", y의 주소값 : " << &y;
return 0;
}
✨ 실행결과
x : 10, y : 10
x : 11, y : 11
x의 주소값 : 0x7ffebcc5148c, y의 주소값 : 0x7ffebcc5148c
🧡 포인터와 참조자?
참조에 의한 전달은 참조자 뿐만 아니라 포인터를 사용해도 똑같은 결과를 얻을 수 있음
사용방법은 같지만 구문 형태상 차이점만 존재!!
c. 참조자의 사용
- 참조자는 크기가 큰 구조체와 같은 데이터를 주로 함수의 인수로 전달해야 할 경우에 사용
- 클래스를 설계할때에도 자주 사용함 (함수에 사용하는 것 보다 더 좋은방법)
함수의 인수로서 전달하는 경우
함수가 참조자를 인수로 받으면, 참조자의 변수의 값을 함수 내에서 조작할 수 있음
❔ 예제 : 함수에서 참조자로 두 변수의 값을 서로 맞바꾸는 예제
#include <iostream>
using namespace std;
void Swap(int&, int&);
int main(void)
{
int num1 = 3, num2 = 7;
cout << "변경 전 num1의 값은 " << num1 << "이며, num2의 값은 " << num2 << "입니다." << endl;
Swap(num1, num2);
cout << "변경 후 num1의 값은 " << num1 << "이며, num2의 값은 " << num2 << "입니다." << endl;
return 0;
}
void Swap(int& x, int& y)
{
int temp;
temp = x;
x = y;
y = temp;
}
✨ 실행결과
변경 전 num1의 값은 3이며, num2의 값은 7입니다.
변경 후 num1의 값은 7이며, num2의 값은 3입니다.
함수 내에서 * 연산자를 사용하지 않아 함수 내부의 코드가 깔끔하고 직관적
💥 하지만 함수 호출이 값에 의한 전달 방법(원래 기본 형태) 과 같은 형태라 코드를 읽기 쉽지 않음
따라서 간단한 함수에서는 참조 전달보다 값에 의한 전달을 사용하는 것이 더 좋다
또한 참조 호출이 꼭 필요할 때에는 참조자 보단 포인터를 사용하는게 더 직관적
구조체의 참조
❔ 예제 : 함수에서 구조체 참조자로 사용
#include <iostream>
using namespace std;
struct Book
{
string title;
string author;
int price;
};
void Display(const Book&);
int main(void)
{
Book web_book = {"HTML과 CSS", "홍길동", 28000};
Display(web_book);
return 0;
}
//함수 내부에서 구조체를 직접 변경할 필요가 없을 때
//const 원본 구조체에 대한 변경을 허용하지 않음
void Display(const Book& bk)
{
cout << "책의 제목은 " << bk.title << "이고, ";
cout << "저자는 " << bk.author << "이며, ";
cout << "가격은 " << bk.price << "원입니다.";
}
✨ 실행결과
책의 제목은 HTML과 CSS이고, 저자는 홍길동이며, 가격은 28000원입니다.
2. 디폴트 인수(default argument)
a. 디폴트 인수란?
🌟 기본값이 미리 정의되어 있는 인수를 의미
함수를 호출할 때 인수를 전달하지 않으면, 함수는 자동으로 미리 정의되어 있는 디폴트 인수 값을 사용
물론 인수를 전달해 함수를 호출하면, 디폴트 인수값이 아닌 전달된 인수를 가지고 함수를 호출하게 됨
b. 디폴트 인수의 설정
- 함수의 원형에만 지정할 수 있음
- 가장 오른쪽 부터 시작해 순서대로 지정할 수 있음
- 가운데 인수들만 별도로 디폴트 인수를 지정할 수는 없음
😀 함수의 원형 예제
1. void Display(int x, int y, char ch, int z = 4); // 가능함.
2. void Display(int x, int y, char ch = 'a', int z = 4); // 가능함.
3. void Display(int x, int y = 2, char ch, int z = 4); // 오류
4. void Display(int x = 1, int y = 2, char ch, int z); // 오류
c. 디폴트 인수가 설정된 함수의 호출
함수로 전달된 인수는 왼쪽부터 대입됨
따라서 인수가 설정된 함수를 호출할 때에는 인수의 전달을 건너뛸 수 없음 ❌❌
😀 함수의 호출 예제
void Display(int x, int y, char ch = 'a', int z = 4); // 함수의 원형
1. Display(1, 2); // 가능함 -> display(1, 2, 'a', 4)와 같음.
2. Display(3, 4, 'b'); // 가능함 -> display(3, 4, 'b', 4)와 같음.
3. Display(5, 6, 'c', 9); // 가능함 -> display(5, 6, 'c', 8)와 같음.
4. Display(7, 8, , 9); // 오류 : 인수 전달은 건너뛸 수 없음.
d. 디폴트 인수 예제
❔ 예제
#include <iostream>
using namespace std;
double Multi(double, double = 2);
int main(void)
{
cout << Multi(3) << endl; // Multi(3, 2)와 같음 : 3 * 3
cout << Multi(3, 3) << endl; // 3 * 3 * 3
cout << Multi(3, 4); // 3 * 3 * 3 * 3
return 0;
}
double Multi(double x, double n)
{
double result = x;
for (int i = 1; i < n; i++)
{
result *= x;
}
return result;
}
✨ 실행결과
9
27
81
3. 함수 오버로딩
a. 함수 오버로딩이란?
🌟 같은 일을 처리하는 함수를 매개 변수의 형식을 달리 해 같은 이름으로 중복해서 정의하는 것
👉 객체 지향 프로그래밍 특징 중 다형성의 구현
b. 함수 시그니처(function signature)
🌟 함수의 원형에 명시되는 매개 변수 리스트
함수 오버로딩의 핵심은 함수 시그니처(function signature)에 있음
두 함수가 매개 변수의 개수와 타입이 모두 같으면 시그니처가 같다고 할 수 있음
👉 즉, 오버로딩은 다른 시그니처의 여러 함수를 같은 이름으로 정의하는 것이라고 볼 수 있음
~ 변수의 개수나 타입이 다른게 하나라도 있으면 오버로딩 된다 ~
c. 함수 오버로딩 과정
- 오버로딩된 함수 호출
- 같은 시그니처를 가지는 함수의 원형을 찾아 호출
💥 오버로딩한 함수는 모호한 호출(호출 시 여러개의 함수에 걸쳐지는) 을 허용하지 않고 오류를 발생시킴
😀 함수의 원형 예제
1. void Display(const char* str, int n); // 문자열 str을 n번 출력함.
2. void Display(const char* str1, const char* str2); // 문자열 str1과 str2를 연달아 출력함.
3. void Display(int x, int y); // x * y를 출력함.
4. void Display(double x, double y); // x / y를 출력함.
😀 함수의 호출 예제
1. Display("C++", 3); // 1번 Display() 함수 호출 -> "C++C++C++"
2. Display("C++", " Programming"); // 2번 Display() 함수 호출 -> "C++ Programming"
3. Display(3, 4); // 3번 Display() 함수 호출 -> 12
4. Display(4.2, 2.1); // 4번 Display() 함수 호출 -> 2
5. Display(4.2, 3); // 3번과 4번 모두 호출 가능 -> 컴파일 오류가 발생함.
d. 함수 오버로딩 예제
❔ 예제
#include <iostream>
using namespace std;
void Shift(int, int);
void Shift(int, int, int);
void Shift(int, int, int, int);
int main(void)
{
Shift(1, 2);
Shift(1, 2, 3);
Shift(1, 2, 3, 4);
return 0;
}
void Shift(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << a << ", " << b << endl;
}
void Shift(int a, int b, int c)
{
int temp;
temp = a;
a = b;
b = c;
c = temp;
cout << a << ", " << b << ", " << c << endl;
}
void Shift(int a, int b, int c, int d)
{
int temp;
temp = a;
a = b;
b = c;
c = d;
d = temp;
cout << a << ", " << b << ", " << c << ", " << d << endl;
}
✨ 실행결과
2, 1
2, 3, 1
2, 3, 4, 1
4. 인라인 함수
a. C++에서의 함수 호출 과정
- 함수 호출
- 스택에 함수로 전달할 매개변수와 함께 호출이 끝난 뒤 돌아갈 반환 주소값을 저장
- 프로그램의 제어가 함수의 위치로 넘어와 함수 내에 선언된 지역 변수도 스택에 저장
- 함수의 모든 코드를 실행
- 실행이 전부 끝나면 반환값을 넘겨줌
- 프로그램의 제어는 스택에 저장된 돌아갈 반환 주소값으로 이동
- 스택에 저장된 함수 호출 정보를 제거
b. 인라인 함수(inline function)
🌟 호출될 때 일반적인 함수의 호출 과정을 거치지 않고, 함수의 모든 코드를 호출된 자리에 바로 삽입하는 방식의 함수
- 함수를 호출하는 데 걸리는 시간은 절약되나, 함수 호출 과정으로 생기는 여러 이점을 포기
- 코드가 매우 적은 함수만을 인라인 함수로 선언하는 것이 좋음
- 따라서 인라인 함수는 크기가 작으므로, 함수의 원형이 나오는 자리에 함수의 본체까지 함께 정의
- 인라인 함수에서는 재귀 호출 X
c. 인라인 함수의 선언
📘 문법
inline 함수의원형
또는
inline 함수의정의
👉 inline 키워드는 함수의 원형이나 함수의 정의 어느 한 쪽에만 표기해도 되며, 양쪽 다 표기해도 상관 X
d. 인라인 함수의 예제
❔ 예제
#include <iostream>
using namespace std;
inline int Sub(int x, int y)
{
return x - y;
}
inline void Print(int x)
{
cout << "계산 결과는 " << x << "입니다.";
}
int main(void)
{
int num1 = 5, num2 = 3;
int result;
result = Sub(num1, num2);
Print(result);
return 0;
}
✨ 실행결과
계산 결과는 2입니다.
👆 위 예제의 실제 실행 모습
int main(void)
{
int num1 = 5, num2 = 3;
int result;
{
int x = num1, y = num2;
result = x - y;
}
{
int x = result;
cout << "계산 결과는 " << x << "입니다.";
}
return 0;
}
e. 매크로 함수와 인라인 함수
C언어는 C++의 인라인 함수와 비슷한 기능의 매크로 함수를 사용
🌟 #define 선행처리 지시문에 인수로 함수의 정의를 전달해 함수처럼 동작하는 매크로를 만들 수 있음
이런 매크로를 함수같은 매크로 또는 매크로 함수라고 함
💥 Warning!
매크로 함수는 일반 함수와는 달리 단순 치환만을 해주므로, 일반 함수와 똑같은 방식으로 동작하지 않음
이러한 매크로 함수를 일반 함수처럼 사용하기 위해서는 모든 인수를 괄호({})로 감싸야함
❔ 예제 : 메크로 함수의 괄호의 중요성
#include <stdio.h>
#define SQR(X) X*X //따라서 #define SQR(X) ((X)*(X))이렇게 작성해야 제대로 작동됨
int main(void)
{
int result;
int x = 5;
puts(SQR(10));
puts(SQR(x));
puts(SQR(x+3)); // x+3*x+3 이되어 3*x+x+3의 수식이 됨
return 0;
}
✨ 실행결과
100
25
23
💥 하지만 인라인 함수는 단순 치환이 아닌 함수의 모든 코드를 호출된 자리에 인라인 코드로 삽입해 주는 것
👉 따라서 일반 함수처럼 값이나 수식을 인수로 전달할 수 있으며, 매개변수 타입에 맞춘 자동 타입 변환도 지원
❔ 예제 : 매크로 함수의 예제를 인라인 함수로 작성
#include <iostream>
using namespace std;
inline int Sqr(int x)
{
return x * x;
}
int main(void)
{
int result;
int x = 5;
cout << "계산 결과는 " << Sqr(10) << "입니다." << endl;
cout << "계산 결과는 " << Sqr(x) << "입니다." << endl;
cout << "계산 결과는 " << Sqr(x+3) << "입니다.";
return 0;
}
✨ 실행결과
계산 결과는 100입니다.
계산 결과는 25입니다.
계산 결과는 64입니다.
'프로그래밍 언어 > C++' 카테고리의 다른 글
[게임 프로그래머 입문 올인원] 포인터와 배열 : 포인터 (20, 21강) (0) | 2023.09.06 |
---|---|
[게임 프로그래머 입문 올인원] 포인터와 배열 : 배열 기초 (18강) (0) | 2023.09.06 |
[자료형(data type)] C++ 파생형 함수 총정리 1 (0) | 2023.09.06 |
[게임 프로그래머 입문 올인원] 함수와 디버깅 : 파일 분할 (16강) (0) | 2023.09.06 |
[게임 프로그래머 입문 올인원] 함수와 디버깅 : 스택과 레지스터 (13강) (0) | 2023.09.05 |