본문 바로가기
C++/뇌를 자극하는 C++

[C++] 챕터 15 - 함수 2 : 함수의 모든 것

by Minkyu Lee 2023. 6. 14.

// 15-1 : 디폴트 인자의 사용

/*
책 내용이 좋으나 예제코드가 없어 여기에 적는다.
// 오버로딩
여러 함수들이 동일한 이름 사용할 수 있는 기능이다.
int max(int a, int b);
float max(float a, float b);
위를 보면 이름은 같으나, 반환값과 인자 타입이 다르다.

뭐가 좋은가?
타입이 바뀌더라도 max 함수 호출 코드는 바뀌지 않아서 좋다.

//기본적인 오버로딩 규칙
- 여러개 있을때 판별기준은 무엇인가?
컴퓨터가 인자의 타입을 확인해 가장 잘 어울리는 시그너치를 가진 함수를 호출한다.

- 반환값만 틀린 경우 오버로드 불가하다.
int Var(char c, int i);
double Var(char c, int i);
위 코드는 반환은 다르나, 시그니처 동일하다.
판단이 안되어서 오버로드 할 수 없다.

- 시그니처가 달라도 오버로드 불가한 경우.
void Same(int i);
void Same(int& r);
int i = 10;
Same(i);
위 두 함수 모두 이 방식으로 호출할 수 있어 컴퓨터가 판단이 안되어 오버로드 할 수 없다.

*/

bool SetFramesPerSec(int fps = 10)
{
    // 코드 생략 (책에서 생략함)
    return true;
}

int main()
{
    SetFramesPerSec(); // 인자 없이 호출

    return 0;
}

/*
--- 결과 ---

---
디폴트 인자 사용.
사용이 간단하다. 함수 원형에 매개변수 이름 뒤 디폴트 값 적으면 된다.

단, 몇가지 제한이 있다.
모든 디폴트 인자는 오른쪽 끝에 모여있어야한다.

*/

 

// 15-2 : 무작정 자기 자신 호출하는 함수

void RecursiveCall()
{
    RecursiveCall();
}

int main()
{
    RecursiveCall();

    return 0;
}

/*
--- 결과 ---

---
재귀호출.
함수가 자기 자신을 호출한다.
수학적인 알고리즘에서 잘 등장하는 중요한 개념이다.
하지만 이 예제 잘못되었다. 무한히 반복하기 때문이다.
언제 끝나게 할 것인가?가 중요하다.
*/

 

// 15-3 : 재귀 호출 끝내기

#include <iostream>
using namespace std;

void WhilePositive(int n)
{
    cout << n << "\n";

    // 인자가 0보다 작으면 끝내기
    if (n < 0)
        return;
   
    // 호출마다 -1 감소가 일어난다.
    WhilePositive(n - 1);
    cout << n << "\n";
}

int main()
{
    WhilePositive(10);

    return 0;
}

/*
--- 결과 ---
10
9
8
7
6
5
4
3
2
1
0
-1
0
1
2
3
4
5
6
7
8
9
10

---

*/

 

// 15-4 : 재귀 호출을 이용한 2진수로의 변환

#include <iostream>
using namespace std;

void Convert2Bin(int dec)
{
    if (dec <= 0)
        return;

    Convert2Bin(dec / 2);
    cout << dec % 2;
}

int main()
{
    Convert2Bin(13);
    cout << "\n";

    return 0;
}

/*
--- 결과 ---
1101

---
재귀 호출 이용한 2진수로 변환.
10진수를 2진수로 바꾸었다.

현재 재귀함수는 정보가 점점 작아진다.
또한 결과들을 거꾸로 모은다.
이 특징을 이용하면 유용하다.

*/

 

// 15-5 : 함수의 포인터 기본

#include <iostream>
using namespace std;

// 원형이 동일한 두 개의 함수 정의.
void Dog()
{
    cout << "Dog.\n";
}

void Cat()
{
    cout << "Cat.\n";
}

int main()
{
    void(*p)(); // 함수의 포인터 정의
   
    p = &Dog; // 변수에 대한 포인터 사용과 크게 다르지 않다.
    (*p)();

    p = &Cat;
    (*p)();

    return 0;
}

/*
--- 결과 ---
Dog.
Cat.

---
함수의 포인터 기본.

포인터는 함수도 가리킬 수 있다.
이 포인터 사용하여 함수 호출 가능하다.
함수도 주소가 있다.
모든 정보는 메모리에 존재한다.

괄호고 *p 감싼 것은 우선순위 때문이다.

// typedef를 사용
함수의 포인터 정의를 간단하게 한다.

typedef를 사용하면 함수의 포인터 타입에 대한 별명을 만들 수 있다.

typedef void (*FN_TYPE2)(int);
위처럼 사용하면, 포인터 타입의 이름이 FN_TYPE2가 된다.

예를 들면,
int AnyName(int a, int b); 라면
typedef int (*FN_TYPE1) (int, int); 이다.

*/

 

// 15-6 : 자기 스스로 환경을 비교하는 경우

#include <iostream>
using namespace std;

void ForWindows9x();
void ForWindowsNT();
bool IsWindows9x();
void ImportantFunc();

int main()
{
    ImportantFunc();

    return 0;
}

void ForWindows9x()
{
    cout << "9x\n";
}

void ForWindowsNT()
{
    cout << "NT\n";
}

bool IsWindows9x()
{
    return false;
}

void ImportantFunc()
{
    // 현재 환경 검사 후 호출
    if (IsWindows9x())
        ForWindows9x();
    else
        ForWindowsNT();

    // 생략

    // 다시 호출
    if (IsWindows9x())
        ForWindows9x();
    else
        ForWindowsNT();
}

/*
--- 결과 ---
NT
NT

---
함수의 포인터를 다른 곳에 넘기는 방법 알아본다.
이 함수 필요할 때 호출하라고 넘겨주는 것이다.

A는 어떤 인자를 사용해 C 호출 모른다.
하지만 B는 알고 있다. 이럴 때 C를 넘긴다.

넘기지 않으면, 매번 실행 환경 검사 해서 함수를 호출해야한다.
그것이 예제 코드 내용이다.

*/

 

// 15-7 : 함수의 포인터를 사용한 개선

#include <iostream>
using namespace std;

// 9x나 NT 함수를 가리킬 수 있는 별명을 만든다.
typedef void (*SYSTEM_FUNC)();

void ForWindows9x();
void ForWindowsNT();
bool IsWindows9x();
void ImportantFunc(SYSTEM_FUNC pfnSyst);

int main()
{
    SYSTEM_FUNC pfn;

    if (IsWindows9x())
        pfn = &ForWindows9x;
    else
        pfn = &ForWindowsNT;

    ImportantFunc(pfn);

    return 0;
}

void ForWindows9x()
{
    cout << "9x\n";
}

void ForWindowsNT()
{
    cout << "NT\n";
}

bool IsWindows9x()
{
    return false;
}

void ImportantFunc(SYSTEM_FUNC pfnSyst)
{
    (*pfnSyst)(); // 시스템 버전 감사가 없어졌다. 이미 호출해야 될 함수를 가리키고 있기 때문이다.

    // 생략

    (*pfnSyst)();
}

/*
--- 결과 ---
NT
NT

---
함수의 포인터를 사용한 개선.

함수와의 관계 파악 중요하다.
포인터를 주고, 필요할 때 호출하였다.

*/

댓글