프로그래밍을 하다보면, 함수든 변수든 자료형을 특정하여 선언해야하는 경우가 대부분이다.
그렇기 때문에, 아래와 같이 다른 자료형을 다루지만 같은 의미를 가진 변수나,
다른 자료형을 다루지만 같은 기능을 가진 함수를 선언해야하는 일이 잦다.
#include<iostream>
int GetMax(int x, int y)
{
if (x > y)return x;
else return y;
};
float GetMax(float x, float y)
{
if (x > y)return x;
else return y;
}
//double GetMax...
이러한 불편함을 피하기 위해서 C++에서는 템플릿이라는 기능이 있다.
템플릿에는 크게 함수 템플릿, 클래스 템플릿이 있다.
템플릿에 사용예는 아래와 같다.
#include<iostream>
template <typename T>
T GetMax(T x, T y)
{
if (x > y)return x;
else return y;
}
위 함수와 처음 나왔던 함수의 기능은 같다.
맨 위의 GetMax는 각기 다른 자료형을 다루기 위해 자료형마다 함수를 선언했어야 했지만,
템플릿 기능을 이용하면 자동으로 매개변수의 자료형을 판독하여 그 매개변수의 자료형을 토대로
함수의 기능을 수행한다.
그렇기 때문에 위 함수는 자료형마다 하나씩 선언하지 않아도, 모든 자료형을 매개로 받아
함수의 기능을 수행할 수 있다.
#include<iostream>
template <typename T>
T GetMax(T x, T y)
{
if (x > y)return x;
else return y;
}
int main()
{
GetMax(1.5, 5);
// Error
}
그런데 위의 메인함수에서 실행된 GetMax함수를 사용하려고 했더니 에러가 생겼다.
분명 알아서 자료형을 판독해서 함수를 실행시켜준다고 했는데, 왜 작동하지 않을까?
그 이유는, 템플릿을 선언함에 있어, typename을 하나밖에 설정하지 않았기 때문이다.
그렇기 때문에 아래와 같이 typename의 갯수를 더 선언하여 사용할 자료형의 수를 늘려줄 수 있다.
#include<iostream>
template <typename T1, typename T2>
T GetMax(T1 x, T2 y)
{
if (x > y)return x;
else return y;
}
int main()
{
std::cout << GetMax(1.5, 5) << ", ";
std::cout << GetMax(1.4, 3.2);
// 출력값 : 5, 4.6
}
그러나 typename을 2개를 썼다고 꼭 2개를 사용해야하는 것은 아니다.
가용 자료형의 갯수가 2개로 정해질 뿐, 1개의 자료형만 사용해도 가능하고 최대 2개까지 사용할 수 있는 것이다.
위의 예가 앞서 첫번째로 소개한 함수 템플릿이다. 아래부터는 클래스 템플릿에 대해 알아보자.
#include<iostream>
template <typename T1, typename T2>
class Box
{
public:
T1 GetFirst() { return firstData; }
T2 GetSecond() { return secondData; }
void SetFirst(T1 val);
void SetSecond(T2 val);
private:
T1 firstData;
T2 secondData;
}
int main()
{
Box<int, float> box;
box.Setfirst(5);
box.SetSecond(3.14);
std::cout << GetFirst() << ", ";
std::cout << GetSecond();
Box<int, int> box2; // 정상
Box<int> box3; // Error
// 출력값 : 5, 3.14
}
template <typename T1, typename T2>
T1 Box<T1, T2>::SetFirst(T1 val) { firstData = val; }
template <typename T1, typename T2>
T1 Box<T1, T2>::SetSecond(T2 val) { secondData = val; }
클래스 템플릿이던 함수 템플릿이던 선언의 방법은 동일하다.
사용할 자료형의 수를 typename을 통해 정한 후, 클래스의 내용을 채우면 된다.
또한, 템플릿을 사용한 클래스 역시 클래스이기 때문에 멤버함수를 선언하고 정의하게 되는데,
이 때 멤버함수를 클래스 외부에서 정의할 때 유의해야 할 것은,
범위지정연산자를 사용하여 꼭 어느 클래스의 멤버함수인지를 꼭 컴퓨터에게 알려줘야 한다.
클래스의 멤버함수를 클래스 외부에서 정의할 때의 유의사항을 19. 클래스 포스팅에서 다루지 않았기 때문에
위와같이 해당 내용을 코드에 담았다.
그 후, 사용할 때는 조금 다르다.
템플릿 함수를 호출할 때는 기존의 함수를 호출할 때와 같았는데,
템플릿 클래스를 선언할 때는, 클래스를 고른 후,
<>꺾새 안에 해당 템플릿으로 만들어진 클래스에 필요한 자료형을 같이 넣어주어야 한다.
위의 Box 클래스에서 사용한 템플릿은 총 2개의 자료형을 사용할 것이라고 명시하였기 때문에,
꼭 두개의 자료형을 꺾새 안에 넣어주어야 한다.
만약 2개의 자료형을 선언한 템플릿을 사용한 클래스로 객체를 만들려고 할 때,
내가 필요한 객체는 자료형을 하나밖에 사용하지 않더라도, 위의 box2처럼 int자료형을 두번 입력해주어야 한다.
그렇지 않고 box3처럼 자료형을 하나만 입력하면 붉은줄이 가게되어 오류가 나타난다.
이것은 서식에 관한 글이라 짧게 짚고 넘어가자면, 특정 템플릿을 이용한 함수나 클래스를 선언할 때는
꼭 바로 위에 템플릿을 적어주는 것이 가독성이 좋고, 사용함에 있어서 혼동이 없이 사용할 수 있다.
앞서 클래스를 다룰 때는 붕어빵 틀이라는 예시를 든 적이 있다.
하지만 지금 생각해보니 붕어빵 틀 이라기 보다는, 클래스는 붕어빵 가게의 매뉴얼인 것 같다.
붕어빵을 너무 맛있게 만들어서 프랜차이즈가 된 어떤 붕어빵 집은, 분점을 내줄 때 마다 매뉴얼을 따르게 하는 것이다.
모두 같은 붕어빵 모양의 틀, 같은 재료, 같은 반죽 등을 사용해야 하는 것 처럼 말이다.
물론 분점마다 모두 똑같지는 않을 것이다.
소재하고있는 위치는 물론이고, 붕어빵틀의 제조사, 팥의 원산지 혹은 비율,
반죽에 들어가는 밀가루, 붕어빵을 굽는 기술등은 분점마다 다를 것이다.
클래스를 사용하여 만들어낸 객체들이 모두 똑같지는 않은 것 처럼 말이다.
왜 앞서 잘 설명한 클래스의 예시를 바꾸는지 묻는다면,
오히려 붕어빵 틀의 비유가 클래스보단 템플릿에 더 어울리는 것 같기 때문이라고 답 할 수 있겠다.
붕어빵은 틀이 같으면 속의 내용이 무엇이 들었던 간에 모두 같은 모양이지 않은가?
템플릿은 붕어빵 틀로, typename이 붕어빵에 들어가는 재료를 정해주는 것이라고 이해가 되었다.
붕어빵 틀은 붕어빵을 굽는 기능을 한다.
여기서 템플릿을 사용하지 않는다면 팥 붕어빵을 만드려고 하면 팥 붕어빵틀을 따로 만들고,
슈크림 붕어빵을 만드려고 하면 슈크림 붕어빵틀을 따로 만드는 모양이 되는 것이다.
이러한 번거로움을 없애기 위해,
같은 붕어빵틀에 재료만 다르게 넣어도 서로 다른 붕어빵들이 만들어지는 것이 템플릿의 역할인 것 같다.
'C++ 일기' 카테고리의 다른 글
| 21. STL 컨테이너 vector (0) | 2024.02.29 |
|---|---|
| 19. 클래스(Class) (2) | 2024.02.28 |
| 18. 객체지향(OOP)과 절차지향(PP)프로그래밍 (2) | 2024.02.28 |
| 17. 네임스페이스 (namespace) (0) | 2024.02.28 |
| 16. Call by Reference, Call by Value, Call by Reference (0) | 2024.02.27 |