C++은 왜 효율적인가!
1. 디폴트 매개변수
이름에서 알 수 있듯이 기본적으로 미리 매개변수의 값을 정해주는 것입니다.
아래 param에 10을 디폴트로 해준다는 뜻입니다.
int Func(int param = 10)
{
return param;
}
이 함수를 호출 하는 방법은 두 가지가 있습니다.
매개변수가 있어서 생략해도 디폴트값으로 매개변수 값이 정해집니다.
< 결과 >
1. return 10
2. return 1
Func();
Func(1);
편리하긴 편리하지만 모호성이 있습니다.
그래서 C++은 호출자의 코드만 보고 절대로 함수 원형을 확정지어서는 안됩니다!!
그렇다면 매개변수가 두 개일 때도 될까요? 물론 됩니다. 하지만 신경써야할게 더 생기죠!
1. 디폴트 매개변수는 무조건 오른쪽 매개변수부터 기술한다.
2. 절대로 매개변수의 중간을 빼먹으면 안된다! (디폴트 매개변수는 오른쪽부터 연속으로만 가능)
3. 짝 맞추기
그 중 1, 2 두 가지 경우에 대해 직접 보도록 합시다.
1 - 1 무조건 오른쪽부터 기술해야한다.
아래를 보면 첫 번째부터 디폴트인 경우인데 이럴 경우 오류가 납니다.
무조건 int param1, int param2 = 10 이렇게 오른쪽부터 정해줘야 합니다.
int Func(int param1 = 10, int param2)
{
return param1 + param2;
}
1 - 2 절대로 매개변수의 중간을 빼먹으면 안된다!
중간 param2라는 매개변수만 디폴트를 안해주었는데 이렇게 중간이 빵꾸가 나면 오류가 납니다.
int Func(int param1 = 10, int param2, int param3 = 10)
{
return param1 + param2;
}
그래서 디폴트 매개변수는 매개변수 개수가 늘수록 고려해야하는 것도 많아지고 모호성이 있어서 가급적 자제하는 것이 좋습니다.
하지만 코드의 유지보수를 위해서 사용하면 좋을 때도 있습니다.
아래의 코드를 과거에 썼고
int Func(int param1, int param2)
{
return param1 + param2;
}
이렇게 미래의 내가 수정했다고 한다면 디폴트 매개변수를 이용해서
int Func(int param1, int param2, int typeParam = 10)
{
return param1 + param2 * typeParam;
}
과거에 Func(1, 2); 라고 호출했었으니 마지막 추가한 매개변수는 디폴트니 생략해도 오류가 나지 않습니다.
과거의 코드에서 오류가 나지 않게 함수를 유지보수 할 수 있습니다.
2. 함수 다중 정의 (오버로딩)
함수 다중 정의란 이름이 같은 함수를 다양한 모습으로 여러개 정의하는 것입니다.
또 다른 말로 '오버로딩'이라고도 합니다.
함수 이름은 똑같고 매개변수 구성이 달라지면 다른 함수로 인식합니다.
이를 통해 C++은 다형성이라는 특성을 갖을 수 있습니다.
2 - 1 다중 정의 일반
int Add(int a, int b)
{
return a + b;
}
Add(1, 2);
이 함수는 a와 b를 더하는 간단한 함수인데 한계점이 int 자료형만 된다는 것입니다.
이럴 때 오버로딩으로 함수를 다중 정의해준다면 한 가지 개념을 여러 가지 형태로 구현할 수 있습니다.
float Add(float a, float b)
{
return a + b;
}
Add(1.2, 2.3);
하지만 디폴트 매개변수처럼 고려해야 할 요소가 있습니다.
1. 반환형식만 다른 경우
int Add(int a, int b);
float Add(int a, int b);
2. 호출 규칙만 다른 경우
int _funcAdd1 Add(int a, int b);
int _fubcAdd2 Add(int a, int b);
이 두 가지는 다중 정의가 되지 않습니다.
다중 정의, 즉 오버로딩으로 짠다면 코드가 확장성과 편의성을 얻을 수 있습니다.
하지만 여기서 디폴트 매개변수와 다중 정의가 함께 쓰인다면 엄청난 모호성이 발생할 수 있습니다.
코드로 보시면 이해가 되실 겁니다.
void Func(int a)
{
return a;
}
void Func(int a, int b = 10)
{
return a + b;
}
int main()
{
Func(1);
}
이때 과연 컴퓨터는 어떤 것을 실행해야할까요?
컴퓨터도 잘 모르겠다고 이럴 경우에는 호출이 모호 하다고 말합니다.
그러니 편의성과 확장성이 있어도 모호성이 있으니 정확히 써야할 곳에만 적절히 써야합니다.
2 - 2 함수 템플릿
위에 함수 다중 정의에는 문제점이 또 있습니다.
바로 호출하는 사람을 편하게 하자고 함수 제작자는 함수 만드는 것을 여러 번 반복해야합니다.
뿐만 아니라, 더 큰 문제로 같은 일을 하는 함수가 여러개로 존재 한다는 점입니다.
다중 정의로 함수를 3개 만들었다고 하고 함수에 오류가 났다는 것을 깨닫게 되면 제작자는 함수 3개를 수정해야합니다.
또한 편의성과 확장성을 위해 만들었지만 사용하지 않으면 불필요한 코드가 생겨 메모리만 소모하게 되어 비효율적이죠.
그래서 이 함수 템플릿(T)을 씁니다.
아래처럼 사용하면 되는데 typename은 자료형을 의미합니다.
템플릿처럼 일종의 틀로 형식을 지정하지 않아도 쉽게 코드를 확장할 수 있습니다.
template <typename T>
T Add(T a, T b)
{
return a + b;
}
int main()
{
Add<int>(1, 2);
}
3. 인라인 함수
함수를 호출하면 스택 메모리에 쌓입니다. 매개변수 때문에 메모리 복사가 일어납니다.
이처럼 눈에 보이지는 않지만 내부적으로 여러 연산들이 일어납니다.
보통은 이런 문제들을 해결하기 위해 매크로를 사용하곤 합니다. 하지만 매크로는 함수가 아닙니다. 그리고 매개변수에 형식을 지정할 수 없다는 단점도 있습니다.
그래서 만들어진 것이 이 인라인 함수입니다.
매크로의 장점과 함수의 장점, 두 마리 토끼 모두 잡은 함수입니다.
기본적으로 함수이기 때문에 매크로의 단점인 매개변수 형식 지정도 할 수 있고 내부를 보면 매크로처럼 함수 호출도 하지 않아 성능이 매우 향상되고 좋은 함수입니다.
'그렇다면 다 인라인 함수를 쓰지 왜 일반 함수를 사용하는거지?' 라는 의문이 생깁니다.
인라인 함수를 쓰기에는 적합하지 않는 긴 코드를 갖고있는 함수들은 바람직 하지 않기 때문입니다.
그렇다면 다시 또 의문이 생깁니다. 긴 코드라는 것은 어떤 기준일까요?
그것은 컴파일러가 결정하는데요.
프로젝트 속성에 들어가서 C/C++에 최적화에 가면 인라인 함수 확장이란 것이 있습니다.
디폴트는 기본값입니다.
Visual Studio에서는 기본값으로 설정되어 있으면
인라인 함수에 적합한 길이의 코드는 인라인 함수로 알아서 바꿔서 컴파일합니다.
또한 인라인 함수를 사용했지만 적합하지 않는 함수도 알아서 일반 함수로 바꿔서 컴파일한다고 합니다.
오늘은 개발자의 노력에 대해 배운 것 같습니다.
함수를 만들 때 미래를 위해서 이 함수를 사용하는 사람을 위해서 더욱 편리하게 확장성있게 코드를 짜야합니다.
그리고 성능을 위해 최적화에 관련된 내용도 더 찾아봐야 할 거 같습니다.
C++개념 바로잡기는 '이것이 C++이다' 책을 공부한 후 정리하며 쓴 클라이언트 개발 지망생의 공부 노트입니다.

'컴퓨터 언어 > C++' 카테고리의 다른 글
[C++이란?/4일차] C++ 개념 바로잡기 (0) | 2023.02.06 |
---|---|
[C++이란?/3일차] C++ 개념 바로잡기 (0) | 2023.01.30 |
[C++이란?/1일차] C++ 개념 바로잡기 (0) | 2023.01.25 |
[동계 캠프/3일차] AI Sarsa & Q_Learning (0) | 2023.01.11 |
[동계 캠프/2일차] 동적 프로그래밍으로 AI 학습 (0) | 2023.01.10 |
댓글