이번 시간에는 클래스 템플릿에 대해 알아보자
먼저 클래스 템플릿 없이 아래와 같은 코드를 작성한다고 해보자
class RandomBox
{
public:
int GetRandomData()
{
int idx = rand() % 10;
return _data[idx];
}
public:
int _data[10];
};
class RandomBoxFloat
{
public:
float GetRandomData()
{
int idx = rand() % 10;
return _data[idx];
}
public:
float _data[10];
};
int main()
{
srand(static_cast<unsigned int>(time(nullptr))); // rand함수 시드설정
RandomBox rb1;
for (int i = 0; i < 10; i++)
{
rb1._data[i] = i;
}
int value1 = rb1.GetRandomData();
cout << value1 << endl;
RandomBoxFloat rb2;
for (int i = 0; i < 10; i++)
{
rb2._data[i] = i + 0.5f;
}
float value2 = rb2.GetRandomData();
cout << value2 << endl;
}
단순하게 RandomBox라는 클래스를 만들어주고
내부는 거의 비슷하지만 안에서 쓰는 타입이 다른
RandomBoxFloat 클래스를 만들어 주었다
그럼 여기서도
지난 시간에 배운 템플릿의 개념과 방식을 적용하면
굳이 클래스를 두개 만들어 줄 필요가 없지 않을까 싶다
아래를 통해 클래스 템플릿 사용 방법을 알아보자
template <typename T>
class RandomBox
{
public:
T GetRandomData()
{
int idx = rand() % 10;
return _data[idx];
}
public:
T _data[10];
};
int main()
{
srand(static_cast<unsigned int>(time(nullptr))); // rand함수 시드설정
RandomBox<int> rb1;
for (int i = 0; i < 10; i++)
{
rb1._data[i] = i;
}
int value1 = rb1.GetRandomData();
cout << value1 << endl;
RandomBox<float> rb2;
for (int i = 0; i < 10; i++)
{
rb2._data[i] = i + 0.5f;
}
float value2 = rb2.GetRandomData();
cout << value2 << endl;
}
함수 템플릿과 사용 방식은 거의 차이가 없다
지금까지 우리는 template <typename T>
이렇게만 사용했지만
template <int SIZE> 처럼 사용 할 수도 있다
그럼 이제 아래의 코드를 통해 typename 없이 template을 사용하는 방법도 알아보자
template <typename T, int SIZE> // template <typename T, int SIZE = 10> 기본값을 설정할 수도 있다
class RandomBox
{
public:
T GetRandomData()
{
int idx = rand() % SIZE;
return _data[idx];
}
public:
T _data[SIZE];
};
int main()
{
srand(static_cast<unsigned int>(time(nullptr))); // rand함수 시드설정
RandomBox<int, 10> rb1;
for (int i = 0; i < 10; i++)
{
rb1._data[i] = i;
}
int value1 = rb1.GetRandomData();
cout << value1 << endl;
RandomBox<int, 20> rb2;
for (int i = 0; i < 20; i++)
{
rb2._data[i] = i;
}
int value2 = rb2.GetRandomData();
cout << value2 << endl;
}
이처럼 무조건 typename을 붙여야 하는 것은 아니다
template<> 안에 들어가는 것은 골라줘야 하는 목록이라고 볼 수 있다
그런데,
우리가 일반적으로 함수의 인자를 받는 개념이랑
클래스 템플릿 인자를 설정하는 개념과는 차이가 있다
아래 코드에서 제일 마지막 줄을 보면 그 차이를 이해할 수 있다
template <typename T, int SIZE>
class RandomBox
{
public:
T GetRandomData()
{
int idx = rand() % SIZE;
return _data[idx];
}
public:
T _data[SIZE];
};
int main()
{
srand(static_cast<unsigned int>(time(nullptr))); // rand함수 시드설정
RandomBox<int, 10> rb1;
for (int i = 0; i < 10; i++)
{
rb1._data[i] = i;
}
int value1 = rb1.GetRandomData();
cout << value1 << endl;
RandomBox<int, 20> rb2;
for (int i = 0; i < 20; i++)
{
rb2._data[i] = i;
}
int value2 = rb2.GetRandomData();
cout << value2 << endl;
rb1 = rb2; // 오류가 발생한다
}
왜 저기서 오류가 발생할까?
바로 RandomBox<int, 10>의 형태로 만들어진 rb1과
RandomBox<int, 20>의 형태로 만들어진 rb2는 서로 같은 클래스로 인식을 하지 않기 때문이다
비록, 우리가 같은 클래스로 만들어준 것 같지만
결국 그 내부의 형태와 구성은 전혀 다르기 때문에
서로 전혀 관련이 없는 다른 클래스로 인식을 한다는 것이다
따라서, 템플릿 타입이 100% 일치해야 같은 클래스로 인식한다
다음으로는
클래스 템플릿 특수화를 알아보자
기존에 우리가 만들었던 클래스 템플릿에서
double 타입만을 위한 클래스 템플릿을 만들어보자
template <typename T, int SIZE>
class RandomBox
{
public:
T GetRandomData()
{
int idx = rand() % SIZE;
return _data[idx];
}
public:
T _data[SIZE];
};
// 템플릿 특수화
// double만을 위한 버전을 따로 만들어준 것
template <int SIZE>
class RandomBox<double, SIZE> // 이렇게 반드시 원하는 내부를 작성해줘야 함
{
public:
double GetRandomData()
{
int idx = rand() % SIZE;
return _data[idx];
}
public:
double _data[SIZE];
};
int main()
{
srand(static_cast<unsigned int>(time(nullptr)));
RandomBox<int, 10> rb1;
for (int i = 0; i < 10; i++)
{
rb1._data[i] = i;
}
int value1 = rb1.GetRandomData();
cout << value1 << endl;
RandomBox<double, 20> rb2;
for (int i = 0; i < 20; i++)
{
rb2._data[i] = i + 0.5;
}
double value2 = rb2.GetRandomData();
cout << value2 << endl;
}
아무래도 함수 템플릿 특수화보다는
더 작성해야 할 것이 있어 더 익숙해지도록 하자
이렇게 STL을 공부하기 위한 사전지식인
함수 포인터, 함수 객체, 템플릿을 알아보았다
다음 시간에는 마지막으로 콜백 함수에 대해 알아보고
STL로 넘어가도록 하자
'C++ > [루키스] 콜백함수' 카테고리의 다른 글
[STL 사전지식] 6. 콜백 함수 (0) | 2023.01.25 |
---|---|
[STL 사전지식] 4. 템플릿 기초 (1) (0) | 2023.01.25 |
[STL 사전지식] 3. 함수 객체 (0) | 2023.01.23 |
[STL 사전지식] 2. 함수 포인터 (2) (0) | 2023.01.22 |
[STL 사전지식] 1. 함수 포인터 (1) (0) | 2023.01.22 |