C++ 13주차 템플릿과 제네릭 클래스
템플릿 구체화의 오류
제네릭 타입에 두 개 이상 구체적 타입 지정 시 주의
1 2 3 4 5
template <class T> void swap(T & a, T & b) int s = 4; double t = 5; swap(s,t)
- swap 함수는 매개변수 a와 b 모두 제네릭 타입이 동일함
- swap 함수를 호출할때 s와 t는 매개변수의 타입이 서로 다르므로, 컴파일 오류가 발생함
- 템플릿으로부터 swap(int &, double &) 함수를 구체화가 불가능
2개의 제너릭 타입을 갖는 템플릿
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
template <class T1, class T2> void myCopy(T1 src[], T2 dest[], int n) //src[]의 n개 원소를 dest[]에 복사 { for (int i = 0; i < n; i++) dest[i] = (T2)src[i]; //T1타입의 값을 T2 타입으로 변경 } int main() { int x[] = { 1,2,3,4,5 }; double d[5]; char c[5] = { 'H', 'e', 'l','l','o' }, e[5]; myCopy(x, d, 5); //int x[]의 원소 5개를 double d[]에 복사 myCopy(c, e, 5); //char c[]의 원소 5개를 char e[]에 복사 for (int i = 0; i < 5; i++) cout << d[i] << ' '; //d[] 출력 cout << endl; for (int i = 0; i < 5; i++) cout << e[i] << ' '; //e[] 출력 cout << endl; }
Output
1 2
1 2 3 4 5 H e l l o
템플릿의 char형 구체화 문제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T>
void print(T array[], int n) {
for (int i = 0; i < n; i++)
cout << array[i] << '\t';
cout << endl;
}
int x[] = { 1,2,3,4,5 };
double d[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
print(x, 5); // 템플릿 T가 int 타입으로 구체화
print(d, 5);
char c[5] = { 1, 2, 3, 4, 5 };
print(c, 5); // 템플릿 T가 char 타입으로 구체화
1
2
3
1 2 3 4 5
1.1 2.2 3.3 4.4 5.5
┌ ┐ └ ┘ │
- char형태로 구체화 될 경우 해당 숫자가 그 숫자의 위치를 가진 문자가 출력되는 문제가 발생함
중복 함수와 템플릿의 우선 순위
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T>
void print(T array[], int n) {
for (int i = 0; i < n; i++)
cout << (int)array[i] << '\t';
cout << endl;
}
int x[] = { 1,2,3,4,5 };
double d[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
print(x, 5); // 템플릿 T가 int 타입으로 구체화
print(d, 5);
char c[5] = { 1, 2, 3, 4, 5 };
print(c, 5); // 템플릿 T가 char 타입으로 구체화
Output
1
2
3
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
템플릿을 이용한 제네릭 클래스
제네릭 클래스 구현
1
2
3
4
5
6
7
8
9
10
template <class T>
class MyStack {
int tos;
T data[100]; //T타입의 배열
public:
MyStack();
void push(T element);
T pop();
};
제네릭 클래스 구현
1
2
3
4
5
6
7
template <class T>
void MyStack<T>::push(T element) { //매개 변수에 템플릿 활용
//구현 내용
}
template <class T> T MyStack<T>::pop() {
//구현 내용
}
제네릭 클래스 활용
1
2
3
4
5
6
7
8
MyStack<int> iStack;
MyStack<double> dStack;
iStack.push(3);
int n = iStack.pop();
dStack.push(3.5);
double d = dStack.pop();
제네릭 클래스의 활용
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
template <class T>
class MyStack2 {
int tos; //스택의 Top
T data[100]; //크기 100짜리 스택
public:
MyStack2();
void push(T element); //element를 data[] 배열에 삽입
T pop(); //스택의 top 위치의 데이터를 data[] 배열에서 리턴
};
template <class T>
MyStack2<T>::MyStack2(){
tos = -1;
}
template <class T>
void MyStack2<T>::push(T element) {
if (tos == 99) {
cout << "stack full";
return;
}
tos++;
data[tos] = element;
}
template <class T>
T MyStack2<T>::pop() {
T retData;
if (tos == -1) {
cout << "stack empty";
return 0; //오류 표시
}
retData = data[tos--];
return retData;
}
int main()
{
MyStack2<int> iStack; //int만 저장하는 스택
iStack.push(3);
cout << iStack.pop() << endl;
MyStack2<double> dStack; //double만 저장
dStack.push(3.5);
cout << dStack.pop() << endl;
MyStack2<char>* p = new MyStack2<char>(); //char만 저장
p->push('a');
cout << p->pop() << endl;
delete p;
}
Output
1
2
3
3
3.5
a
제네릭 클래스
cout의 실행 순서
- Cout에서 « 연산자가 연속될 경우, 마지막 데이터부터 cout의 스택에 삽입 후, 스택에 삽입된 데이터를 팝하며 « 연산자를 순서대로 처리
포인터를 이용한 제네릭 클래스
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
template <class T>
class MyStack3 {
int tos; //스택의 Top
T data[100]; //크기 100짜리 스택
public:
MyStack3();
void push(T element); //element를 data에 삽입
T pop();
};
template <class T>
MyStack3<T>::MyStack3() {//생성자
tos = -1; //스택은 비어있음
}
template <class T>
void MyStack3<T>::push(T element) {
if (tos == 99) {
cout << "stack full";
return;
}
tos++;
data[tos] = element;
}
template<class T>
T MyStack3<T>::pop() {
T retData;
if (tos == -1) {
cout << "stack empty";
return 0; //오류 표시
}
retData = data[tos--];
return retData;
}
class Point3 {
int x, y;
public:
Point3(int x = 0, int y = 0) { this->x = x; this->y = y; }
void show() { cout << '(' << x << ',' << y << ')' << endl; }
};
int main(){
MyStack3<int*> ipStack;
int* p = new int[3];
for (int i = 0; i < 3; i++)
p[i] = i * 10; //0,10,20으로 초기화
ipStack.push(p); //포인터 push
int* q = ipStack.pop(); //포인터 pop
for (int i = 0; i < 3; i++)
cout << q[i] << ' ';
cout << endl;
delete[] p;
MyStack3<Point> pointStack; //Point 객체 저장 스택
Point a(2, 3), b;
pointStack.push(a); //Point 객체 a 푸시, 복사되어 저장
b = pointStack.pop(); //point 객체 pop
b.show();
MyStack3<Point*> pStack;
pStack.push(new Point(10, 20));
Point* pPoint = pStack.pop(); //Point 객체의 포인터 Pop
pPoint->show(); //Point 객체 출력
MyStack3<string> stringStack; //문자열만 저장하는 스택
string s = "C++";
stringStack.push(s);
stringStack.push("Java");
cout << stringStack.pop() << ' ';
cout << stringStack.pop() << endl;
}
Output
1
2
3
4
0 10 20
(2,3)
(10,20)
Java C++
2개의 제네릭 타입을 가진 클래스
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
34
35
36
37
38
39
40
41
42
43
44
template <class T1, class T2>
class GClass {
T1 data1;
T2 data2;
public:
GClass();
void set(T1 a, T2 b);
void get(T1& a, T2& b);;
};
template <class T1, class T2>
GClass<T1, T2>::GClass() {
data1 = 0;
data2 = 0;
}
template < class T1, class T2>
void GClass<T1, T2>::set(T1 a, T2 b) {
data1 = a;
data2 = b;
}
template <class T1, class T2>
void GClass<T1, T2>::get(T1& a, T2& b) {
a = data1;
b = data2;
}
int main()
{
//2개의 제네릭 타입 가진 클래스
int a;
double b;
GClass<int, double> x;
x.set(2, 0.5);
x.get(a, b);
cout << "a=" << a << '\t' << "b=" << b << endl;
char c;
float d;
GClass<char, float> y;
y.set('m', 12.5);
y.get(c, d);
cout << "c=" << c << '\t' << "d=" << d << endl;
}
Output
1
2
a=2 b=0.5
c=m d=12.5
C++ 표준 템플릿 라이브러리
STL
표준 템플릿 라이브러리
많은 제네릭 클래스와 제네릭 함수를 포함함
구성
컨테이너 - 템플릿 클래스
1 2 3
- 데이터를 담은 자료 구조를 표현한 클래스 - 리스트, 큐, 스택, 맵, 셋, 벡터
Iterator - 컨테이너 원소에 대한 포인터
- 컨테이너의 원소들을 순회하면서 접근하기 위해 만들어진 컨테이너 원소에 대한 포인터
알고리즘 - 템플릿 함수
- 컨테이너 원소에 대한 기능(복사, 삭제, 정렬…)를 구현한 템플릿 함수
- 컨테이너의 멤버함수가 X
포함되는 라이브러리
컨테이너
- 임의 타입의 객체 보관
- 시퀸스 컨테이너, 연관 컨테이너, 컨테이너 어댑터…
반복자
- 컨테이너에 보관된 객체 접근
알고리즘
- 반복자들을 가지고 일련의 작업 수행
| 컨테이너 클래스 | 내용 |
|---|---|
| array | 고정 길이 배열 |
| vector | 가변배열 |
| queue | FIFO 자료구조, 우선순위 큐 |
| deque | 앞 뒤로 삽입/삭제가 가능한 큐 |
| list | 양방향 연결 리스트 |
| forward_list | 단방향 연결 리스트 |
| stack | LIFO 자료구조 |
| map | 이진탐색트리 기반, 자동정렬, key-value pair 구성 |
| set | 이진탐색트리 기반, 자동정렬, key만 저장 |
| unordered_map | 정렬되지않은 map |
| unoridered_set | 정렬되지 않은 set |
- 이외에도 다양한 클래스가 존재함
반복자의 종류
입력 반복자
출력 반복자
순방향 반복자
양방향 반복자
임의 접근 반복자
STL 알고리즘의 종류
| 계수 알고리즘 | count, … |
|---|---|
| 탐색 알고리즘 | search,, find, … |
| 비교 알고리즘 | equal, mismatch, … |
| 초기화 알고리즘 | fill, generate, … |
| 변경 알고리즘 | transform, … |
| 복사 알고리즘 | copy, … |
| 삭제 알고리즘 | remove, unique, … |
| 대치 알고리즘 | replace, … |
| 정렬 알고리즘 | sort, … |
| 분할 알고리즘 | partition, … |
헤더파일
- 컨테이너 클래스를 사용하기 위한 헤더 파일
- 사용방법: 해당 클래스가 선언된 헤더파일을 include
- Ex) #include
- 알고리즘 함수를 사용하기 위한 헤더 파일
- 알고리즘 함수에 상관없이 #include **
**
- 알고리즘 함수에 상관없이 #include **
- 이름 공간
- STL이 선언된 이름 공간은 std
Vector
특징
- 가변 길이 배열을 구현한 제네릭 클래스
- 벡터 길이에 대한 고민이 필요 X
- 원소 저장, 삭제, 검색 등 다양한 멤버 함수 지원
- 저장된 인덱스는 인덱스로 접근 가능
- 인덱스는 0부터 시작
함수
사용
예제
예제 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main()
{
//Vector 예제
vector<int> v; //정수만 삽입 가능한 벡터 생성
v.push_back(1); //벡터에 정수 1 삽입
v.push_back(2);
v.push_back(3);
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
v[0] = 10; //벡터의 첫번째 원소를 10으로 변경
int n = v[2]; //n에 3이 저장
v.at(2) = 5; //벡터의 3번째 원소를 5로 변경
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
}
Ouput
1
2
1 2 3
10 2 5
예제 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
vector<string> sv;
string name;
cout << "이름 4개 입력" << endl;
for (int i = 0; i < 4; i++) {
cout << i + 1 << ">>";
getline(cin, name);
sv.push_back(name);
}
name = sv.at(0); //벡터의 첫 원소
for (int i = 1; i < sv.size(); i++) {
if (name < sv[i]) //sv[i]의 문자열이 name보다 사전에서 뒤에 나옴
name = sv[i]; //name을 sv[i]의 문자열로 변경
}
cout << "사전에서 가장 뒤에 나오는 이름 : " << name << endl;
Output
1
2
3
4
5
6
이름 4개 입력
1>>한라산
2>>백두산
3>>후지산
4>>설악산
사전에서 가장 뒤에 나오는 이름 : 후지산
iterator
의미
- 컨테이너의 원소를 가르키는 포인터
변수 선언
구체적인 컨테이너를 지정해 반복자 변수 생성
Ex)
1
2
vector<int>::iterator iter;
iter = vec.begin();
사용
예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(){
//iterator 예제
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vector<int>::iterator iter; //벡터 v의 원소에 대한 포인터 iter 선언
for (iter = vec.begin(); iter != vec.end(); iter++) { //iterator를 이용해 모든 원소 탐색
int n = *iter; //iter가 가르키는 원소값 리턴
n = n * 2;
*iter = n; //iter가 가르키는 원소에 값 쓰기
}
for (iter = vec.begin(); iter != vec.end(); iter++)
cout << *iter << ' ';
cout << endl;
}
Output
1
2 4 6
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.
