CPP Module 07
CPP Module 07
과제를 통해 배울 수 있는 것
이번 과제에서는 C++의 템플릿에 대해 학습한다. 템플릿을 이용한 여러 함수, 반복자나 배열을 만든다.
template
내가 맨 처음 이 과제를 평가하러 갔을 때 템플릿의 첫인상이 딱 제네릭 타입이었다.
템플릿은 C++에서 제네릭 프로그래밍의 기초를 담당하고 있다. 제네릭 프로그래밍은 데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입을 가질 수 있게 하는 것이다.
즉 템플릿인 T
라는 타입이 있다고 하면 이 T
에는 int
든, char
든, double
이든 어떠한 자료형이든 들어갈 수 있다.
Exercise 00: Start with a few
functions
이 exercise에서는 swap
, min
, max
함수를 구현해야 하며, 인자로는 두 가지 변수를 받는다. 이 변수들은 타입이 정해져 있지 않다.
서브젝트를 보면 템플릿은 헤더 파일에 정의되어야 한다고 명시되어 있기 때문에, 해당 함수들을 헤더 파일에 정의해주면 그만이다.
template <typename T>
void swap(T &a, T &b) {
T tmp = a;
a = b;
b = tmp;
}
template <typename T>
T min(T &a, T &b) {
return (a < b ? a : b);
}
template <typename T>
T max(T &a, T &b) {
return (a > b ? a : b);
}
template
다음에 꺽쇠 괄호 내부에 템플릿의 별칭을 T
라고 지정해주고, 각 함수의 매개 변수들의 자료형으로 지정해주었다. 그러면 a
나 b
가 int
든 어떠한 자료형으로도 전달될 수 있음을 의미한다.
Exercise 01: Iter
01번 과제는 iter
라는 이름의 함수를 구현하는 것이다. 이 함수는 배열의 주소, 배열의 길이, 배열의 각 요소에 적용할 함수를 인자로 받는다.
이 iter
함수의 어디에 템플릿 타입을 전달받을 수 있을까? 바로 배열의 타입이다. 배열이 int[]
일지, char[]
일지를 모르니까.
template<typename T>
void iter(T *arr, int len, void (*func)(T const &)) {
for (int i = 0; i < len; i++) {
func(arr[i]);
}
}
Exercise 02: Array
앞의 과제들은 비교적 쉬운 편이었는데, 이번에는 조금 구현할 게 많다.
이번엔 템플릿 클래스 Array
를 구현해야 한다. Array
에는 T
타입의 요소를 가지고 있으며, 서브젝트에 제시된 부분을 전부 구현해야 한다. 내용들을 살펴보면 대부분 기본적으로 배열의 동작을 구현하라는 것이다.
template <typename T>
class Array {
public:
Array();
Array(unsigned int n);
Array(const Array<T> &other);
Array<T> &operator=(const Array<T> &other);
~Array();
T &operator[](unsigned int idx) const;
unsigned int size() const;
private:
T *_data;
unsigned int _size;
};
template <typename T>
Array<T>::Array() {
_data = new T[0];
_size = 0;
}
template <typename T>
Array<T>::Array(unsigned int n) {
_data = new T[n];
_size = n;
}
template <typename T>
Array<T>::Array(const Array<T> &other) {
_data = new T[other._size];
_size = other._size;
for (unsigned i = 0; i < _size; i++) {
_data[i] = other._data[i];
}
}
template <typename T>
Array<T> &Array<T>::operator=(const Array<T> &other) {
if (this != &other) {
delete[] _data;
_data = new T[other._size];
_size = other._size;
for (unsigned int i = 0; i < _size; i++) {
_data[i] = other._data[i];
}
}
return *this;
}
template <typename T>
Array<T>::~Array() {
delete[] _data;
}
template <typename T>
T &Array<T>::operator[](unsigned int idx) const {
if (idx >= _size) {
throw std::exception();
}
return _data[idx];
}
template <typename T>
unsigned int Array<T>::size() const {
return _size;
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const Array<T> &arr) {
os << "[";
for (unsigned int i = 0; i < arr.size(); i++) {
os << arr[i];
if (i != arr.size() - 1) {
os << ", ";
}
}
os << "]";
return os;
}
번외. 제네릭 VS 템플릿
내가 맨 앞에서 템플릿의 첫인상이 제네릭이라고 했는데, 문득 이 포스트를 작성하다보니 과연 그 둘이 완전히 같을까 하는 생각이 들었다. 그리고 구글링을 조금 해봤는데 역시나 그 둘에 차이점이 있었다.
제네릭 | 템플릿 |
---|---|
런타임 때 자료형이 대체될 때까지 제네릭 | 컴파일 때 자료형이 특정됨 |
제네릭 형식이 포함된 어셈블리를 참조할 때 제네릭 형식을 특정 형식으로 대체 가능 | 컴파일 시, 일반 형식이기 때문에 다른 어셈블리에서 결과 형식을 특수화할 수 없음 |
동일한 형식 인수를 사용하는 서로 다른 두 어셈블리에서 특수화된 제네릭은 같은 형식 | 동일한 형식 인수를 사용하는 서로 다른 두 어셈블리에서 특수화된 템플릿은 런타임에 다른 형식 |
모든 참조형 매개변수에 사용할 수 있는 실행 가능한 코드 조각으로 생성됨 | 템플릿은 각 자료형에 대해 분리된 런타임 코드를 생성 |
비형식 템플릿 매개변수(template <int i> C {} )를 허용하지 않음 |
허용 |
특정 형식에 대한 템플릿의 사용자 지정 구현 허용하지 않음 | 허용 |
형식 인수의 하위 집합에 대한 사용자 지정 구현 허용하지 않음 | 허용 |
제네릭 형식의 기본 클래스로 형식 매개 변수를 허용하지 않음 | 허용 |
지원하지 않음 | 템플릿-템플릿 매개 변수(template<template<class T> class X> class MyClass ) 지원 |
댓글남기기