inline function
편집다음 소스를 살펴보세요.
4.01 : 짧은 함수 |
---|
#include <iostream>
using namespace std;
int small(int first, int second)
{
if(first > second)
return 1;
else
return 0;
}
int main(void)
{
cout<<small(1, 2)<<endl;
cout<<small(2, 3)<<endl;
cout<<small(3, 2)<<endl;
cout<<small(1, 0)<<endl;
return 0;
}
|
0
0 |
4.01소스의 small함수가 너무 짧다고 느껴지지 않으시나요? 따로 함수로 빼서 쓰기엔 너무 단순합니다. 하지만 매번 반복해서 if-else문을 쓰기는 귀찮아 보입니다. C에서는 이런 문제를 매크로 함수를 이용하여 해결하였습니다.
macro
편집다음은 C스타일로 4.01문제를 해결한 소스입니다.
4.02 : C스타일 매크로 함수 |
---|
#include <iostream>
using namespace std;
#define SMALL(x, y) ((x)>(y) ? 1 : 0)
int main(void)
{
cout<<SMALL(1, 2)<<endl;
cout<<SMALL(2, 3)<<endl;
cout<<SMALL(3, 2)<<endl;
cout<<SMALL(1, 0)<<endl;
return 0;
}
|
0
0 |
매크로 함수로 SMALL을 정의했습니다. 이제 전처리기에 의해 소스의 매크로 함수 호출 부분이 치환됩니다.
T I P |
매크로 함수가 잘 이해 되지 않습니다.
보통 C언어의 학습시 매크로 함수는 최후반부에 위치합니다. 때문에 어떤 학습자는 매크로 함수에 대해서 공부하지 않았을 수도 있습니다. |
예제 4.01번 |
매크로 함수를 복습해 봅시다. SWAP하는 매크로 함수를 정의 보세요. 호출문은 다음과 같습니다.
SWAP(number1, number2, temp); |
하지만 매크로 함수는 복잡한 내용을 작성하기 힘들었고, 때로 어떤 함수는 작성이 불가능할 때가 있었습니다.
4.03 : 매크로 함수가 가능할까? |
---|
#include <iostream>
using namespace std;
void realFunction(int *arr, int i)
{
for(int k=0; k<i; k++) arr[k]=0;
}
|
매우 짧은 함수임은 사실이지만 매크로 함수로 옮길 방도가 마땅히 생각나지 않습니다(만약 옮긴다고 해도,그리 깔끔하지 않을 것 같습니다).
매크로 함수로 옮기기 힘든 함수도 있다! 이것이 매크로 함수의 첫번째 문제입니다.
매크로 함수의 두번째 문제는 자료형 검사를 하지 않는다.입니다. 다음 소스를 보면서 이해해 봅시다.
4.04 : 작동하는 소스 |
---|
#include <iostream>
#define MAKE(x,y) ((x)*(y)-(x))
using namespace std;
int make(int num1, int num2)
{
return num1*num2-num1;
}
int main(void)
{
int input, input2;
cin>>input>>input2;
cout<<MAKE(input, input2)<<endl;
cout<<make(input, input2)<<endl;
return 0;
}
|
위 4.04 소스를 다음과 같이 잘못 썼다고 해봅시다.
4.04 : 잘못된 매크로 |
---|
#include <iostream>
#define MAKE(x,y) ((x)*(y)-(x))
int make(int num1, int num2)
{
return num1*num2-num1;
}
using namespace std;
int main(void)
{
int input, input2;
cin>>input>>input2;
cout<<MAKE(input, &input2)<<endl;
cout<<make(input, &input2)<<endl;
return 0;
}
|
MAKE함수와 make함수의 2번째 값으로 input2이 아니라 input2의 주소값을 입력했습니다. 코딩을 하면서 쉽사리 할 수 있는 실수입니다. 이 실수를 make 함수를 통해 발견할 수는 있지만 MAKE 함수를 통해서는 발견할 수 없습니다. make 함수는 선언될때 make(int num1, int num2)로 명시되어 있기 때문에, int가 아닌 int*값이 전달되면 오류를 표시해줄 것입니다. 하지만 MAKE 함수는 아무 오류도 표시하지 않을 것입니다. 매크로 함수는 자료형을 검사하지 않기 때문입니다.
학습자 여러분이 1000줄, 10000줄이 넘는 긴 소스를 작성한다고 생각해봅시다. 컴파일 오류가 나면 오류가 난 지점으로가 무엇이 문제였는지 확인할 수 있습니다. 하지만 컴파일 오류가 나지 않은채 실행되었는지 제대로 작성하지 않으면 문제가 무엇인지 찾기 어렵습니다.
매크로 함수는 자료형 검사를 하지 않고, 만약 잘못된 값을 대입해도 문제점을 지적해 주지 않는다. 이는 프로그램의 잘못된 점을 찾기 어렵게 한다!
C++에서는 매크로 함수처럼 작동하지만 "복잡한 함수도 사용 가능하고", "자료형 검사를 하는" 기능을 제공해줍니다.
그것이 바로 인라인 함수입니다.
인라인 함수
편집우선 다음 소스를 보자
4.05 : 일반적인 소스 |
---|
#include <iostream>
using namespace std;
void swap(int & a, int & b)
{
int temp=a;
a=b;
b=temp;
}
int main(void)
{
int a, b;
cin>>a>>b;
cout<<"a is "<<a<<", b is "<<b<<endl;
swap(a,b);
cout<<"swap celld. now a is "<<a<<", b is "<<b<<endl;
return 0;
}
|
a is 10, b is 20 swap called. a is 20, b is 10 |
참조자를 통한 swap 함수의 구현과 사용의 예입니다. 자, 우리는 이 swap함수를 C++의 새로운 기능을 통해 매크로 함수처럼 작동하게 만들 것입니다. 다음 소스를 봅시다.
4.06 : 인라인 함수를 이용한 swap | |
---|---|
코드 대치 전 | 코드 대치 후 |
#include <iostream>
using namespace std;
inline void swap(int & n, int & m)
{
int temp=n;
n=m;
m=temp;
}
int main(void)
{
int a, b;
cin>>a>>b;
cout<<"a is "<<a<<", b is "<<b<<endl;
swap(a,b);
cout<<"swap celld. now a is "<<a<<", b is "<<b<<endl;
return 0;
}
|
#include <iostream>
using namespace std;
int main(void)
{
int a, b;
cin>>a>>b;
cout<<"a is "<<a<<", b is "<<b<<endl;
{
int &n=a;
int &m=b;
int temp=n;
n=m;
m=temp;
}
cout<<"swap celld. now a is "<<a<<", b is "<<b<<endl;
return 0;
}
|
a is 10, b is 20 swap called. a is 20, b is 10 |
a is 10, b is 20 swap called. a is 20, b is 10 |
소스 4.06을 살펴보면, swap함수의 선언 앞에 inline이라는 키워드가 써있습니다.
이 inline이 바로 우리의 문제점을 해결해줄 인라인 함수화 키워드입니다.
코드 대치 전을 보면 인라인 함수가 아닌 일반 함수처럼 swap을 호출하는 모습을 보고 있습니다. 이 소스는 컴파일 과정에서 코드 대치 후 소스로 바뀝니다.
주의하세요! |
매크로는 전처리 과정에서 우리가 눈으로 보는 문자열 형태의 소스를 컴파일하기 전에 수정합니다. 매크로를 호출한 부분에 매크로의 정의를 가져다 붙여넣는 방식이지요. 인라인 함수는 컴파일 과정에서 어셈블리 또는 바이너리 코드를 수정합니다. 컴파일하다가 인라인 함수를 만나면 인라인 함수 컴파일한 부분을 가져다 붙여넣는 방식입니다. |
인라인 함수의 역할을 아시겠나요? 인라인 함수는 마치 매크로 함수처럼 소스를 변화시킵니다(주의하세요! 에 말했듯 변화시키는 때는 다르긴 하지만요). 하지만 여전히 '함수'처럼 역할을 하기 때문에 자료형을 검사합니다. 함수와 같이 자료형 검사 한다는 것을 다음 소스를 보면서 더 잘 이해해 봅시다.
4.07 인라인 함수의 자료형 검사 |
---|
#include <iostream>
using namespace std;
int main(void)
{
int a, b;
cin>>a>>b;
cout<<"a is "<<a<<", b is "<<b<<endl;
{
int &n=a;
int &m=&b;
int temp=n;
n=m;
m=temp;
}
cout<<"swap celld. now a is "<<a<<", b is "<<b<<endl;
return 0;
}
|
4.06 소스는 제대로 실행되지 않을 것입니다. int & m에 b가 아니라 b의 주소값을 집어 넣었기 때문입니다. 컴파일러는 자료형 검사를 할때 문제점을 발견할 것이고 오류가 있음을 알릴 것입니다.
인라인 함수는 매크로 함수의 장점을 지니고 있으면서, 일반 함수처럼 정의할 수 있기 때문에 복잡한 함수의 정의와 자료형 검사를 하는 기능입니다.
하지만 인라인 함수에도 여전히 해결하지 못한 문제가 존재합니다.
- 매크로 함수와 마찬가지로 함수의 내용을 직접 가져다 붙여넣기하는 것이기 때문에, 소스의 크기가 커진다.
- 다시 말해서 매크로 함수보단 덜하지만 여전히 짧은 함수만 인라인 함수로 정의하는 것이 적합하다.
- 매크로 함수보다 조금 복잡한 함수는 구성 가능하지만, 여전히 재귀 함수, 가상 함수(나중에 배움), 함수 포인터를 사용하는 함수는 사용할 수 없다.
- 인라인 함수는 전처리 과정에 실제 코드에 삽입된다. 즉, 코드를 실행하기 전에 적용되는 것이다. 따라서 어떤 호출문보다 상위에 정의되어야 하고, 최상단(예를 들어, 헤더 파일 안)에 정의하는 것이 일반적이다.
T I P |
매크로 함수와 인라인 함수에 각각 장단점이 있다고 인식하자. 모든 면에서 인라인 함수가 매크로 함수보다 나은 것은 아닙니다.
가령 매크로 함수는 자료형을 검사하지 않기 때문에 여러가지 자료형에 대해 딱 1번만 정의해도 사용 가능합니다. |
예제 4.02번 |
인라인 함수로 배열의 모든 값을 출력해주는 함수를 만들고 이 함수를 호출할 main문도 작성해 보세요.
인라인 함수의 선언은 다음과 같습니다. inline void printAllRoom(int * arr, int size); |
정리
편집지금까지 배운 내용을 기억해보세요. 복습은 최고의 공부법!
- 소스가 너무 짧고 단순해서 함수로 만들기는 아깝고, 그렇다고 매번 같은 내용을 쓰긴 싫어졌다.
- C에서 매크로 함수를 사용하게 해줬다.
- C++에서는 매크로 함수와 인라인 함수를 쓸 수 있다.
- 인라인 함수는 매크로 함수보다 더 복잡한 함수도 쓸 수 있다.
- 인라인 함수는 자료형 검사를 해준다.
- 인라인 함수는 소스 최상단이나 헤더파일에 정의하는 것이 일반적이다.
- 복잡한 함수를 인라인 함수로 만드는 건 여전히 무리가 있어 보인다.