학습 개요
- 컨테이너 클래스는 객체들을 저장하는 클래스임
- 만일 여러 가지 클래스의 객체에 대해 각각 컨테이너 클래스가 필요하다면 여러 개의 컨테이너 클래스가 선언되어야 할 것임
- 이 경우 동일한 코드를 반복적으로 작성해야 하는 번거로움이 따르는데 템플릿은 이러한 경우 유용하게 활용할 수 있는 기능임
- 클래스 템플릿을 선언해 놓으면 그 안에서 대상으로 하는 객체의 클래스 등을 인수로 전달하여 필요한 클래스가 선언된 효과를 얻게 됨
- 클래스 템플릿을 선언하고 활용하는 방법에 대하여 학습함
- 클래스 및 클래스의 멤버 함수 뿐만 아니라 일반 함수도 템플릿으로 선언함으로써, 여러 가지 대상에 맞는 함수가 선언된 효과를 얻을 수 있음
- 함수 템플릿을 선언하여 활용하는 방법에 대하여 학습함
학습 목표
- 컨테이너 클래스 및 템플릿의 개념을 설명할 수 있음
- 클래스 템플릿을 선언하고, 이를 이용하여 객체를 정의할 수 있음
- 함수 템플릿을 선언할 수 있음
주요 용어
- 컨테이너 클래스(container class)
- 객체를 저장하는 클래스
- 클래스 템플릿(class template)
- 클래스를 선언하기 위한 형판으로, 전달된 템플릿 인수에 따라 이에 맞는 클래스가 선언됨
- 일반화 프로그래밍(generic programming)
- 클래스, 함수 등을 대상 자료형 등을 특정하지 않은 상태로 선언한 후, 필요할 때 자료형 등을 파라미터를 통해 전달하여 정의하는 프로그래밍 방식
- 정렬(sorting)
- 데이터를 정해진 기준에 맞는 순서로 다시 배열하는 것
강의록
컨테이너 클래스와 템플릿
컨테이너 클래스
- 컨테이너 클래스(container class)란?
객체를 저장하는 클래스
int형 데이터를 저장하는 스택 클래스 - Stack1.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14
typedef int STACK_ITEM; class Stack { public: enum { MAXSTACK=20 }; int top; STACK_ITEM item[MAXSTACK]; public: Stack(); // 생성자 bool empty() const; bool full() const; void initialize(); void push(STACK_ITEM s); STACK_ITEM pop(); };
float형 데이터를 저장하는 스택 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 14
typedef float STACK_ITEM; class Stack { public: enum { MAXSTACK=20 }; int top; STACK_ITEM item[MAXSTACK]; public: Stack(); // 생성자 bool empty() const; bool full() const; void initialize(); void push(STACK_ITEM s); STACK_ITEM pop(); };
int형 스택과 float형 스택이 모두 필요할 경우
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
typedef int INT_ITEM; class StackInt { INT_ITEM item[MAXSTACK]; }; void StackInt::push(INT_ITEM n) { item[--top] = n; } typedef float FLOAT_ITEM; class StackFloat { FLOAT_ITEM item[MAXSTACK]; // StackInt와 동일한 코드 // 단 INT_ITEM만 FLOAT_ITEM으로 변경 }; void StackFloat::push(FLOAT_ITEM n) { item[--top] = n; }
- 동일 코드 반복 발생
템플릿
- 템플릿(template)이란?
- 클래스, 함수 등을 선언하기 위한 형판
- 특정 자료형이 아닌 일반 자료형을 대상으로 템플릿을 선언함
- 자료형, 상수 등을 매개 변수를 통해 템플릿에 전달하면 이에 따라 클래스나 함수가 자동으로 선언됨
- 일반화 프로그래밍(generic programming)
- 여러 가지 대상을 위한 클래스나 함수를 템플릿으로 선언함으로써, 동일한 코드를 반복적으로 작성하는 것을 방지함
클래스 템플릿
클래스 템플릿의 선언
클래스 템플릿의 선언 형식
1 2 3 4
template <templateParameters> class ClassTemplateName { };
templateParameters
- 템플릿 매개 변수 목록
- 자료형을 받을 템플릿 매개 변수
typename T
또는class T
로 표기 (T는 사용자 정의 명칭)
ClassTemplateName
- 클래스 템플릿 이름
예: Stack 클래스 템플릿 - StackT.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
template <typename T> class Stack { public: T *buf; // buffer pointer int top; // stack top int size; // 스택의 크기 public: Stack(int s); // 생성자 virtual ~Stack(); // 소멸자 bool full() const; bool empty() const; void push(const T& a); void push(T&& a); T&& pop(); };
클래스 템플릿의 멤버 함수 선언
템플릿 선언문 외부에서 멤버 함수를 선언하는 형식
1 2 3 4 5
template <templateParameters> ReturnType ClassTemplateName<args>::funcName(fParameterList) { }
ReturnType
- 멤버 함수의 반환 자료형
funcName
- 멤버 함수의 이름
args
templateParameters
의 매개 변수
fParameterList
- 멤버 함수의 형식 매개 변수 목록
예: Stack 클래스 템플릿 - StackT.h
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 31 32 33
template <typename T> Stack<T>::Stack(int s) : size(s), top(0) { buf = new T[s]; } template <typename T> Stack<T>::~Stack() { delete[] buf; } template <typename T> bool Stack<T>::full() const { return top == size; } template <typename T> bool Stack<T>::empty() const { return top == 0; } template <typename T> void Stack<T>::push(const T& a) { buf[top++] = a; } template <typename T> void Stack<T>::push(T&& a) { buf[top++] = move(a); } template <typename T> T&& Stack<T>::pop() { return move(buf[--top]); }
클래스 템플릿의 객체 정의
클래스 템플릿의 객체 정의의 형식
1
ClassTemplateName<ClassName> objName(constrArgs);
ClassName
- 템플릿 매개 변수에 전달할 클래스 또는 자료형 이름 등의 템플릿 인수
objName
- 정의할 객체의 이름
constrArgs
- 생성자에 전달할 인수
클래스 템플릿의 객체 사용
예: char을 저장하는 스택의 활용 - StackMain.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#include "StackT.h" #include "MyString.h" using namespace std; int main() { Stack<char> sc(100); // char 스택 선언 sc.push('a'); // char 스택 사용 sc.push('b'); cout << "CHAR STACK : "; while (!sc.empty()) { cout << sc.pop(); } cout << endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
template <typename T> class Stack { public: T *buf; // buffer pointer int top; // stack top int size; // 스택의 크기 public: Stack(int s); // 생성자 virtual ~Stack(); // 소멸자 bool full() const; bool empty() const; void push(const T& a); void push(T&& a); T&& pop(); };
예: int를 저장하는 스택의 활용 - StackMain.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
int main() { Stack<int> si(50); // int 스택 선언 si.push(5); // int 스택 사용 si.push(10); cout << "INT STACK : "; while (!si.empty()) { cout << si.pop(); } cout << endl; Stack<MyString> msStack(10); // MyString 스택 선언 MyString s1("KNOU"); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
template <typename T> class Stack { public: T *buf; // buffer pointer int top; // stack top int size; // 스택의 크기 public: Stack(int s); // 생성자 virtual ~Stack(); // 소멸자 bool full() const; bool empty() const; void push(const T& a); void push(T&& a); T&& pop(); };
예: MyString을 저장하는 스택의 활용 - StackMain.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
int main() { Stack<MyString> msStack(10); // MyString 스택 선언 MyString s1("KNOU"); MyString s2("Dep."); MyString s3("CS"); msStack.push(s1); // MyString 스택 사용 msStack.push(s2 + s3); cout << "MYSTRING STACK : "; while (!msStack.empty()) { cout << msStack.pop() << " "; } cout << endl; return 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
template <typename T> class Stack { public: T *buf; // buffer pointer int top; // stack top int size; // 스택의 크기 public: Stack(int s); // 생성자 virtual ~Stack(); // 소멸자 bool full() const; bool empty() const; void push(const T& a); void push(T&& a); T&& pop(); };
클래스 템플릿의 대상 객체 관련 유의 사항
- 사용자가 선언한 클래스의 객체를 저장하는 컨테이너
- 기본 자료형 외에도 사용자 정의 클래스 객체를 저장하기 위해 컨테이너 클래스 템플릿을 사용할 수 있음
- ex) MyString 객체를 저장하기 위한 스택
- 이 경우 클래스 템플릿에서 필요로 하는 멤버 함수가 대상 클래스에 포함되어 있어야 함
- 클래스 템플릿 Stack에 저장할 객체에는 디폴트 생성자, 대입 연산자, 이동 대입 연산자 등이 필요함
ex)
1 2 3 4 5 6 7 8
class Person { string name; public: Person(const string& n) : name(n) {} void print() { cout << name; } };
1 2 3 4 5
int main() { Stack<Person> pStack(10); // 에러 발생 }
1 2 3 4 5
template <typename T> Stack<T>::Stack(int s) : size(s), top(s) { buf = new T[s]; // Person에 디폴트 생성자가 필요함 }
- 기본 자료형 외에도 사용자 정의 클래스 객체를 저장하기 위해 컨테이너 클래스 템플릿을 사용할 수 있음
비 자료형 템플릿 매개 변수
- 템플릿 매개 변수를 통해 전달할 수 있는 인자
- 자료형 매개 변수
- 기본 자료형, 클래스, 구조체 등
- 비 자료형 매개 변수
- 정수형 자료형의 상수식
- 객체나 함수에 대한 포인터
- 객체나 함수에 대한 l-value 참조
- 멤버에 대한 포인터
- 자료형 매개 변수
비자료형 매개 변수의 사용 예
1 2 3
template <typename T, int size> class Buffer { T buf[size]; };
1 2 3 4 5 6 7 8
void f() { Buffer<char, 128> buf1; // 크기가 128개인 char형 버퍼 선언 Buffer<Complex1, 20> buf2; // 크기가 20개인 complex1 버퍼 선언 int n = 10; Buffer<char, n> buf3; // 에러! - 실행시 값이 결정되는 수식 }
함수 템플릿
함수 템플릿의 선언
함수 템플릿 선언 형식
1 2 3 4
template <templateParameters> ReturnType funcName(fParameterList) { // 함수 몸체 }
templateParameters
- 템플릿 매개 변수 선언
funcName
- 함수 템플릿 이름
함수 템플릿의 활용 예 - swapFT
함수 템플릿 선언 - SwapFunc.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#ifndef SWAP_FUNCTION_TEMPLATE_H_INCLUDED #define SWAP_FUNCTION_TEMPLATE_H_INCLUDED #include <utility> // for std::move template <typename ANY> void swapFT(ANY &a, ANY &b) { ANY temp = move(a); // 이동 대입 연산자가 필요함 a = move(b); b = move(temp); } #endif
함수 템플릿을 이용한 프로그램 - SwapFTMain.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#include <iostream> #include "SwapFT.h" #include "MyString.h" using namespace std; int main() { int x = 10, y = 20; cout << "x = " << x << ", y = " << y << endl; cout << "값 교환 후 -> "; swapFT(x, y); cout << "x = " << x << ", y = " << y << endl << endl; MyString s1("KNOU"), s2("CS"); cout << "s1 = " << s1 << ", s2 = " << s2 << endl; swapFT(s1, s2); cout << "값 교환 후 -> "; cout << "s1 = " << s1 << ", s2 = " << s2 << endl; return 0; }
함수 템플릿의 활용 예 - sortFT
- 버블 정렬
배열의 선두에서 시작하여 차례로 인접한 두 값을 비교하여 앞의 값이 크면 값을 교환하는 것을 반복
함수 템플릿의 활용 예 - sortFT
함수 템플릿 선언 - SortFT.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include "SwapFT.h" // bubble sort algorithm template <typename T> void sortFT(T arr[], int size) { bool doAgain = true; for (int i = 1; doAgain; i++) { doAgain = false; for (int j = 0; j < size - i; j++) { if (arr[j] > arr[j + 1]) { swapFT(arr[j], arr[j + 1]); doAgain = true; } } } }
함수 템플릿을 이용한 프로그램 - SortMain.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include <iostream> #include "SortFT.h" #include "MyString.h" using namespace std; int main() { int x[10] = { 6, 0, 3, 1, 2, 9, 4, 5, 7, 8 }; sortFT(x, 10); for (auto i : x) cout << i << " "; cout << endl; }
연습 문제
객체를 저장하기 위한 클래스를 나타내는 것은?
a. 컨테이너 클래스
- 컨테이너 클래스는 스택, 큐, 배열, 리스트 등 객체들을 저장하기 위한 클래스를 통칭하는 것임
지정된 개수의 객체를 저장할 수 있는 배열을 나타내는 컨테이너 클래스 템플릿을 선언하고자 한다. 위 지문 문장의 ㈀, ㈁, ㈂, ㈃에 넣을 적절한 내용은?
1 2 3 4 5 6 7 8 9
// (ㄱ) <typename (ㄴ)> class Array { T* buf; int size; public: Array(int s) : size(s) { buf = // (ㄷ); } ~Array() { delete [] buf; } // (ㄹ) operator [] (int i) { return buf[i]; } };
a. ㈀
template
, ㈁T
, ㈂new T[s]
, ㈃T&
- ㈀에는 템플릿 선언을 위한 키워드인
template
이 필요하며, ㈁에는 이 컨테이너 클래스가 저장하고자 하는 자료형에 해당되는 매개 변수가 필요함 - 선언문의 내용으로 볼 때
buf
의 자료형으로 사용된T
가 이에 해당됨을 알 수 있음 - ㈂에는
T
클래스의 객체를 동적 할당하는 명령이 필요하며, ㈃은buf[i]
가T
클래스 객체이므로[]
연산자는T
의 참조를 반환하도록 함
- ㈀에는 템플릿 선언을 위한 키워드인
위 지문과 같이 클래스 템플릿이 선언 되었을 때, double형 값을 저장하는
Array
객체를 선언하는 올바른 문장은?1 2 3 4 5 6 7 8
template <class T> class Array { T* buf; int size; public: Array(int s) : size(s) { buf = new T[s]; } T& operator [] (int i) { return buf[i]; } };
a.
Array<double> dArr(20);
- 클래스템플릿이름<클래스이름> 형태로 템플릿을 사용함클래스이름>
정리 하기
- 컨테이너 클래스는 스택, 큐, 배열, 리스트 등과 같이 객체를 저장하는 클래스를 의미함
- 템플릿은 클래스, 함수 등을 선언하기 위한 형판임
- 하나의 클래스 템플릿에 서로 다른 템플릿 인수를 전달하여 사용하면 여러 가지 클래스가 선언된 것과 같음
- 클래스 템플릿의 템플릿 인수로 전달할 클래스는 클래스 템플릿에서 필요로 하는 메소드들을 포함하고 있어야 함
- 클래스(또는 자료형) 이름 외에도 함수 이름이나 상수를 템플릿에 인수로 전달할 수 있음