일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- increment operator
- virtual function
- std::endl
- return by reference
- std::vector
- 더 지니어스 양면포커
- base from member
- std::ostream
- operator overloading
- virtual inheritance
- c++ multi chatting room
- suffix return type
- vector capacity
- constructor
- member function pointer
- new&delete
- pointer to member data
- placement new
- virtual destructor
- conversion constructor
- delete function
- C++
- this call
- dynamic_cast
- vector size
- c++ basic practice
- virtual function table
- diamond inheritance
- std::cout
- discord bot
- Today
- Total
I'm FanJae.
[C++] Function II 본문
※ 본 포스트는 코드누리 C++ Basic 강의 내용을 보고 정리한 포스트입니다.
1. 함수 템플릿(Function template)
1-1. 함수 오버로딩의 단점
함수 오버로딩(Function overloading) - 관련 내용 링크
- 인자의 형태(타입, 개수)가 다르면 동일 이름의 함수를 여러개 만들 수 있다.
Ex) square(int), square(dobule)
※ square 함수를 만들 때, 인자 타입과 반환 타입만 다르고 구현이 동일(유사)한 함수를 여러개 만들어야 한다.
1-2. 함수 템플릿(Function template)의 정의
- C++ 언어의 해결책은 구현이 동일(유사)한 함수가 여러 개 필요하면 함수를 만들지 않는다.
- 대신, 함수를 생성하는 틀(템플릿)을 만들자는 것이다.
// template<typename T> 이것도 많이 사용된다.
template<class T>
T square(T a)
{
return a * a;
}
int main()
{
square<int>(3);
square<double>(3.4);
}
- 위와 같이 사용하면, 타입에 상관없이 동일한 형태의 함수를 생성해 준다.
- 이 코드는 실질적으로 아래와 같이 변환된다.
1-2-1. Template instantiation(템플릿 인스턴스화)
int square(int a)
{
return a*a;
}
double square(double a)
{
return a*a;
}
- Template(틀) 을 사용해서 컴파일러가 실제 함수를 생성하는 과정이다.
- main()에서 square에 대해서 int, double 형을 사용했기 때문에 위와 같이 2개의 실제 함수를 생성한 것이다.
- 단순히 template을 생성하는 것만으로는 코드가 생성되지 않는다.
- 어셈블리 레벨에서 확인할때, main() 밖에 없다.
※ 즉, 함수 템플릿을 만들고 사용하지 않으면, 인스턴스화(Instantiation)되지 않는다.
- main()에서 square<int>(3);과 같이 호출할 때, 함수가 생성됨을 확인할 수 있다.
1-2-2. 함수 템플릿을 사용하는 방법
① 템플릿 인자(타입)을 명시적으로 전달
square<int>(3);
- 사용자가 전달한 타입으로 함수를 생성한다.
② 템플릿 인자(타입)을 생략
square(3)
- 컴파일러가 함수 인자를 보고 타입을 추론한다.
③ 코드 폭발(Code Bloat)
- 템플릿이 너무 많은 타입에 대해 인스턴스화(Instantiation) 되어서 코드 메모리가 증가하는 현상
char c = 3;
short s = 3;
int n = 3;
/* 주석 친 것과 같이 호출하면, 총 3개 타입에 대한 인스턴스화가 일어난다.
square(c);
square(s);
square(n);
*/
- 위 예제는 총 3개 타입에 대한 인스턴스화가 일어난다. 이에 따라서 코드 메모리가 증가할 수 있다.
- 이런 부분의 경우는 아래와 같이 수정할 수 있다.
char c = 3;
short s = 3;
int n = 3;
square<int>(c);
square<int>(s);
square<int>(n);
- 이와 같이 명시하여 1개의 타입에 대한 인스턴스화를 할 수 있다.
④ 함수 템플릿을 만드는 방법
//template <typename T>
template<class T>
T square(T a)
{
return a * a;
}
auto square(auto a) // C++ 20
{
return a * a;
}
- C++20 부터는 auto를 이용한 방법도 사용이 가능하다.
※ 함수와 함수 템플릿은 별개의 개념이다. square는 함수 템플릿이다. 따라서, square의 주소는 구할 수 없다.
하지만, square <타입>은 구할 수 있다.
printf("%p\n",&square); // X
printf("%p\n",square<int>); // O
2. 인라인 함수(inline Function)
int Add1(int a, int b)
{
return a + b;
}
inline int Add2(int a, int b)
{
return a + b;
}
int main()
{
int ret1 = Add1(1, 2);
int ret2 = Add2(1, 2);
}
- 일반 함수는 호출시 함수 인자를 약속된 장소에 넣는다. 이후, 함수로 이동한다.
- 이에 반면 인라인 함수는 함수 호출 코드를 함수의 기계어 코드로 바로 치환해 달라는 의미이다.
즉, 함수 본문이 삽입된다.
2-1. 어셈블리어 코드를 통한 분석
Compile Explorer에서 /Ob1 명령어를 입력하면, 인라인 치환이 적용된다.
- Add2는 함수를 생성하지 않았고, 목적 코드가 줄어든 것을 확인할 수 있다.
※ 인라인 함수는 빠르다는 장점이 있지만, 크기가 큰 함수를 여러번 치환하면 목적코드(실행파일)이 커질 수 있다. 단, 크기가 작은 함수는 인라인 치환시 목적코드를 줄이기도 한다.
※ 위 처럼, 함수 포인터와 같이 Add2의 주소를 받아오면, Add2를 생성하는 것을 확인할 수 있다.
단, 이것이 인라인 함수 치환 여부에 영향을 주지는 않는다.
3. Linkage
// mylib.h
int Add1(int a, int b);
inline int Add2(int a, int b);
template <class T>
T Add3(T a, T b);
// mylib.cpp
int Add1(int a, int b)
{
return a+b;
}
inline int Add2(int a, int b)
{
return a+b;
}
template <class T>
T Add3(T a, T b)
{
return a+b;
}
// using_mylib.cpp
#include "mylib.h"
int main()
{
Add1(1,2);
Add2(1,2);
Add3(1,2);
}
- 위 예제에서 인라인 함수나 템플릿 함수 호출시 link Error가 발생한다.
3-1. 이러한 문제가 발생하는 이유
- 컴파일러는 모든 소스파일을 동시에 컴파일하지 않는다.
- 파일별로 분리해서 컴파일한다.
// using_mylib.cpp
#include "mylib.h"
int main()
{
Add1(1,2); // call "Add1 주소"
Add2(1,2);
Add3(1,2);
}
- call "Add1 주소" -> 컴파일 할 때는 Add1의 주소를 알 수 없다. 하지만 이는 링커가 이를 채워줄 것이다.
- 하지만, Add2 를 컴파일 할때, 호출 위치에 인라인 함수의 본문을 삽입해야 한다.
- 이러한 문제는 Add3를 컴파일 할때 템플릿 인스턴스화를 진행하면서 유사한 문제가 발생한다.
※ 즉, 함수 구현 자체를 헤더 파일에 넣어야 한다.
3-2. Internal linkage & External linkage
Internal linkage : 심볼(함수, 변수이름)이 선언된 같은 컴파일 단위(파일) 에서만 사용 가능한 경우
Ex. 인라인 함수, 템플릿, static 키워드 등
External linkage : 프로젝트 내 모든 컴파일 단위에서 사용 가능한 경우
Ex. 일반 함수, 전역 변수 등
※ 개인적으로는 Linkage에 대한 개념을 익히는데 매우 고전했다. 컴파일 단위라는 것에 대한 개념 이해가 부족했고, Linkage를 학습하면서 다시 제대로 이 개념을 익히고 갔다.
'C++ > Basic' 카테고리의 다른 글
[C++] Function III (0) | 2024.08.08 |
---|---|
[C++] Function III (0) | 2024.08.08 |
[C++] Function I (0) | 2024.08.07 |
[C++] C++ 에서 새롭게 추가된 Type & Variable (0) | 2024.08.06 |
[C++] 표준 입출력 정리 (0) | 2024.08.06 |