프로그래밍 언어/C++

[자료형(data type)] C++ 파생형 함수 총정리 2

순정법사 2023.09.06
더보기
자료형 중 파생형 함수 공부 / https://ddanzimind.tistory.com/32

 

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. 오버로딩된 함수 호출
  2. 같은 시그니처를 가지는 함수의 원형을 찾아 호출

💥 오버로딩한 함수는 모호한 호출(호출 시 여러개의 함수에 걸쳐지는) 을 허용하지 않고 오류를 발생시킴

 

😀 함수의 원형 예제

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++에서의 함수 호출 과정

  1. 함수 호출
  2. 스택에 함수로 전달할 매개변수와 함께 호출이 끝난 뒤 돌아갈 반환 주소값을 저장
  3. 프로그램의 제어가 함수의 위치로 넘어와 함수 내에 선언된 지역 변수도 스택에 저장
  4. 함수의 모든 코드를 실행
  5. 실행이 전부 끝나면 반환값을 넘겨줌
  6. 프로그램의 제어는 스택에 저장된 돌아갈 반환 주소값으로 이동
  7. 스택에 저장된 함수 호출 정보를 제거

 

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입니다.

 

 

 


출처 : http://www.tcpschool.com/cpp/cpp_cppFunction_reference

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com